* test(#4145): regression rows for hash-matching prefix-less pristine baselines RED skeleton: src/pristine-baseline.cts exports findPristineByHash as a null-returning stub so the new rows fail behaviorally, not at require time. Failing-first rows: verifier resolution (no_baseline must drop to 0 when an exact-hash orphan exists), findPristineByHash unit row, and the two saveLocalPatches relocation rows. Negative-space rows pin today's behavior: missing baselines still report ok_no_baseline, mismatching orphans are never adopted or deleted, canonical precedence and the #3657 drift posture are untouched. * fix(#4145): resolve gsd-pristine/ baselines by recorded hash, relocate orphans Both pristine readers joined the manifest-keyed path strictly, so a snapshot stored without the gsd-core/ prefix (an earlier release's writer) was reported as ok_no_baseline by the verifier and pushed into regeneration by saveLocalPatches — where incoming-release candidates can never satisfy the recorded outgoing hash, leaving the correct baseline permanently unconsumed. - src/pristine-baseline.cts (new, ADR-457): shared findPristineByHash — deterministic sorted scan of gsd-pristine/, exact sha-256 equality with the recorded pristine_hashes entry (the same authority the #3657 drift guard trusts), symlink-skipping, canonical path excluded via skipRel. - verify-reapply-patches.cjs verifyFile(): on canonical miss with a recorded hash, adopt byte-identical content found anywhere under gsd-pristine/ before reporting OK_NO_BASELINE. Drift posture (#3657), canonical precedence, and the frozen REASON/report shapes are untouched; the verifier stays read-only. - install.js saveLocalPatches(): preserve-check rescue — relocate a hash-matching orphan to the canonical path (copy, hash-verify, then remove the orphan) so the state self-heals on the next update instead of repeating forever. Honest accounting: new non-overlapping rescued counter. - Workflow doc: one-sentence note on hash-based snapshot resolution. - Derived ripples: INVENTORY-MANIFEST.json regen, eslint ignore + .gitignore entries for the compiled artifact, seedFixture mkdir fix in the new rows. Emitted-Drift-Ack-Growth: reapply-patches.md — one-sentence note on hash-based pristine snapshot resolution (#4145) * fix(#4145): review follow-up — orphan scan never consumes a canonical path Adversarial review finding: with two modified files sharing byte-identical outgoing content, recoverOrphanedPristine could adopt the OTHER file's canonical pristine as its rescue source — relocating it (copy + delete at its home path) and ping-ponging the single baseline between the two files across updates. findPristineByHash's skip parameter now accepts a Set, and saveLocalPatches passes the normalized manifest keys so every canonical path is excluded; only genuine non-canonical orphans are eligible for removal (no strict-join reader ever consults those). Adds the canonical-theft regression row, a Set-skip unit assertion, and tightens the workflow doc sentence the same pass flagged as overstated. * fix(#4145): INVENTORY roster row + symlink-fixture correction Two leftovers from the ab17b7a1e5 bench run, both root-caused: - docs/INVENTORY.md roster row for cli_modules/pristine-baseline.cjs (#3762 gate: every manifest entry carries a row). - The findPristineByHash symlink unit fixture placed its symlink target INSIDE the scanned root, so the walk legitimately matched the real target file. The implementation skips the symlink itself; the fixture now keeps the target outside the scanned tree so the assertion tests what it claims. * changeset(#4145): fixed fragment for pristine baseline hash resolution --------- Co-authored-by: gsd-agent <agent@gsd.local>
This commit is contained in:
@@ -475,6 +475,7 @@
|
||||
"planning-scope.cjs",
|
||||
"planning-snapshot.cjs",
|
||||
"planning-workspace.cjs",
|
||||
"pristine-baseline.cjs",
|
||||
"probe-core.cjs",
|
||||
"profile-output.cjs",
|
||||
"profile-pipeline-command-router.cjs",
|
||||
|
||||
@@ -603,6 +603,7 @@ Full listing: `gsd-core/bin/lib/*.cjs`.
|
||||
| `planning-scope.cjs` | Frozen `SCOPE` discriminator (`COMPLETE`/`TRUNCATED`/`UNSCOPED`/`UNREADABLE`) distinguishing a genuinely-empty derivation from one computed over a truncated or unscoped input, so callers can branch on the difference instead of reading a plausible zero (ADR-3180) |
|
||||
| `planning-snapshot.cjs` | Parsed projection of `.planning/` composed exclusively from the ADR-3180 §7 owners (milestone identity, phase enumeration, phase completion, plan/summary counting, STATE.md current-phase) — exposes only scope-carrying parsed values, never raw document text, so a diagnostic rule cannot re-derive a field's location (ADR-3180 §8.1) |
|
||||
| `planning-workspace.cjs` | Planning path/workstream seam (`planningDir`, `planningPaths`, active-workstream routing, `.planning/.lock` orchestration) |
|
||||
| `pristine-baseline.cjs` | Hash-first recovery for `gsd-pristine/` baselines stored at an unexpected path (compiled from `src/pristine-baseline.cts`, gitignored; #4145) — `findPristineByHash(pristineDir, recordedHash, skip?)` walks `gsd-pristine/` in deterministic sorted order, skips symlinks, and returns the first file whose SHA-256 equals the recorded `backup-meta.json.pristine_hashes` entry (the same authority the #3657 drift guard trusts); the `skip` set excludes canonical manifest-keyed paths so a relocation never consumes another file's canonical baseline. Shared by `verify-reapply-patches.cjs`'s `verifyFile` (read-only adoption when the strict join misses) and `install.js`'s `saveLocalPatches` (orphan relocation self-heal) so the two readers cannot drift apart again |
|
||||
| `project-root.cjs` | Resolves a project root from a starting directory using four heuristics (own `.planning/` guard, `sub_repos` config, `multiRepo` flag, `.git` heuristic) |
|
||||
| `profile-output.cjs` | Profile rendering, USER-PROFILE.md and dev-preferences.md generation |
|
||||
| `profile-pipeline-command-router.cjs` | ADR-959 capability command router for the profile-pipeline command family — dispatches scan-sessions, extract-messages, profile-sample (pipeline phase) and write-profile, profile-questionnaire, generate-dev-preferences, generate-claude-profile, generate-claude-md (output phase); phase 6 cutover |
|
||||
|
||||
Reference in New Issue
Block a user