* fix(#2372): widen decision-coverage scan to planner-canonical tags, fix message
Bug: check.decision-coverage-plan's remediation message told the user to
cite decisions "(or body)" but extractPlanDesignatedSections only scanned
<objective>/<tasks>/<task>/<action>. A decision cited in <read_first>,
<behavior>, <verify>, <acceptance_criteria>, or <done> was invisible to
the gate — false BLOCKING coverage gap, plus the message's own fix-hint
sent the user to "the body" where re-citing still failed.
Two-part fix (must change together — that drift was the bug):
1. Widen XML_DECISION_TAGS_RE in src/check-command-router.cts to also
match <read_first>, <behavior>, <verify>, <acceptance_criteria>,
<done>. These are all planner-canonical tags the planner is told to
use (plan-phase.md:830-862, plan-phase.md:772). The body negative-
lookahead mirrors the opening-tag set so each tag's body is captured
independently.
2. Correct buildPlanMessage to name ONLY the surfaces the extractor
actually scans (front-matter must_haves/truths/objective,
designated markdown headings, and the nine planner-canonical tag
bodies). The misleading "(or body)" clause is gone.
Also updates the planner's documented contract (agents/gsd-planner.md:69)
and user-facing docs (docs/CONFIGURATION.md, docs/USER-GUIDE.md) to
reflect the wider scan.
Regression tests in tests/decisions.test.cjs cover each newly-scanned
tag body, a control (no citation still uncovered), and a message/extractor
parity assertion that names every scanned surface — so the two cannot
drift apart again.
Out of scope (per triage): cmdDecisionCoverageVerify/buildVerifyMessage
is a separate command (decision-coverage-verify) checking shipped
artifacts, not plan citations — untouched.
* chore(#2372): regenerate agent-size-baseline + golden-install-parity fixtures
gsd-planner.md grew 49172 → 49294 (+122 chars) from the widened decision-
coverage contract (5 new scanned tag names + heading clarification).
Growth is justified: the contract surface is itself the fix — the prior
text under-described what the gate scans, which was the bug.
Updates:
- tests/agent-size-baseline.json (gsd-planner.md: 49172 → 49294)
- 17 tests/fixtures/golden-install-parity/*.json (one hash per runtime)
- tests/fixtures/install-tree/*.json (regenerated by gen:golden)
* fix(#2372): per-tag matching — outer-tag citations survive inner-tag nesting
Code review (subagent) flagged a Medium edge-case regression from the
single-alternation regex: when a newly-scanned tag nests inside another
scanned tag, the alternation's negative lookahead halts the outer tag's
body at the inner tag — losing any D-NN citation in the outer tag's
prefix prose. Concretely:
<action>per D-05 <verify>npm test</verify></action>
→ 3-tag alternation (old): captured 'per D-05 <verify>npm test</verify>' as <action> body → D-05 caught
→ 9-tag alternation (bug): captured 'npm test' only (from <verify>); D-05 in <action> prefix LOST
Switches extractXmlTagBodies to per-tag matching: each tag gets its own
regex whose negative-lookahead tempers only against the SAME tag's
reopening. So <verify> inside <action> is absorbed into <action>'s body
(D-05 caught) AND <verify> is matched separately on its own pass.
Per-tag preserves both:
- the reporter's case (sibling tags inside <read_first>)
- nested-tag citations in outer-tag prefix prose
- ReDoS safety (each per-tag regex keeps the #2128 body tempering)
Also adds the reviewer's other requested edge-case tests:
- non-scanned tag (<name>) bearing D-NN must NOT count
- self-closing form <read_first /> safely ignored
- attribute form <verify type="...">D-NN</verify> (canonical planner shape)
- CRLF newlines in tag body do not break capture
* chore(changeset): backfill pr:2443 in .changeset/noble-elks-chatter.md