fix(delegate): tell the parent when a worktree was preserved un-inspected
The preserved worktree is invisible to the only consumer that can act on it. Completes the #88113 fix. That change correctly stops the destructive prune when a git probe fails, but still returns commits=0 / dirty=False -- values that were never measured. Those are the defaults the prune used to delete on, so the failure payload is byte-identical to "inspected fine, child left nothing": inspection FAILED, uncommitted work kept -> {commits: 0, dirty: False, pruned: False} inspected OK, child produced nothing -> {commits: 0, dirty: False, pruned: False} The only failure signal was a logger.warning, and the sole consumer of this payload is the parent agent reading the serialized delegate_task entry -- it cannot read logs (no in-repo code reads the key back). So the parent's rational reading of the failure case is "the child produced no work", which is the exact wrong conclusion: a worktree possibly full of uncommitted work is preserved and then never looked at. The data survives but nobody is told to recover it. Changes: - subagent_worktree: one _unproven() helper stamps inspection_failed + a note naming the worktree/branch, warns, and returns the payload. Both unproven exits route through it, so they cannot drift apart again. - subagent_worktree: the pre-existing exception path (timeout, OSError, a non-numeric rev-list stdout) produced the same unproven payload but logged at DEBUG -- effectively silent. It now takes the same flagged path as a non-zero exit; identical outcomes get identical reporting. - delegate_tool: the caller's finalize-raised fallback assigned the creation-side metadata dict (path/branch/repo_root/base_commit) -- a disjoint schema missing commits/dirty/pruned. It now emits the same flagged shape, and logs at WARNING. - Docs + docstring + module contract now state that pruning requires affirmative proof, so a future cleanup doesn't "fix" the preserved worktree by restoring the unconditional prune and reintroducing this P1. Purely additive: the happy-path payload shape is unchanged, so no existing reader can break. Validation: - 18/18 tests/tools/test_subagent_worktree.py; 127 passed across the delegation suites (test_delegate, batch_validation, control_actions, timeout_diagnostic). - 3 new guards mutation-checked: neutering the flag fails all three; reverting the production file to pre-fix main fails all three. Restores checksum-verified. - E2E on real git: inspection-failure now returns inspection_failed=true with work intact on disk; proven-clean still prunes (pruned=true).
This commit is contained in:
+17
-2
@@ -2508,8 +2508,23 @@ def _run_single_child(
|
||||
subagent_worktree.finalize_subagent_worktree(_worktree_info)
|
||||
)
|
||||
except Exception as e:
|
||||
logger.debug("worktree finalize failed: %s", e)
|
||||
entry_dict["worktree"] = dict(_worktree_info)
|
||||
# finalize is written hard not to raise, but if it ever does the
|
||||
# state is unknown — emit the SAME schema the parent expects,
|
||||
# flagged, instead of leaking the creation-side metadata shape.
|
||||
logger.warning("worktree finalize failed: %s", e)
|
||||
entry_dict["worktree"] = {
|
||||
"path": _worktree_info.get("path", ""),
|
||||
"branch": _worktree_info.get("branch", ""),
|
||||
"commits": 0,
|
||||
"dirty": False,
|
||||
"pruned": False,
|
||||
"inspection_failed": True,
|
||||
"note": (
|
||||
"worktree finalize raised; state unknown — inspect "
|
||||
f"{_worktree_info.get('path', '')} manually before "
|
||||
"assuming no work."
|
||||
),
|
||||
}
|
||||
|
||||
try:
|
||||
_heartbeat_thread.start()
|
||||
|
||||
Reference in New Issue
Block a user