fix(execute): accept phase-prefixed plan: frontmatter in routing resolver#660
fix(execute): accept phase-prefixed plan: frontmatter in routing resolver#660halindrome wants to merge 7 commits into
Conversation
…lver resolve_plan_path() rebuilt the canonical plan id by concatenating the phase and plan frontmatter fields. When plan: was already phase-qualified (NN-MM — the same form cross_phase_deps uses), this double-prefixed to NN-NN-MM and raised a spurious frontmatter_mismatch, failing the whole phase with invalid_dependency_graph before any agent was spawned. Normalize an already-NN-MM plan value directly instead of re-prepending the phase; a bare plan number still combines with the phase to build the canonical id. Genuine mismatches (wrong plan number, wrong phase, whether bare or phase-qualified) remain rejected. Adds regression coverage in verify-execute-delegation-routing.sh for the unquoted and quoted phase-prefixed accepted forms plus a still-rejected phase-prefixed mismatch. Fixes swt-labs#659 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011a2JTKVquR4tSpmtXwyoZ6
…e id
The Lead's Stage 4 showed the {NN}-{MM} plan filename but never stated that
the plan: frontmatter field is the plan number {MM} alone, so it emitted the
phase-prefixed file id (e.g. plan: "66-01"). That non-canonical value is what
tripped the execute router's frontmatter reconciliation in swt-labs#659.
Document the plan: field in the PLAN.md and SUMMARY.md templates (matching the
existing phase: comment) and add an explicit Stage 4 instruction in
agents/vbw-lead.md that plan: carries {MM} only — the phase lives in phase:.
This is the cause-side fix; the resolver-side tolerance for an already
phase-prefixed value is the companion patch in this PR.
Refs swt-labs#659
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011a2JTKVquR4tSpmtXwyoZ6
QA Review — Round 1Model used: Claude Opus 4.8 ( What was tested
Edge cases (all CONFIRMED safe — funnel through the same guarded
|
Round 1 QA (Claude Opus 4.8) found no blocking or major issues. Two minor notes (redundant outer pad_number; tolerate-not-migrate existing plans) are intentional/out-of-scope. No code changes required. Refs swt-labs#659 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011a2JTKVquR4tSpmtXwyoZ6
QA Review — Round 2Model used: Claude Opus 4.8 ( What was tested
Findings (per angle, all CONFIRMED)
Severity
Action taken this roundAddressed the minor suggestion: added a self-contained 3-digit-phase regression case ( Evidence commit: |
Round 2 QA (Claude Opus 4.8) found no blocking or major issues. Acted on the one minor suggestion: added a 3-digit-phase regression case (phase: 100, plan: "100-01") asserting resolution to canonical 100-01 with no 100-100-01 double-prefix. Suite now 93/93. Refs swt-labs#659 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011a2JTKVquR4tSpmtXwyoZ6
QA Review — Round 3 (final)Model used: Claude Opus 4.8 ( What was tested
Findings (all CONFIRMED, none actionable)
SeverityNo critical, major, or minor actionable findings. All five confirmed safe. VerdictCorrectly-scoped, fail-closed-preserving change. Tests assert canonical-id values (not just absence of error); the genuine-mismatch and multi-digit guards prevent silent acceptance and lock the pad symmetry; the new fixture is isolated; cause-side docs are consistent. Safe to merge. — Clean round. Evidence commit: |
Round 3 QA (Claude Opus 4.8), final pre-merge pass: no critical/major/minor actionable findings. Verified non-tautological tests, isolated multi-digit fixture, no fail-closed gap (comparison key is directory-derived, not frontmatter), consistent cause-side wording, and shellcheck-clean test block. Safe to merge. No code changes required. Refs swt-labs#659 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011a2JTKVquR4tSpmtXwyoZ6
Resolver tolerates phase-prefixed plan: frontmatter + Lead/template cause-fix (swt-labs#659). PR swt-labs#660.
…refixed names worktree-create/merge/cleanup built the "<phase>-<plan>" slug by always prepending PHASE. Callers (references/execute-protocol.md) pass the already phase-qualified plan id (NN-MM — the form .execution-state.json and plan filenames use), so the slug double-prefixed to NN-NN-MM: e.g. plan 43-03 produced worktree .vbw-worktrees/43-43-03 and branch vbw/43-43-03. Mirror the resolve-execute-delegation-mode.sh guard from this PR: use an already-qualified plan as-is, otherwise combine it with PHASE. worktree-agent-map.sh is unaffected (it never concatenates phase-plan). Also fixes a latent contract violation in worktree-merge.sh surfaced by the new regression test: on a successful merge git's porcelain summary leaked to stdout ahead of "clean", breaking the documented "exactly clean|conflict" output. Suppress git stdout/stderr so the contract holds. Adds regression coverage in tests/worktree.bats for the phase-qualified forms across create, merge, and cleanup (including the agent-map glob). Refs swt-labs#659 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F1pWjYMkNnRqqtkMpWJ3h6
Clean round — no blocking findings. Independent read-only review (Claude Opus 4.8) confirmed the double-prefix fix across create/merge/cleanup, verified the 4 new regression tests fail against origin/main scripts and pass on this branch, and confirmed the merge stdout-contract fix preserves MERGE_STATUS. Two minor observations, both pre-existing and unreachable under VBW's 2-digit phase/plan id conventions (documented in the PR QA comment, not fixed here): - cleanup agent-map glob is a substring match (identical behavior in the old bare-input path; secondary to the exact-name clear). - merge commit-message text can mix the arg PHASE with the qualified plan's number (cosmetic only; merge target branch is always correct). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F1pWjYMkNnRqqtkMpWJ3h6
QA Round 4 — review of commit
|
Linked Issue
Fixes #659
What
Fix both sides of the phase-prefixed
plan:frontmatter failure:plan:value (NN-MM) in addition to the canonical plan-number-only form (MM); both normalize to the same canonical id.plan:carries the plan numberMMonly — not theNN-MMfile id — so the non-canonical value stops being emitted in the first place.Why
resolve_plan_path()rebuilt the canonical plan id by concatenating thephaseandplanfrontmatter fields:norm_fm_id=$(normalize_plan_ref "$(pad_number "$fm_phase")-$(pad_number "$fm_plan")" ...)When
plan:is already phase-qualified (plan: "66-01"), this double-prefixes the phase →66-66-01 != 66-01→frontmatter_mismatch→ the resolver exitsinvalid_dependency_graphand the entire phase fails before any agent is spawned.Two root causes converged:
NN-MMform is already first-class incross_phase_deps(templates/PLAN.md), so rejecting it here was inconsistent.plan: "66-01"at all. Its Stage 4 prominently showed the{NN}-{MM}plan filename but never stated that theplan:frontmatter field is{MM}alone, so it mirrored the file id into the field.The defect shipped in
deb36058(2026-05-01) and stayed latent onmainuntil a Lead emitted the phase-prefixed form. It was hit live during a/vbw:viberun building phase 66 in a real consumer repo.How
Effect —
scripts/resolve-execute-delegation-mode.sh,resolve_plan_path(): normalize an already-NN-MMfm_plandirectly instead of re-prepending the phase. A bare plan number still combines with the phase. Genuine mismatches (wrong plan number / wrong phase, bare or phase-qualified) still fail closed.Cause — self-document the
plan:field intemplates/PLAN.mdandtemplates/SUMMARY.md(matching the existingphase:comment), and add an explicit Stage 4 instruction inagents/vbw-lead.mdthatplan:carries{MM}only while the phase lives inphase:.Affected:
scripts/resolve-execute-delegation-mode.sh,agents/vbw-lead.md,templates/PLAN.md,templates/SUMMARY.md(+ regression tests). The resolver is invoked fromreferences/execute-protocol.md, driven by/vbw:vibe. No command, hook, or version-file changes.Testing
claude-vbworclaude --plugin-dir "<path-to-vbw-clone>")Automated testing performed:
testing/verify-execute-delegation-routing.sh: 93/93 PASS — adds the 4 execute resolver: phase-prefixed plan: frontmatter double-prefixes id, fails with frontmatter_mismatch/invalid_dependency_graph #659 cases (phase-prefixed unquoted/quoted accepted, canonical-id mapping, mismatch still fails closed) plus a multi-digit-phase case (phase: 100,plan: "100-01"→100-01, no100-100-01).testing/run-all.sh: BATS 3565 passed / 0 failed, Lint 1/1. (A few contract checks flag only under heavy parallel load — shared-TMPDIRflakes; all pass in isolation and none reference the changed paths.)verify-lsp-first-policy.sh,verify-commands-contract.sh,verify-permission-mode-contract.shall green.shellcheck -S warning+bash -nclean on the changed script and test.66-66-01diagnostic confirmed before, and fixed after, the change.QA Review Evidence
Three independent read-only devil's-advocate rounds on Claude Opus 4.8, each as a fresh session. Reports posted as separate PR comments. Rounds 1 and 3 found no actionable issues; round 2 surfaced one minor suggestion (multi-digit-phase coverage), which was addressed with a new regression case.
claude-opus-4-8)a45ebeb8(round 1),ed4b75c0(round 2 — added multi-digit test),7fa9add6(round 3)fix(scope): address QA round NNotes
6b768ab8(resolver tolerance — effect) and5d78e8cd(Lead/template canonical form — cause), plus the three QA evidence commits.tests/agent-shutdown-integration.bats(test_prune_recovers_from_stale_lock_directory_with_no_pid_file) hangs intermittently; it is byte-identical tomainand independent of this change.🤖 Generated with Claude Code