Context
Transcript-verified root cause of the worst defect cluster shipped to date (nessie #602 → PR #611, 2026-07-27). The repoRoot working tree was dirty with residue from a PRIOR #602 attempt (a different hook implementation, a test file gutted from 575→64 lines, a broken StrategyView edit) when the cycle started. Nothing checked. The cascade:
- This cycle's developer transcript is clean — the shipped defects were prior-run residue, not authored this cycle (verified:
ms3j1eq7-3mjzm7-developer.json touched only in-scope files, 382 tests passed).
- The mechanized commit-pr (PR19) failed with "patch does not apply / already exists in index" — stale residue conflicted — forcing the LLM ops fallback.
- The fallback ran
git add -A on the dirty tree, sweeping the gutted test file + broken out-of-scope edit into the commit. Green CI, merged, live data-loss regression (nessie PR #613 fixed it by hand).
Historical note: the old PM-driven flow had ABORT-on-dirty-tree ops doctrine (parseAbort exists for exactly the "Working tree is not clean" reply). The compiled driver has no deterministic equivalent — doctrine got lost in the mechanization.
This work must ship as its own separate PR, independent of any other open issue.
What to build
A. Pre-flight check in runBranch (extension/src/work-driver.ts, before the ops dispatch): git status --porcelain at ctx.repoRoot via the existing verifyExecFn ?? execp seam. Non-empty output → emit cap-hit preflight-dirty-tree → handoff. The cap's explainCap branch must list the leftover files (first ~20, from the porcelain output, stored in a new optional pipelineState.preflightDirtyFiles) and give verbatim recovery commands (git stash -u / git checkout -- <files> / inspect-first guidance), noting that residue from a prior cycle is the most likely source.
B. Re-assert at commit-pr entry: in mechanizedCommitPr, before consolidation, verify the repoRoot porcelain contains ONLY paths attributable to this cycle (paths under the declared workstream paths, or new files the developers created — heuristic: if any modified file matches a workstream's declared outOfScope list, bail to fallback is NOT enough; emit the same preflight-dirty-tree cap since the LLM fallback would sweep it with git add -A). Keep it simple: any modified file matching an outOfScope entry → cap, don't consolidate.
C. Schema: additive — preflight-dirty-tree cap literal in the workflow-state union; optional preflightDirtyFiles?: string[] on PipelineState.
Env: PI_ENSEMBLE_PREFLIGHT=0 escape hatch (default ON).
Acceptance criteria
Out of scope
- Auto-stashing (operator decides; the halt is the feature).
- Worktree-cleanliness checks beyond repoRoot + the declared worktrees map.
- Any LLM involvement.
Test plan
Offline: injected verifyExecFn porcelain fixtures (dirty at branch, dirty-out-of-scope at commit-pr, clean) in test-work-driver.ts following the PR18 integration-test pattern. Live (nessie): deliberately leave an uncommitted file in nessie's repoRoot, run /work N, confirm the pre-flight handoff names it.
Addendum (2026-07-27) — interaction with the parallel-work series (#287–#290)
Scope reduced: the parallel-work series (PR-A) removes the residue cause (development at repoRoot). This preflight is NOT superseded — it narrows to the commit-pr integration-mutex entry (guarding operator edits / crash residue at the integration point). The branch-step preflight is deleted along with the ops branch dispatch.
Context
Transcript-verified root cause of the worst defect cluster shipped to date (nessie #602 → PR #611, 2026-07-27). The repoRoot working tree was dirty with residue from a PRIOR #602 attempt (a different hook implementation, a test file gutted from 575→64 lines, a broken StrategyView edit) when the cycle started. Nothing checked. The cascade:
ms3j1eq7-3mjzm7-developer.jsontouched only in-scope files, 382 tests passed).git add -Aon the dirty tree, sweeping the gutted test file + broken out-of-scope edit into the commit. Green CI, merged, live data-loss regression (nessie PR #613 fixed it by hand).Historical note: the old PM-driven flow had ABORT-on-dirty-tree ops doctrine (
parseAbortexists for exactly the "Working tree is not clean" reply). The compiled driver has no deterministic equivalent — doctrine got lost in the mechanization.This work must ship as its own separate PR, independent of any other open issue.
What to build
A. Pre-flight check in
runBranch(extension/src/work-driver.ts, before the ops dispatch):git status --porcelainatctx.repoRootvia the existingverifyExecFn ?? execpseam. Non-empty output → emit cap-hitpreflight-dirty-tree→ handoff. The cap'sexplainCapbranch must list the leftover files (first ~20, from the porcelain output, stored in a new optionalpipelineState.preflightDirtyFiles) and give verbatim recovery commands (git stash -u/git checkout -- <files>/ inspect-first guidance), noting that residue from a prior cycle is the most likely source.B. Re-assert at commit-pr entry: in
mechanizedCommitPr, before consolidation, verify the repoRoot porcelain contains ONLY paths attributable to this cycle (paths under the declared workstreampaths, or new files the developers created — heuristic: if any modified file matches a workstream's declaredoutOfScopelist, bail to fallback is NOT enough; emit the samepreflight-dirty-treecap since the LLM fallback would sweep it with git add -A). Keep it simple: any modified file matching anoutOfScopeentry → cap, don't consolidate.C. Schema: additive —
preflight-dirty-treecap literal in the workflow-state union; optionalpreflightDirtyFiles?: string[]on PipelineState.Env:
PI_ENSEMBLE_PREFLIGHT=0escape hatch (default ON).Acceptance criteria
verifyExecFnporcelain output) halts at branch with cappreflight-dirty-treeBEFORE any ops dispatch fires (assert dispatchFn not called for the branch step), end-to-end throughrunWorkDriver.explainCap("preflight-dirty-tree", …)names the files and the recovery commands.mechanizedCommitPrwith a porcelain entry matching a workstreamoutOfScopepath → cap, no consolidation, no LLM fallback sweep.PI_ENSEMBLE_PREFLIGHT=0restores today's behaviour.Out of scope
Test plan
Offline: injected
verifyExecFnporcelain fixtures (dirty at branch, dirty-out-of-scope at commit-pr, clean) in test-work-driver.ts following the PR18 integration-test pattern. Live (nessie): deliberately leave an uncommitted file in nessie's repoRoot, run/work N, confirm the pre-flight handoff names it.Addendum (2026-07-27) — interaction with the parallel-work series (#287–#290)
Scope reduced: the parallel-work series (PR-A) removes the residue cause (development at repoRoot). This preflight is NOT superseded — it narrows to the commit-pr integration-mutex entry (guarding operator edits / crash residue at the integration point). The branch-step preflight is deleted along with the ops branch dispatch.