fix(orch): resilient worktree reset and better failure UX - #12
Merged
Conversation
…ion error contracts
- WorkspaceMode ('repo' | 'workspace') with mode determination rules
- WorkspaceRepoConfig, WorkspaceRoutingConfig, WorkspaceConfig interfaces
- ExecutionContext with workspaceRoot/repoRoot separation
- WorkspaceConfigErrorCode (12 stable error codes) + WorkspaceConfigError
- createRepoModeContext() factory for repo-mode defaults
- WORKSPACE_CONFIG_FILENAME constant and workspaceConfigPath() helper
- Full JSDoc documenting mode behavior invariants
- Create extensions/taskplane/workspace.ts with full validation chain - loadWorkspaceConfig(): null when no config (repo mode), throws WorkspaceConfigError on invalid - canonicalizePath(): Windows-safe path normalization matching worktree.ts pattern - buildExecutionContext(): unified context builder for Step 2 wiring - Deterministic validation order with all 12 WorkspaceConfigErrorCode branches - Per-repo git root validation via git rev-parse - Duplicate repo path detection after canonicalization - Routing validation: tasks_root existence, default_repo reference check
Address R006 findings: extension.ts was using workspaceRoot for .pi state, orphan detection, batch state, abort signal, and discovery, while engine.ts, resume.ts, and execution.ts use repoRoot (aliased from cwd). This created a split where abort signals and state files could be read/written to different paths in workspace mode. Fix: use repoRoot for all operations to match engine/resume/execution. In repo mode this is a no-op (workspaceRoot === repoRoot). TODO comment added for future workspace-mode state root splitting.
… Step 3 verification
- Updated polyrepo-support-spec.md with TP-001 delivery status, schema adjustments, and Phase 1 checklist - Updated polyrepo-implementation-plan.md marking WS-A as delivered, PR-1 complete, readiness checklist updated - Reviewed docs/reference/commands.md — no user-visible changes - Logged 5 discoveries in STATUS.md - Created .DONE marker
- ParsedTask.promptRepoId?: string field in types.ts - Section-based parser: ## Execution Target with Repo: line - Inline parser: **Repo:** <id> fallback - Precedence: section > inline, repo ID validated against /^[a-z0-9][a-z0-9-]*$/ - Backward compat: missing metadata = undefined, no error - 28 tests in discovery-prompt-parser.test.ts (all pass)
- resolveTaskRouting() applies 3-level precedence: prompt → area → default - Wire workspaceConfig through runDiscovery, executeOrchBatch, resumeOrchBatch - Add FATAL_DISCOVERY_CODES constant for DRY fatal-error classification - Add TASK_REPO_UNRESOLVED/TASK_REPO_UNKNOWN error codes - Update engine.ts and extension.ts to use FATAL_DISCOVERY_CODES - 14 routing tests (categories 8.x-14.x), all 38 routing tests pass
… call-site plumbing confirmed
- Parse repo_id from task-runner.yaml into TaskArea.repoId (config.ts) - Annotate formatDiscoveryResults pending tasks with resolved repo - Add routing-specific guidance to /orch-plan and /orch fatal abort - Add 13 tests: config parsing, output annotation, guidance text
…existing failures confirmed unrelated, CLI smoke OK
…older support Add canonical task-path resolver that correctly handles both: - Repo mode: task folder inside repoRoot → worktree-relative path - Workspace mode: task folder outside repoRoot → absolute path (external) Refactored resolveTaskDonePath, parseWorktreeStatusMd, and pollUntilTaskComplete to delegate to the new single-source-of-truth resolver. Archive fallback preserved for both branches. Deferred: abort.ts selectAbortTargetSessions (Step 1 scope).
…lemented and verified
…r external task folders
…ernal task folders
… resolution Add execution-path-resolution.test.ts with comprehensive coverage: - Monorepo: task folder inside repo root translates to worktree path - External/workspace: task folder outside repo uses absolute path - Archive fallback for both monorepo and external paths - resolveTaskDonePath delegation correctness - Edge cases: prefix overlap, backslash paths, multi-lane resolution, same canonical path for external across worktrees
…esolution 29 tests covering four resolution branches of resolveCanonicalTaskPaths: - repo-contained (monorepo) → worktree-relative resolution - external (workspace) → canonical absolute resolution - archive fallback when primary missing - primary-path fallback when no files exist Plus downstream call-site coverage: - resolveTaskDonePath delegation - parseWorktreeStatusMd canonical path usage - selectAbortTargetSessions abort-flow regression (6 cases) - monorepo completion detection regression (4 cases)
…n strategy; complete Step 4 documentation
- Add loadWorkspaceConfigForDoctor() with line-based YAML parser - Add workspace mode banner showing repo count, default repo, tasks_root - Handle invalid workspace config as FAIL with error code and remediation hint - Workspace root non-git does not trigger false negatives - Repo mode output unchanged when no workspace config exists
…ctor - Extend discoverTaskAreaMetadata() to extract repo_id per area - Add repo-path existence and git-repo validation in cmdDoctor() - Add area repo_id routing validation against known workspace repos - Gated on workspace mode + valid config; repo mode unchanged - All verification scenarios pass: missing path, non-git, unknown repo_id
…4 fix - Fix repo_id trim/truthy alignment: whitespace-only repo_id values no longer produce spurious AREA_REPO_ID_UNKNOWN failures (aligns with orchestrator config.ts behavior) - Sort knownRepoIds for deterministic hint output in area routing errors - Add consolidated 'Run: taskplane init' hint after missing config files - Improve WORKSPACE_REPO_NOT_GIT hint with both git init and config fix remediation options - Verified repo-mode output unchanged when all checks pass - All pre-existing test failures confirmed unrelated to changes
Three fixes for batch execution reliability on Windows: 1. safeResetWorktree: git clean -fd failures are no longer fatal. After cleaning, check git status --porcelain instead of the exit code. If only untracked files remain (e.g., undeletable Windows reserved names like 'nul'), proceed with the reset anyway. 2. forceCleanupWorktree: new last-resort recovery function. When both safeResetWorktree and removeWorktree fail, force-remove the directory (with Windows rd /s /q fallback for reserved names), prune stale git worktree refs, and delete the lane branch. Applied in both engine.ts and resume.ts fallback paths. 3. Batch failure message: show actionable next steps (orch-status, orch-resume, orch-abort) and explain blocked task count instead of just 'Batch failed'.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Batch execution failed at wave 3 because a file named
nul(Windows reserved device name) was left in the worktree by an agent. This caused a cascade:git clean -fdfailed (can't deletenulon Windows) →safeResetWorktreereturned failureremoveWorktreealso failed (git had partially deregistered the worktree)Fixes
Resilient worktree reset —
safeResetWorktree()now checksgit status --porcelainafter cleaning instead of treating thegit cleanexit code as fatal. If only untracked files remain (like undeletablenul), the reset proceeds.Force cleanup fallback — New
forceCleanupWorktree()function as last resort when both reset and remove fail. Force-removes the directory (rd /s /qon Windows), prunes git state, and deletes the lane branch.Better failure UX — Batch failure message now shows actionable next steps (
/orch-status,/orch-resume,/orch-abort) and explains blocked task count.Testing