Skip to content

An isolated sweep bundle cannot land when the deferred-work ledger is gitignored and unseeded: verify_review_bundle reads the worktree's copy #426

Description

@pbean

Found while fixing the bundle-close carry in #406 (5c401f3), which turned out not to reach this shape — so it is filed separately rather than claimed as fixed.

verify.verify_review_bundle (verify.py:1679) requires every id in task.dw_ids to be done in paths.deferred_work, and SweepEngine._verify_review hands it self.workspace.paths — under scm.isolation = "worktree", the unit worktree's paths.

A worktree checks out tracked files only, and scm.worktree_seed defaults to () (policy.py:468). So for a project that gitignores its ledger — an ordinary shape, and this repo's own: the ledger's default home is under a gitignored artifacts dir — the ledger is absent from the worktree entirely. deferredwork.mark_done returns False on a missing file (deferredwork.py:88-89), the close lands nowhere, and the gate then reads a file that does not exist.

The result is a bundle that can never land, on any run, however correct the work is.

Measured

Sandbox project fixture, .gitignore = .bmad-loop/runs/ + deferred-work.md, ledger holding DW-1: open, isolation = "worktree", worktree_seed = (), one bundle over DW-1 whose dev session does real source work and finalizes its spec to done:

SUMMARY  done=1 deferred=1 paused=False
PHASE    deferred
KINDS    [... 'dev-decision', 'review-not-recommended', 'review-skipped',
          'session-start', 'session-end', 'fix-decision',
          'review-verify-failed', 'story-deferred', 'unit-closed', 'worktree-kept']
LEDGER   {'DW-1': 'open'}

The dev gate passes; the review gate fails with fixable=True; a repair round runs and cannot help (the ledger is still not in the worktree); the bundle defers. The entries stay open, deferredwork.open_ids re-bundles them, and the next sweep drives the same work again — an unbounded re-drive, though a loud one: review-verify-failed fires every time.

Same run with the ledger named in scm.worktree_seed: the bundle lands (done=1 deferred=0), which is the configuration 5c401f3's carry covers. Same run with the ledger tracked: lands, and the flip rides the branch — the pre-existing test_sweep_worktree_bundle_merges_to_target shape.

So the exposure is exactly: gitignored and unseeded. That is the default.

Why the #406 carry does not reach it

SweepEngine._carry_isolated_ledger_writes runs from _integrate_unit's DONE arm. A unit that defers never reaches it — deliberately, since a defer discarded the code a close claims to have resolved. Here the unit never reaches DONE at all, so no DONE-leg carry of any kind can help.

Fix shapes (not chosen)

  1. Have verify_review_bundle read the main checkout's ledger when the orchestrator is the ledger writer (_generic_dev()). The gate exists to confirm the orchestrator's own write happened, and under isolation that write is now carried to the main checkout anyway — so the worktree's copy is arguably the wrong oracle regardless. Wrong for the legacy --dw-bundle path, where the session writes the ledger inside the worktree, so it would need to stay path-dependent.
  2. Seed the ledger into every unit worktree automatically when it is gitignored, rather than requiring the operator to name it in worktree_seed. Smallest behavioural surface, and it also makes 5c401f3's carry the universal path instead of a configuration-dependent one. Costs a copy per unit and needs a rule for what "the ledger" resolves to before the workspace is rebased.
  3. Document worktree_seed as required for a gitignored ledger and fail fast at run start rather than at the review gate. Cheapest, but it leaves a valid configuration unrunnable.

(2) looks best on the current reading; not measured.

Cross-refs: #405, #406 (5c401f3 and its commit message, which states this scope limit), #425 (the third producer in the same add -A family).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions