feat(ship): the child contract names the title gate among its guards - #1004
Conversation
There was a problem hiding this comment.
LGTM: Guard entry and exported constant match the unit contract with a solid render/truncation test; only a spec enumeration in agent-ship.md item 13 needs a same-PR update.
- [minor] F1 docs/reference/specs/agent-ship.md:23 — Spec contradiction — agent-ship.md item 13: GUARDS now includes check:pr-title but the spec still enumerates exactly four guards
Verdict: approve — the change does exactly what unit U1 asks, with one minor spec drift to fix.
What the PR does (2 files, +21/−0, fully read at 8eafdba): adds a check:pr-title entry to GUARDS in src/core/ship/contract.ts with a one-line refusal in the house voice ("a title whose type, scope or grammar is not the changelog line, the scope being one of the code map's Areas"), exports PR_TITLE_GUARD as a named constant, and adds a test asserting the rendered Guards line appears in full both at the default budget and at maxChars: 10 (guards never truncated).
Findings
- F1 (minor) — Spec contradiction —
docs/reference/specs/agent-ship.mditem 13: the spec still says "the guards arespecs:check,specs:coverage --test-guard,hygiene:checkanddecisions:check", an exhaustive enumeration the code now exceeds withcheck:pr-title. The spec follows the code in the same PR — update the item's guard list here.
Contract check: unit names no spec rows (received none, per the conservative rule — consistent, though F1 shows the covering spec's prose still needed the edit). Both test scenarios from the unit are present: the Guards section names check:pr-title with its refusal line, and the tiny-budget render keeps it. Test guard: test-guard ok. specs:coverage covers the changed paths via agent-ship.md.
Otherwise clean: the constant is used in both the entry and the test (no copied string), placement beside the existing guards matches the pattern, and the truncation invariant is exercised, not just asserted statically.
The rendered child contract's Guards section now lists check:pr-title with one line on what it refuses — a title whose type, scope or grammar is not the changelog line, the scope being one of the code map's Areas — and the guard's name is exported as PR_TITLE_GUARD so another module can name the same gate without a copied string. Plan: docs/plans/2026-09-14-001-feat-title-gate-in-the-child-contract-plan.md, unit U1.
… four it already enumerated Review finding F1 on the unit's pull request: the spec enumerated the guards exhaustively and the code now exceeds it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
8eafdba to
9833126
Compare
There was a problem hiding this comment.
LGTM: F1 fixed — agent-ship.md item 13 now names check:pr-title beside the four guards, matching the code; no new findings.
Re-review verdict: approve — F1 is fixed and nothing new surfaced.
- F1 (prior round, fixed):
docs/reference/specs/agent-ship.mditem 13 now enumerates all five guards,check:pr-titlebesidespecs:check,specs:coverage --test-guard,hygiene:checkanddecisions:check, with its refusal line matching the code'sGUARDSentry word for word. The spec-vs-code contradiction is gone. - The rebase onto main over #1011/#1013 left the unit's own commits (
e9fa7077,9833126b) unchanged in substance:PR_TITLE_GUARDexport, the guard entry insrc/core/ship/contract.ts, and an additive test asserting the guard's name and refusal line survive even a tiny render budget (guards are never cut). - Test guard: additions only, nothing removed or weakened; the spec update is in the same diff, so no spec contradiction remains.
Head reviewed: 9833126b.
TL;DR
The rendered child contract's Guards section now lists
check:pr-titlebeside the four existing guards, with one line on what it refuses. The guard's name is exported as a constant so other modules can name the same gate without a copied string.What & why
Unit U1 of docs/plans/2026-09-14-001-feat-title-gate-in-the-child-contract-plan.md: a coding child handed a contract should know the title gate exists — a title whose type, scope or grammar is not the changelog line, the scope being one of the code map's Areas, is refused by
npm run check:pr-title/ CI'stitlecheck. TheGUARDSlist insrc/core/ship/contract.tsgains the entry, in the same one-line-on-what-it-refuses voice as its neighbors, andPR_TITLE_GUARDis exported for the prompt module to import.Tour
1. The exported guard name and the new GUARDS entry
PR_TITLE_GUARD is a named constant so another module can reference the same gate without a copied string; the GUARDS entry sits beside decisions:check and states the refusal in one line, including the scope rule.
Look for: the refusal line matches the unit's wording: type, scope or grammar not the changelog line, scope from the code map's Areas
switchboard/src/core/ship/contract.ts
Lines 82 to 108 in 8eafdba
2. The test: named in full, even under a tiny budget
The rendered contract's Guards section contains the full guard line; a render cut to maxChars 10 still contains it, proving guards are never truncated (renderContract's cut order never touches guards).
switchboard/src/core/ship/contract.test.ts
Lines 425 to 435 in 8eafdba
3. Remaining changes
Decisions
Risks & implications
Minimal: a purely additive entry to a rendered prompt block plus a new exported constant. Any test asserting the exact guard count or full Guards text would break — none exist (the render tests loop over GUARDS).
Validation
Red first, then green; full verify passes.
npm cirepaired pre-existing installed-dep drift under deploy/cloudflare-sandbox, unrelated to this change)🤖 Generated with Claude Code