Skip to content

fix(runtime-bridge): gate harvest auto-commit on commits-ahead, not dirty-tree alone - #461

Merged
artyhoo merged 1 commit into
stagingfrom
claude/hungry-clarke-3a549a
Jun 11, 2026
Merged

fix(runtime-bridge): gate harvest auto-commit on commits-ahead, not dirty-tree alone#461
artyhoo merged 1 commit into
stagingfrom
claude/hungry-clarke-3a549a

Conversation

@artyhoo

@artyhoo artyhoo commented Jun 11, 2026

Copy link
Copy Markdown
Owner

Summary

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 swept ~7 out-of-scope stale files into the F2 PR — the same regression class as #370 (.stryker-tmp swept). 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): new HarvestDeps.commitsAhead(branch, base) disambiguator + HarvestResult.dirtyTreeLeftBehind. A dirty tree now branches three ways:
    • dirty + 0 commits ahead of base (branch == base HEAD) → true rework → commit (ZERO-LLM, templated)
    • dirty + ≥1 commit ahead → the committed work is the deliverable → push existing commit(s) only, leave the dirty tree behind, set dirtyTreeLeftBehind, never git add -A
    • clean → ahead-check never called (normal path unchanged)
  • cli/harvest.ts (real wiring): 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 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.

Diff is runtime-bridge only (3 files). No behavior change on the normal clean-tree path.

Prior-art consult

  • Not a capability commit (bug fix to existing files; no new dependency, no new ≥80-LOC file) → Prior-art: skipped — bug fix to existing harvest egress leg, no new capability area or dependency introduced trailer 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 0
  • Full principle suite green (ran in pre-push) → 21 files / 168 tests pass
  • Manual smoke: dirty + ≥1 ahead no longer add -As stale residue; dirty + 0 ahead still 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

…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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant