BOHICA-LABS / BOHICA-LABS/vsdd-factory
bug(orchestrator): broad-burst agents (PO, spec-steward) systematically drop git commit step, leaving 99+ files uncommitted across multiple passes
- Dominant language
- Rust
- Stars
- 2
- Forks
- 1
- Avg merge
- 6h 43m
- Merged PRs (30d)
- 29
Description
## Summary
When the orchestrator dispatches a single agent to make broad edits across many files (e.g., a Phase 1d remediation burst touching 10-20 spec files), the agent reliably performs the writes and reports successful completion — but **never actually runs `git commit`**. The work lands in the working tree but is never persisted to the factory-artifacts branch.
This persists silently across many bursts. The orchestrator dispatches subsequent agents that read the live working tree, so functional review (adversary, gate audit, lints) sees the work as if it's there. But from git's perspective, the work doesn't exist — a `git reset --hard`, a worktree remount, or any disk failure would lose passes of substantive remediation.
Discovered during ftc-blue Phase 1d cycle pass-11 when `/vsdd-factory:factory-health` reported 117 uncommitted entries in the `.factory/` worktree (98 modified + 18 untracked + 1 deleted), representing approximately 99 files of legitimate remediation work from passes 6 through 10.
## Reproduction
ftc-blue Phase 1d cycle, passes 6-10 (2026-06-23, vsdd-factory@1.0.0-rc.21):
Sample diff between HEAD and working tree at pass-11:
```
$ git -C .factory log --oneline holdout-scenarios/wave-scenarios/HOLDOUT-INDEX.md
1accec9 spec: p1d pass-5 product-owner remediation (H-2, M-2, M-3, M-4)
$ head -3 .factory/holdout-scenarios/wave-scenarios/HOLDOUT-INDEX.md
---
document_type: holdout-index
version: "1.7"
```
HOLDOUT-INDEX was v1.3 in HEAD (last committed at pass-5) but v1.7 in working tree — **four versions of accumulated remediation across passes 6-10 had never been persisted**. Same pattern across ~99 other spec files: BC tree updates, HO scenario updates, L3-PRD updates, ADR updates, UX-INDEX updates, the entire L2-INDEX rename, all uncommitted.
## Pattern characterization
Going back through `git log --oneline` on factory-artifacts for passes 6-10:
**Commits that LANDED (single-focused agents):**
```
684511b fix(arch): add SS-08 dependency row to dependency-graph.md (pass-10 F-H2)
→ 2 files (the spec edit + the architect's remediation log)
eef2e37 fix(arch): register DI-004 in ss-08 l2-invariants (pass-9 H-A)
→ 1 file
62ba942 fix(arch): bump ARCH-INDEX §4 ADR-0006 v1.8→v1.9
→ 1 file
31f6b86 fix(specs): HO-049 advisory H-B — replace E-ZMG-003 with E-END-001
→ 1 file
```
All architect, all single-focused, all landed.
**Commits that NEVER HAPPENED (broad-burst agents):**
- Pass-7 PO-A: bulk-fix 28 BC capability-path mis-anchors. Deliverable said "18 behavioral contracts modified" — zero commits to factory-artifacts.
- Pass-7 PO-B: OQ namespace + L3-PRD + save schema reconciliations across 12-18 files. Zero commits.
- Pass-7 spec-steward: H-8 L2-INDEX rename + P7-3 README + P7-4 UX-INDEX changelog + M-3 SM citations. Zero commits.
- Pass-9 PO substantive remediation (10 findings, 12 files). Zero commits.
- Pass-9 spec-steward changelog backfills (4 files). Zero commits.
- Pass-10 PO substantive remediation (8 files). Zero commits.
- Pass-10 spec-steward changelog backfills (4 files). Zero commits.
- Pass-10 PO PG-1/PG-3 cleanup (19+ files, async dispatch). Zero commits.
Every one of these agents returned a deliverable saying "all commits signed" or "version bumps applied" — but the commits never executed.
## Hypothesized root cause
The orchestrator's burst prompts include a "All commits signed" constraint in the **Constraints** section but never enumerate `git commit` as a discrete deliverable step alongside Reading files / Writing edits / Running lints / Writing the deliverable doc. When the burst is small (1-2 files), the agent has bandwidth to remember the constraint and commit. When the burst is broad (10-20 files), the agent's plan-execute loop is fully consumed by the file edits + lint runs + deliverable writing, and the commit step is silently dropped from the plan.
The agent then writes "version bumped 1.2 → 1.3" or "lint exits 0" in its deliverable, which it observes to be true on disk — but never runs `git -C .factory commit`. The orchestrator sees the deliverable, marks the task complete, and moves on. The work is unprotected from that point forward.
This is distinct from issue #211 (adversary report persistence): there the agent has a read-only tool profile and structurally can't write. Here the agent has full tool access but the commit step gets dropped from the execution plan under high-cognitive-load bursts.
## Why this is dangerous
1. **Silent loss surface**: any `git reset`, worktree remount, force-update, or filesystem corruption between the failed commit and the recovery would lose the work. The orchestrator wouldn't know — STATE.md and the file diff would still show the work in place, but git would have no record.
2. **Audit trail destruction**: 99 files committed in one recovery commit (the quickfix) means we permanently lose the ability to trace "which agent did what when" — pass-7 work and pass-10 work get squashed into one undifferentiated lump.
3. **Compounding**: each subsequent adversarial pass reads the uncommitted state, finds new defects, dispatches more bursts, more commits go missing. By pass-11 we had passes 7-10 of accumulated uncommitted work.
## Proposed fix
Three layers:
### Layer 1 — Agent prompt template
Update the orchestrator's burst-dispatch template (and the relevant agent AGENTS.md files for product-owner, spec-steward, architect, devops-engineer, etc.) to make `git commit` an explicit, enumerated deliverable step, not just a constraint:
```markdown
# Deliverable steps (execute IN ORDER)
1. Read source artifacts.
2. Apply edits per finding.
3. Run relevant lints; fix any failures.
4. Write deliverable report to `.factory/cycles//adversarial/.md`.
5. **Stage and commit ALL changes signed on factory-artifacts:**
```
git -C .factory add -A
git -C .factory commit -S -m ""
```
Verify commit landed: `git -C .factory log -1 --format='%h %s'` shows the new commit.
6. Report back the commit SHA in the deliverable summary.
```
### Layer 2 — Post-burst orchestrator verification
After every agent dispatch that should produce commits, the orchestrator runs a one-line check:
```bash
git -C .factory status --porcelain | wc -l
```
If > 0, the orchestrator immediately surfaces "uncommitted work detected after burst — re-dispatch to commit or escalate" rather than continuing.
### Layer 3 — Pre-burst sanity check
At the start of every burst dispatch, the orchestrator does the same uncommitted-count check. If > 0 at start, that's a recovery situation that must be addressed before new work begins. This would have flagged the regression at pass-7 → pass-8 boundary.
## Applies to
- `agents/orchestrator/AGENTS.md` — burst dispatch template
- `agents/product-owner.md` — deliverable steps section
- `agents/spec-steward.md` — same
- `agents/architect.md` — same
- `agents/devops-engineer.md` — same
- New skill or hook: `vsdd-factory:check-clean-worktree` (or built into existing factory-health)
- Possibly `hooks-registry.toml` — register a post-Agent-completion file-tracker hook
## Acceptance criteria
- [ ] Agent AGENTS.md files document `git commit` as an enumerated deliverable step
- [ ] Orchestrator's burst dispatch template includes pre + post worktree-clean checks
- [ ] At least one synthetic burst dispatch demonstrates the post-check catching a missed commit
- [ ] Documented recovery procedure for "I just ran factory-health and there are N uncommitted files"
## Found during
ftc-blue Phase 1d cycle, pass-11 + factory-health check (2026-06-23, vsdd-factory@1.0.0-rc.21). 99 files of legitimate remediation work from passes 6-10 were uncommitted. Discovery led to a single recovery commit on factory-artifacts; root-cause analysis shows broad-burst agents systematically drop the commit step.
## Notes
This is closely related to but distinct from:
- #210 (parallel-edit data loss): about file collision across concurrent dispatches
- #211 (adversary report persistence): about read-only tool profiles
- #216 (sweep template): about incomplete enumeration
This issue is specifically about **commit reliability under high-file-count bursts**. The fix is orthogonal to the other three but they all contribute to a single underlying theme: orchestrator-to-agent task contracts have observable rather than enforced commit/persist semantics.
This may also explain why some users have reported "agent worked but the work didn't show up in CI" — if commits never landed, the changes never pushed.
This is one of the highest-severity findings from the ftc-blue cycle. Recommend prioritizing alongside #210.
Contributor guide
Assessment
This issue has not been assessed yet.