Phase 3 of epic #4636, stage 3b. Satisfies #4653's criterion that
`opts.allowAbsolute` become "a named acceptance policy on the predicate, not a
per-call-site boolean".
The flag was actively misleading at the call site. `{ allowAbsolute: true }`
reads as "containment is relaxed here". It never was: an absolute path that
resolves outside the root is rejected exactly as a traversal is. The flag only
ever controlled whether an absolute candidate was CONSIDERED. On a security
predicate that is the wrong thing for a reviewer to have to infer, and 31 call
sites were asking them to infer it.
PathAcceptance.RelativeOnly relative candidates only
PathAcceptance.AbsoluteInsideRoot absolute accepted, containment unchanged
The three exported wrappers take the policy and translate it inward.
validatePath keeps its internal `{ allowAbsolute }` opts and its body untouched —
the engine is not re-derived here either, only the exported surface is renamed.
MEASURED, NOT ESTIMATED. 31 call sites across 10 files, counted by walking the
AST with the repo's own @typescript-eslint/parser rather than grepping: a text
match would have folded in the options-type declaration, default parameter
values and comments. All 31 pass the literal `true`; none passes `false` or a
dynamic value, so the migration is uniform and `RelativeOnly` is purely the
existing default made nameable. audit.cts alone holds 18 of them.
This migration is compiler-verified in a way the containment-value migration in
the previous commit was not: the parameter type changed from an object to a
string union, so any missed site is a build error rather than a silent
behavioral difference. That is why a 31-site mechanical edit is acceptable in
the phase whose stated risk is the width of mechanical change.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Add an optional structured `coverage:` block to SUMMARY.md frontmatter and a
deterministic classifier that `verify-work` consumes to route deliverables to
auto-pass vs human-UAT — replacing the rejected #1598/#1599 post-hoc heuristic.
- New `src/coverage.cts` (→ bin/lib/coverage.cjs) parses the nested coverage
block (extractFrontmatter can't — its `-` items are scalars-only; this is a
focused parser, sibling of parseMustHavesBlock), validates each entry, and
classifies into auto_passed vs present. Frozen MODE/PRESENT_REASON/ERROR_CODE
typed-IR surface. Exposed via `uat classify-coverage --summary <f>`.
- Auto-pass is the narrow proven case only: strict-boolean human_judgment:false
AND non-empty all-`pass` verification AND zero validation errors. Everything
else — judgment, empty/failing verification, malformed entry — routes to the
human (fail-safe). A malformed block falls back to legacy prose extraction and
surfaces an error; an absent block is byte-identical to pre-#1602.
- execute-plan create_summary populates the block (fail-safe default
human_judgment:true); verify-work extract_tests consumes it; create_uat_file
marks auto-passed entries `source: automated`.
- Templates (summary + 3 variants), CONTEXT.md predicate + glossary, INVENTORY,
eslint/gitignore registration, and Diataxis docs (COMMANDS reference +
USER-GUIDE explanation) updated.
- Behavioral tests via the CLI (no source-grep); parser-robustness regressions
for the null-entry/comment-header/mis-indent cases found in adversarial review.
Closes#1602
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>