* fix(#4623): keep repo-wide planning docs out of the verification digest, and accept --files on verification.fingerprint
Two defects in the covered-input fingerprint (#4155), one issue.
1. `computeCoveredDigest` hashed the whole bytes of every declared path
uniformly, so `.planning/ROADMAP.md` and `.planning/REQUIREMENTS.md` —
which every phase rewrites as ordinary bookkeeping, and which the closing
phase's own `phase.complete` / `requirements mark-complete` rewrite AFTER
the verifier ran — flipped every phase that declared them to `stale` on
zero implementation change, and from there `isPhaseComplete` →
`init.manager` → `complete-milestone`'s `ALL_PHASES_VERIFIED` gate.
Fingerprint v2 leaves any direct child of a planning root out of the
hash: `.planning/` itself, plus the phase's own planning root (the parent
of its `phases/`, so `planningDir`'s `<project>/` and `workstreams/<ws>/`
layouts are covered without the digest knowing what a workstream is —
`sharedPlanningRoots` / `isSharedPlanningDoc`, defined by position rather
than a name list so the set cannot drift; a root is accepted only when the
phase dir sits under a `phases/` directory inside `.planning/`). Such a path is still validated
exactly as every other covered path (confined, present, a regular file —
the fail-closed contract is unchanged); only its bytes are ignored, and a
declaration made only of shared documents fails closed like an empty one.
A stored digest names its version, and `readVerificationStatus` now
recomputes under THAT version (`parseFingerprintVersion`,
`KNOWN_FINGERPRINT_VERSIONS`): a legacy v1 report keeps v1 semantics
until it is re-fingerprinted, so the upgrade alone stales nothing; a
version this build cannot recompute fails closed.
2. `verification.fingerprint` received a raw positional slice, so
`--files a`, `--files "a,b"` and `--files a --files b` all put the literal
token into the covered set and failed closed as "a covered file is
missing, unreadable, or escapes the project root" — the message that
convinced the reporting project the digest was permanently
unrecomputable. `parseFingerprintFileArgs` accepts every form (plus
`--files=a,b`, freely mixed with bare positionals), treats any other
`--flag` and an empty `--files` value as usage errors that say so, and
the phase-dir argument must now be an existing directory: omitting it
used to take the first covered file as the phase dir and print a
plausible digest over the rest at exit 0.
Regression tests (tests/verification-status.test.cjs, #4623 block): the
cross-phase case from the report, the same-phase `requirements
mark-complete` / `phase.complete` cases from the thread, a workstream-scoped
root, v1-preserved / unknown-version-stale, the fail-closed cases (missing,
directory, escaping symlink, all-shared), every `--files` form against the
bare form, the unknown-flag / empty-value / omitted-phase-dir errors, and
AC5's zero-file error. Verified failing against the pre-fix source: 29 of 34
fail, the 7 that pass pin behaviour the fix must leave unchanged.
Docs: CONTEXT.md Verification Module, agents/gsd-verifier.md's
covered_files instruction (rewritten in place — the file sits 21 bytes under
its LARGE hard cap), gsd-core/templates/verification-report.md.
Fixes#4623
Emitted-Drift-Ack-Growth: gsd-verifier.md — the #4155 covered_files instruction now states that planning-root docs are digest-inert (#4623); +18 bytes, under the LARGE cap
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DCMY8P8s6dp4g3Rxu3nNAi
* chore(#4623): set changeset fragment pr to 4749
---------
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Tom Boucher <trekkie@nomorestars.com>