Skip to content

fix(vibe): Close five gaps from a PM pull request review - #300

Merged
shafty023 merged 11 commits into
mainfrom
fix/vibe-pm-review-gaps
Oct 16, 2026
Merged

shafty023 merged 11 commits into
mainfrom
fix/vibe-pm-review-gaps

Conversation

@shafty023

@shafty023 shafty023 commented Oct 16, 2026 •

Copy link
Copy Markdown
Collaborator

Purpose

A product manager built closedloop-ai/symphony-alpha#8861 (a static Desktop sign-in return page) with vibe 1.0.36 and took it to a pull request. Review found five gaps, and all five were still open on 1.0.47 (operator ruling, 2026-10-16). This closes them in vibe 1.0.49 (#299 took 1.0.48 and is merged in).

What changed

  1. Tokens. New "Check the bundled tokens first" in guardrails.md. The writer and the guardrails reviewer read the tokens the touched surface actually bundles (each stylesheet's :root and .dark blocks) before writing or accepting any hardcoded color, shadow or dark override. A raw value, a page-local re-declaration or an own dark override where a bundled token exists is blocking, never advisory. "Not bundled" is now recorded per token, with the stylesheets and blocks searched.
  2. Rendering. The reference check renders the final state in light and dark wherever the surface supports both, one Reference check line per theme. Handoff's verify step renders a static or generated page directly (its own generator, headless, both themes) instead of accepting "could not drive". Still no render without a reference image in the build loop.
  3. Post-PR fixes. Handoff gains "Changes on the open pull request": the session resumes, the environment worker reads the unresolved review threads, the same writer fixes them in its pull-request turn, the guardrails review and checks rerun, the push goes to the same branch through the normal hook (never forced), the ticket refreshes, and the pull request body is rebuilt from the session record with each thread answered (fixing SHA) through the REST review-comment reply endpoint. apps/desktop/scripts/** is now named in the Never column; behavior unchanged.
  4. PR-path tests. review-soul is required on the tests the writer wrote. The writer's pull-request mode adds two rules: a test name never claims more than its assertions pin, and assertions prefer tokens, structure and behavior over style-string pins.
  5. Main moving during validation. The affected test selection is pinned to the transaction's captured mainSha, so origin/main advancing no longer throws "Affected test selection owner changed". The session's own HEAD changing, or a rewritten main that would move the wrapper's base, still refuses. Dist rebuilt.

Also: the test fixture for the canonical test:affected wrapper is shared. The new contract test imports vibe_contract_text.py, byte-identical to the one #299 adds, so the two merge without conflict there; the wider helper dedupe stays with #299.

Tests and evals

  • Contract tests: test_vibe_pm_review_gaps_contract.py, 12 cases, all 12 fail on 1.0.47 (c829f66) and on 1.0.48 (0f7035e). Three existing assertions that pinned the old wording (one-theme reference check, the all-or-nothing tokens note, the empty Never cell) are updated in place, each citing this ruling.
  • Vitest (item 5): "passes while origin/main advances during affected-selection" and "during check:source-gates" both fail on 1.0.47, with exactly "Test selection refs changed during the owning budget child" and "Affected test selection owner changed before its report assessment". A rewound origin/main and an unpinned wrapper filter still refuse; the existing post-HEAD and after-selection-commit guards are kept.
  • Evals, one run each, concurrency 2, the same case files on both versions (branch head ba6557a, 1.0.49):
Case Claude, branch Claude, 1.0.47 Codex, branch Codex, 1.0.47
pm-hardcoded-shadow-blocked-at-review 4/4 2/4 (false "not bundled" claim left advisory) 6/6 5/6 (false claim not blocking)
pm-static-page-light-and-dark 7/7 7/7 9/9 8/9 (no dark-theme reference check logged)
pm-review-comments-fix-the-pr 11/11 1/11 (told "already finished") 13/13 3/13 (told "already finished")
Cases passed 3/3 1/3 3/3 0/3

Usage, branch: Claude 129,395 cache-written and 811,784 cache-read input tokens and 10,141 output tokens, 111 s wall; Codex 549,865 input (471,680 cached) and 4,778 output tokens, 63 s wall. On 1.0.47: Claude 137,485 cache-written and 853,735 cache-read input tokens and 9,275 output tokens, 110 s wall; Codex 482,835 input (407,680 cached) and 4,398 output tokens, 57 s wall. Codex counts include its turn-cap and loaded-plugin checks. The light-and-dark case passes on Claude with 1.0.47 too, so on Claude it guards the behavior rather than separating versions.

Earlier iterations, kept for the record: the first light-and-dark grader judged a truncated trace (Claude's judge sees only the start and end of a long trace) and was replaced by tool-use and checks-log graders; two regexes were narrowed (an advisory note on the decision table, "unresolved" matching "resolv"); Codex batches several shell commands in one code-mode call, so the runner now counts each matching command; and two Codex runs on the branch found a real gap each (a page-local .dark rule; step 7's summary dropping thread resolution), fixed in the writer and in handoff step 7. After merging #299, one more Codex run left thread resolution out again (12/13); the vibe skill's post-PR summary now names it, and the final runs above are on that commit.

Commands run

uv run pytest plugins/vibe -q                                   # 266 passed (after merging main)
uv run ruff check plugins/vibe && uv run pyright plugins/vibe   # clean
node --test plugins/vibe/skills/*/scripts/*.test.mjs            # 301 pass
cd tools/vibe-workers && npm run typecheck && npm run build     # clean, dist rebuilt
npx vitest run src/main-sync-budget.test.ts src/main-sync-budget-ref-read.test.ts  # 28 passed
cd tools/plugin-manifests && npm test                           # 6 passed

- Check bundled tokens (:root and .dark) before any raw color, shadow
  or dark override; a raw value where a token exists is blocking, and
  "not bundled" is recorded per token with the evidence of the check
- Render the reference check in light and dark; handoff verify renders
  a static or generated page directly instead of "could not drive"
- Add the post-PR remediation path: same writer, guardrails and checks
  rerun, push to the same branch, refreshed PR body, thread replies
- Require review-soul on PR-path tests; tests pin what they name and
  prefer token, structure and behavior assertions
- Pin the affected test selection to the captured main so a moving
  origin/main no longer invalidates main-sync validation
- Name apps/desktop/scripts/** in the Never column (no behavior change)

Testing: vibe pytest, Codex runner tests, focused vitest, ruff, pyright
Risks: Prose changes steer agents; behavior covered by contract tests
and evals on both harnesses
- Grade light and dark from tool use and the checks log, since a
  long build trace reaches the judge with its middle cut
- Tell the stand-in that every page gets the dark class
- Narrow the advisory and thread-reply regexes to what they claim

Testing: Codex runner tests, vibe pytest, ruff, pyright
Risks: None identified
- Restore main's copies of the contract tests and keep only the three
  assertion updates this change needs
- Ship vibe_contract_text.py byte for byte as the open helper dedupe
  has it, so the two changes merge without conflict

Testing: uv run pytest plugins/vibe (256 passed), ruff, pyright
Risks: None identified
- The person no longer mentions dark mode, so the eval measures the
  rule that renders both themes rather than her request

Testing: uv run pytest plugins/vibe
Risks: None identified
- Codex code mode runs several shell commands in one tool call, so a
  tool_used grader counts each matching command, not each call
- Thread graders accept answer or respond and no longer match
  'unresolved' as resolution

Testing: uv run pytest plugins/vibe, ruff, pyright
Risks: None identified
- A Codex eval run added a .dark rule to a static page whose tokens
  already carry dark values; the writer now states that dark mode comes
  from the bundled tokens and the surface adds no dark rule of its own

Testing: uv run pytest plugins/vibe
Risks: None identified
- Codex summaries of step 7 dropped resolving fixed threads; the step
  now says it resolves each thread it fixed in its main clause

Testing: uv run pytest plugins/vibe
Risks: None identified
- Claude and Codex scores on the three new cases, here and on 1.0.47

Testing: n/a (changelog only)
Risks: None identified
# Conflicts:
#	CHANGELOG.md
#	plugins/vibe/skills/handoff/SKILL.md
- The vibe skill's post-PR summary named the reply but not the
  resolution, and two of three Codex rehearsals dropped it

Testing: uv run pytest plugins/vibe
Risks: None identified
- Thread resolution in the changelog's post-PR entry, and the earlier
  Codex runs that left it out

Testing: n/a (changelog only)
Risks: None identified
@shafty023
shafty023 merged commit bbe43ee into main Oct 16, 2026
10 checks passed
@shafty023
shafty023 deleted the fix/vibe-pm-review-gaps branch October 16, 2026 16:53
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