fix(runtime-bridge): gate harvest auto-commit on commits-ahead, not dirty-tree alone - #461
Merged
Merged
Conversation
…irty-tree alone harvestTask committed any dirty working tree via `git add -A` before pushing, on the theory that a dirty tree == aif's uncommitted rework leg. But the aif container routinely leaves a dirty tree AFTER committing the real work (stale base-state residue: reverted CLAUDE.md/rules, resurrected deleted skill dirs). On 2026-06-11 (task d037c54d, F2 AifFireBackend) that would have add -A'd ~7 out-of-scope stale files into the F2 PR — the same regression class as #370 (.stryker-tmp swept). Disambiguate by commits-ahead-of-base instead of mere dirtiness: - dirty + 0 ahead (branch == base HEAD) => true rework => commit (ZERO LLM, templated) - dirty + >=1 ahead => the committed work IS the deliverable => push the existing commit(s), leave the dirty tree behind, surface dirtyTreeLeftBehind + warn the operator. Never `git add -A` stale residue into the PR. - harvest.ts: new HarvestDeps.commitsAhead(branch, base); HarvestResult.dirtyTreeLeftBehind. - cli/harvest.ts: real commitsAhead via `git rev-list --count <baseRef>..HEAD` (resolveBaseRef prefers origin/<base>, falls back to <base>); stderr WARNING + dirtyTreeLeftBehind in JSON stdout. Only consulted on a dirty tree, so the clean (normal) path is unchanged even if base is unresolvable. - test: paired-negative — dirty + >=1 ahead => NO commit, pushes existing only; dirty + 0 ahead => commits. Reproduces the 2026-06-11 / #370 regression class. Prior-art: skipped — bug fix to existing harvest egress leg, no new capability area or dependency introduced.
This was referenced Jun 11, 2026
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.
Summary
harvestTaskcommitted any dirty working tree viagit add -Abefore pushing, on the theory that a dirty tree == aif's uncommitted rework leg. But the aif container routinely leaves a dirty tree after committing the real work (stale base-state residue: reverted CLAUDE.md/rules, resurrected deleted skill dirs). On 2026-06-11 (taskd037c54d, F2AifFireBackend) that would have swept ~7 out-of-scope stale files into the F2 PR — the same regression class as #370 (.stryker-tmpswept). PR #457 documents the incident. This gates the auto-commit on the actual rework signature instead of mere dirtiness.Changes
harvest.ts(pure DI core): newHarvestDeps.commitsAhead(branch, base)disambiguator +HarvestResult.dirtyTreeLeftBehind. A dirty tree now branches three ways:branch == base HEAD) → true rework → commit (ZERO-LLM, templated)dirtyTreeLeftBehind, nevergit add -Acli/harvest.ts(real wiring):commitsAheadviagit rev-list --count <baseRef>..HEAD(resolveBaseRefprefersorigin/<base>, falls back to<base>); stderr WARNING +dirtyTreeLeftBehindin JSON stdout. Only consulted on a dirty tree, so the clean path works even if base is unresolvable.test/harvest.test.ts: paired-negative — dirty + ≥1 ahead ⇒ NO commit (pushes existing only); dirty + 0 ahead ⇒ commits. Reproduces the 2026-06-11 / doc-audit-ship-boundary-meta-launch #370 regression class.Prior-art consult
Prior-art: skipped — bug fix to existing harvest egress leg, no new capability area or dependency introducedtrailer on the commit.Test plan
npx vitest run test/harvest.test.ts(runtime-bridge) → 10/10 pass; watched the 2 new assertions fail RED first, then pass GREEN (TDD)npx tsc --noEmit(runtime-bridge) → exit 0dirty + ≥1 aheadno longeradd -As stale residue;dirty + 0 aheadstill commits the rework leg§1.7 Skipped: mechanical bug fix to packages/runtime-bridge (egress logic + its tests), touches no discipline-bearing rule/principle/doc surface