fix(#4145): resolve gsd-pristine/ baselines by recorded hash, relocate orphans (#4364)

* 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:
Tom Boucher
2026-09-06 02:04:18 -04:00
committed by GitHub
parent d5a85da8ab
commit 6adf3098ac
11 changed files with 724 additions and 7 deletions

View File

@@ -4013,3 +4013,227 @@ describe('Bug #4086: saveLocalPatches resolves skills/ manifest keys at the runt
});
});
}
// ────────────────────────────────────────────────────────────────────────
// Folded regression block — #4145 (self-heal side). saveLocalPatches'
// preserve-check resolved gsd-pristine/ entries strictly by the manifest-keyed
// path, so a hash-matching snapshot stored without the gsd-core/ prefix was
// pushed into regeneration from the incoming release; when upstream changed
// the file, the candidate hash-mismatched and was discarded. The correct
// baseline was never consumed and never pruned — the state repeated on every
// future update. The fix rescues exact-recorded-hash orphans by relocating
// them to the canonical path.
// ────────────────────────────────────────────────────────────────────────
{
const { describe: __foldDescribe } = require('node:test');
__foldDescribe('folded:bug-4145-saveLocalPatches-orphan-rescue', () => {
'use strict';
process.env.GSD_TEST_MODE = '1';
const { test, describe, beforeEach } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const os = require('node:os');
const crypto = require('node:crypto');
const ROOT = path.join(__dirname, '..');
const INSTALL = require(path.join(ROOT, 'bin', 'install.js'));
const { cleanup } = require('./helpers.cjs');
const MANIFEST_NAME = 'gsd-file-manifest.json';
function sha256(content) {
return crypto.createHash('sha256').update(content).digest('hex');
}
describe('Bug #4145: saveLocalPatches rescues hash-matching orphaned pristine snapshots', () => {
let tmpDir;
let configDir;
let newSrcDir;
let pristineDir;
const FILE = 'gsd-core/bin/lib/frontmatter.cjs';
const OLD_PRISTINE = '# Old Release Content\nThis is the outgoing pristine.\n';
const NEW_RELEASE = '# New Release Content\nUpstream rewrote this file wholesale in v2.\n';
const USER_MODIFIED = OLD_PRISTINE + '## User addition\nUser customization here.\n';
beforeEach((t) => {
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-4145-slp-'));
configDir = path.join(tmpDir, 'config');
newSrcDir = path.join(tmpDir, 'new-release-src');
pristineDir = path.join(configDir, 'gsd-pristine');
fs.mkdirSync(configDir, { recursive: true });
fs.mkdirSync(newSrcDir, { recursive: true });
t.after(() => {
cleanup(tmpDir);
});
});
function seedFixture({ orphanRel, orphanContent, canonicalContent, newReleaseContent }) {
fs.mkdirSync(path.dirname(path.join(configDir, FILE)), { recursive: true });
fs.writeFileSync(path.join(configDir, FILE), USER_MODIFIED);
fs.writeFileSync(
path.join(configDir, MANIFEST_NAME),
JSON.stringify({ version: '1.0.0', files: { [FILE]: sha256(OLD_PRISTINE) } }, null, 2),
);
if (canonicalContent !== undefined) {
fs.mkdirSync(path.dirname(path.join(pristineDir, FILE)), { recursive: true });
fs.writeFileSync(path.join(pristineDir, FILE), canonicalContent);
}
if (orphanRel !== undefined) {
fs.mkdirSync(path.dirname(path.join(pristineDir, orphanRel)), { recursive: true });
fs.writeFileSync(path.join(pristineDir, orphanRel), orphanContent);
}
fs.mkdirSync(path.dirname(path.join(newSrcDir, FILE)), { recursive: true });
fs.writeFileSync(path.join(newSrcDir, FILE), newReleaseContent);
}
/**
* Core regression (self-heal): the hash-matching snapshot sits at
* bin/lib/frontmatter.cjs — without the gsd-core/ segment. The new release
* changed the file upstream, so regeneration candidates are discarded.
* After the fix the orphan is relocated to the canonical manifest-keyed
* path and the unprefixed copy no longer lingers.
*/
test('#4145: saveLocalPatches relocates a hash-matching unprefixed orphan to the canonical pristine path', () => {
const orphanRel = 'bin/lib/frontmatter.cjs';
seedFixture({ orphanRel, orphanContent: OLD_PRISTINE, newReleaseContent: NEW_RELEASE });
INSTALL.saveLocalPatches(configDir, {
packageSrc: newSrcDir, runtime: 'claude', pathPrefix: '$HOME/.claude/', isGlobal: true,
});
const canonical = path.join(pristineDir, FILE);
assert.ok(fs.existsSync(canonical), 'canonical prefixed pristine must exist after the update');
assert.equal(sha256(fs.readFileSync(canonical, 'utf8')), sha256(OLD_PRISTINE),
'relocated baseline must carry the outgoing (recorded-hash) bytes, not new-release bytes');
assert.equal(fs.existsSync(path.join(pristineDir, orphanRel)), false,
'the unprefixed orphan must not linger once relocated');
});
/**
* Stale canonical (new-release bytes) + hash-matching orphan elsewhere:
* the stale entry is removed by the #3407 path and then rescued from the
* orphan — the file must not end in no-baseline limbo.
*/
test('#4145: rescues after stale-canonical removal when a hash-matching orphan exists', () => {
seedFixture({
orphanRel: 'legacy/frontmatter.cjs',
orphanContent: OLD_PRISTINE,
canonicalContent: NEW_RELEASE, // stale — hash mismatch
newReleaseContent: NEW_RELEASE,
});
INSTALL.saveLocalPatches(configDir, {
packageSrc: newSrcDir, runtime: 'claude', pathPrefix: '$HOME/.claude/', isGlobal: true,
});
const canonical = path.join(pristineDir, FILE);
assert.ok(fs.existsSync(canonical), 'canonical pristine must exist after stale removal + rescue');
assert.equal(sha256(fs.readFileSync(canonical, 'utf8')), sha256(OLD_PRISTINE),
'rescued baseline must carry the recorded-hash bytes');
assert.equal(fs.existsSync(path.join(pristineDir, 'legacy', 'frontmatter.cjs')), false,
'the orphan must be consumed by the relocation');
});
/** Negative space: no orphan, upstream changed — regeneration discard (#3407) is unchanged. */
test('#4145: leaves the baseline absent when no orphan exists and upstream changed', () => {
seedFixture({ newReleaseContent: NEW_RELEASE });
INSTALL.saveLocalPatches(configDir, {
packageSrc: newSrcDir, runtime: 'claude', pathPrefix: '$HOME/.claude/', isGlobal: true,
});
assert.equal(fs.existsSync(path.join(pristineDir, FILE)), false,
'no hash-matching source exists — the baseline must stay absent (over-broad/no-baseline fallback)');
});
/** Negative space: a mismatching orphan is neither adopted nor deleted. */
test('#4145: never adopts nor deletes a hash-mismatching orphan', () => {
const orphanRel = 'bin/lib/frontmatter.cjs';
seedFixture({ orphanRel, orphanContent: NEW_RELEASE, newReleaseContent: NEW_RELEASE });
INSTALL.saveLocalPatches(configDir, {
packageSrc: newSrcDir, runtime: 'claude', pathPrefix: '$HOME/.claude/', isGlobal: true,
});
assert.equal(fs.existsSync(path.join(pristineDir, FILE)), false,
'mismatching bytes must not be written to the canonical pristine path');
assert.equal(fs.existsSync(path.join(pristineDir, orphanRel)), true,
'pruning files the recorded hashes do not vouch for is not this fix\'s job');
});
/**
* Review finding (fix follow-up): two modified files sharing byte-identical
* outgoing content. The orphan scan must never consume a path that is
* another manifest file's canonical pristine path — otherwise the rescue
* would relocate a correct canonical away from its owner and the two files
* would ping-pong it between updates. Only genuine non-canonical orphans
* are eligible.
*/
test('#4145: does not steal a byte-identical canonical belonging to another modified file', () => {
const FILE_B = 'gsd-core/bin/lib/other-file.cjs';
const SHARED_OLD = '# Shared Old Content\nByte-identical across two manifest files.\n';
// A and B are both user-modified on top of byte-identical outgoing stock,
// so both manifest records carry the SAME pristine hash.
fs.mkdirSync(path.dirname(path.join(configDir, FILE_B)), { recursive: true });
fs.writeFileSync(path.join(configDir, FILE), SHARED_OLD + '## User addition A\nCustom A.\n');
fs.writeFileSync(path.join(configDir, FILE_B), SHARED_OLD + '## User addition B\nCustom B.\n');
fs.writeFileSync(
path.join(configDir, MANIFEST_NAME),
JSON.stringify({
version: '1.0.0',
files: { [FILE]: sha256(SHARED_OLD), [FILE_B]: sha256(SHARED_OLD) },
}, null, 2),
);
// A (processed first) has the ALREADY-correct canonical holding the shared
// old bytes. B has no canonical and no orphan — B's only possible hash
// match is A's canonical. Without the canonical skip set, B's rescue would
// copy A's canonical to B's path and then DELETE A's canonical.
fs.mkdirSync(path.dirname(path.join(pristineDir, FILE)), { recursive: true });
fs.writeFileSync(path.join(pristineDir, FILE), SHARED_OLD);
fs.mkdirSync(path.dirname(path.join(newSrcDir, FILE)), { recursive: true });
fs.writeFileSync(path.join(newSrcDir, FILE), NEW_RELEASE);
fs.mkdirSync(path.dirname(path.join(newSrcDir, FILE_B)), { recursive: true });
fs.writeFileSync(path.join(newSrcDir, FILE_B), NEW_RELEASE);
INSTALL.saveLocalPatches(configDir, {
packageSrc: newSrcDir, runtime: 'claude', pathPrefix: '$HOME/.claude/', isGlobal: true,
});
// A's canonical must survive untouched — never stolen to become B's.
assert.ok(fs.existsSync(path.join(pristineDir, FILE)),
'the byte-identical canonical of the earlier-processed file must survive');
assert.equal(sha256(fs.readFileSync(path.join(pristineDir, FILE), 'utf8')), sha256(SHARED_OLD));
// B gains no baseline from A's canonical (falls to regeneration instead).
assert.equal(fs.existsSync(path.join(pristineDir, FILE_B)), false,
'a canonical path of another file must never be relocated as the rescue source');
});
/** Preserve-path lock: an already-correct canonical stays put; the preserve loop ignores the orphan. */
test('#4145: preserves an already-correct canonical and leaves a coexisting identical orphan in place', () => {
const orphanRel = 'bin/lib/frontmatter.cjs';
seedFixture({
orphanRel,
orphanContent: OLD_PRISTINE,
canonicalContent: OLD_PRISTINE, // already correct
newReleaseContent: NEW_RELEASE,
});
INSTALL.saveLocalPatches(configDir, {
packageSrc: newSrcDir, runtime: 'claude', pathPrefix: '$HOME/.claude/', isGlobal: true,
});
const canonical = path.join(pristineDir, FILE);
assert.ok(fs.existsSync(canonical));
assert.equal(sha256(fs.readFileSync(canonical, 'utf8')), sha256(OLD_PRISTINE),
'preserved canonical must be byte-identical to before the run');
assert.equal(fs.existsSync(path.join(pristineDir, orphanRel)), true,
'the preserve path must not disturb unrelated files');
});
});
});
}

View File

@@ -1170,3 +1170,272 @@ describe('Bug #4086: verifyFile resolves skills entries at the runtime skills ro
});
});
}
// ────────────────────────────────────────────────────────────────────────
// Folded regression block — #4145 (a hash-matching gsd-pristine/ baseline
// stored without the gsd-core/ prefix is never resolved). verifyFile() joined
// the manifest-keyed path strictly; when stat missed and a hash was recorded
// it reported OK_NO_BASELINE even though byte-correct content sat elsewhere
// under gsd-pristine/. The fix consults the recorded pristine_hashes — the
// same authority the #3657 drift guard trusts — and adopts an exact-hash
// match found anywhere in the tree.
// ────────────────────────────────────────────────────────────────────────
{
const { describe: __foldDescribe } = require('node:test');
__foldDescribe('folded:bug-4145-pristine-prefix-resolution', () => {
'use strict';
process.env.GSD_TEST_MODE = '1';
const { test, describe, before, after } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const crypto = require('node:crypto');
const { cleanup } = require('./helpers.cjs');
const { runNode } = require('./helpers/process-seam.cjs');
const ROOT = path.join(__dirname, '..');
const SCRIPT = path.join(ROOT, 'gsd-core', 'bin', 'verify-reapply-patches.cjs');
const { REASON } = require(SCRIPT);
const { findPristineByHash } = require(
path.join(ROOT, 'gsd-core', 'bin', 'lib', 'pristine-baseline.cjs'),
);
let tmpRoot;
let patchesDir;
let configDir;
let pristineDir;
function sha256(content) {
return crypto.createHash('sha256').update(content).digest('hex');
}
function writeFile(absPath, content) {
fs.mkdirSync(path.dirname(absPath), { recursive: true });
fs.writeFileSync(absPath, content);
}
function writeBackupMeta(pristine_hashes) {
writeFile(path.join(patchesDir, 'backup-meta.json'), JSON.stringify({ pristine_hashes }, null, 2));
}
function resetFixture() {
for (const dir of [patchesDir, configDir, pristineDir]) {
cleanup(dir);
}
fs.mkdirSync(patchesDir);
fs.mkdirSync(configDir);
fs.mkdirSync(pristineDir);
}
function runVerifier() {
const r = runNode([
SCRIPT,
'--patches-dir', patchesDir,
'--config-dir', configDir,
'--pristine-dir', pristineDir,
'--json',
], { timeoutMs: 30_000 });
return {
status: r.exitCode,
report: r.stdout && r.stdout.length ? JSON.parse(r.stdout) : null,
};
}
before(() => {
tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-4145-'));
patchesDir = path.join(tmpRoot, 'patches');
configDir = path.join(tmpRoot, 'installed');
pristineDir = path.join(tmpRoot, 'pristine');
resetFixture();
});
after(() => {
cleanup(tmpRoot);
});
describe('Bug #4145: hash-matching prefix-less pristine baseline is resolved', () => {
/**
* Core regression. The manifest key is `gsd-core/bin/lib/frontmatter.cjs`
* but the snapshot sits at `bin/lib/frontmatter.cjs` — one segment away
* from the joined path. Its SHA-256 equals the recorded pristine_hashes
* entry. The upstream release replaced the file wholesale and only the
* user's line survived the merge, so a verifier that recovered the
* baseline computes exactly one user-added line (present → exit 0,
* no_baseline 0), while the pre-fix run reported ok_no_baseline.
*/
test('#4145: resolves a hash-matching prefix-less pristine baseline instead of reporting ok_no_baseline', () => {
resetFixture();
const FILE = 'gsd-core/bin/lib/frontmatter.cjs';
const OLD_PRISTINE =
'outgoing pristine stock line one with substantial content\n' +
'outgoing pristine stock line two also substantial content\n';
const USER_LINE = 'user customization line that must survive the reapply merge';
const backupContent = OLD_PRISTINE + USER_LINE + '\n';
const installedContent =
'incoming upstream replacement line with substantial content\n' + USER_LINE + '\n';
writeBackupMeta({ [FILE]: sha256(OLD_PRISTINE) });
writeFile(path.join(patchesDir, FILE), backupContent);
writeFile(path.join(configDir, FILE), installedContent);
// The orphan: same bytes, stored WITHOUT the gsd-core/ prefix.
writeFile(path.join(pristineDir, 'bin', 'lib', 'frontmatter.cjs'), OLD_PRISTINE);
const { status, report } = runVerifier();
assert.equal(status, 0, `expected exit 0; report=${JSON.stringify(report)}`);
assert.equal(report.no_baseline, 0, 'a hash-matching baseline was on disk — it must be resolved');
assert.deepEqual(report.no_baseline_files, []);
assert.equal(report.failures, 0);
const r0 = report.results[0];
assert.equal(r0.status, 'ok');
assert.notEqual(r0.reason, REASON.OK_NO_BASELINE);
});
/** Negative space: nothing anywhere under gsd-pristine/ matches the record. */
test('#4145: still reports ok_no_baseline when the recorded hash matches nothing under gsd-pristine', () => {
resetFixture();
const FILE = 'gsd-core/bin/lib/frontmatter.cjs';
const backupContent =
'upstream line present in the backup of the outgoing release\n' +
'model: sonnet — the user customisation line in the backup file\n';
const installedContent =
'replacement upstream line in the newer release version\n' +
'model: sonnet — the user customisation line in the backup file\n';
writeBackupMeta({ [FILE]: 'deadbeef00000000000000000000000000000000000000000000000000000001' });
writeFile(path.join(patchesDir, FILE), backupContent);
writeFile(path.join(configDir, FILE), installedContent);
// No pristine file anywhere.
const { status, report } = runVerifier();
assert.equal(status, 0, 'no-baseline is advisory, never a failure');
assert.equal(report.no_baseline, 1);
assert.equal(report.results[0].reason, REASON.OK_NO_BASELINE);
});
/**
* Negative space: an orphan whose bytes do NOT hash to the record is never
* adopted — only exact recorded-hash matches are accepted.
*/
test('#4145: never adopts a hash-mismatching orphan — only exact recorded-hash matches', () => {
resetFixture();
const FILE = 'gsd-core/bin/lib/frontmatter.cjs';
const OLD_PRISTINE = 'outgoing pristine bytes that the record hashes\n';
const OTHER_CONTENT = 'some other release snapshot with different bytes\n';
writeBackupMeta({ [FILE]: sha256(OLD_PRISTINE) });
writeFile(path.join(patchesDir, FILE), 'outgoing pristine bytes that the record hashes\nuser line\n');
writeFile(path.join(configDir, FILE), 'user line\n');
// Orphan exists but hashes to something else.
writeFile(path.join(pristineDir, 'bin', 'lib', 'frontmatter.cjs'), OTHER_CONTENT);
const { status, report } = runVerifier();
assert.equal(status, 0);
assert.equal(report.no_baseline, 1, 'a mismatching orphan is not a baseline');
assert.equal(report.results[0].reason, REASON.OK_NO_BASELINE);
});
/**
* Precedence lock: the canonical prefixed path still resolves exactly as
* today even when an identical-content orphan also exists — the strict join
* stays first, and the verifier (read-only) leaves the orphan untouched.
*/
test('#4145: prefixed canonical baseline resolves exactly as today when an identical orphan also exists', () => {
resetFixture();
const FILE = 'gsd-core/workflows/execute-phase.md';
const pristineContent = 'stock workflow line long enough to pass the significance threshold\n';
const droppedLine = 'user workflow customisation that was lost in the merge operation';
const backupContent = pristineContent + droppedLine + '\n';
const installedContent = pristineContent; // user line dropped — real failure
writeBackupMeta({ [FILE]: sha256(pristineContent) });
writeFile(path.join(patchesDir, FILE), backupContent);
writeFile(path.join(configDir, FILE), installedContent);
writeFile(path.join(pristineDir, FILE), pristineContent);
const orphanPath = path.join(pristineDir, 'workflows', 'execute-phase.md');
writeFile(orphanPath, pristineContent);
const { status, report } = runVerifier();
assert.equal(status, 1, 'the dropped user line must still be caught via the canonical baseline');
const r0 = report.results[0];
assert.equal(r0.status, 'fail');
assert.equal(r0.reason, REASON.FAIL_USER_LINES_MISSING);
assert.ok(r0.missing.includes(droppedLine));
// Read-only verifier: the orphan is never relocated or pruned by a verify run.
assert.equal(fs.existsSync(orphanPath), true, 'verifier must not mutate gsd-pristine/');
});
/**
* Drift-path lock: a hash-MISMATCHING canonical snapshot still reports
* OK_PRISTINE_DRIFT_DETECTED (#3657) — the recovery scan must not reach the
* drift case.
*/
test('#4145: canonical-path drift still reports ok_pristine_drift_detected even when a hash-matching orphan exists', () => {
resetFixture();
const FILE = 'gsd-core/agents/gsd-executor.md';
const oldPristine = 'old pristine line that was present when backup was captured\n';
const newPristine = 'refreshed upstream line in the newer pristine snapshot\n';
const userLine = 'user customisation line that should be preserved across updates';
writeBackupMeta({ [FILE]: sha256(oldPristine) });
writeFile(path.join(patchesDir, FILE), oldPristine + userLine + '\n');
writeFile(path.join(configDir, FILE), newPristine + userLine + '\n');
writeFile(path.join(pristineDir, FILE), newPristine); // canonical drifted
// A hash-matching orphan exists elsewhere — drift must still win.
writeFile(path.join(pristineDir, 'agents', 'gsd-executor.md'), oldPristine);
const { status, report } = runVerifier();
assert.equal(status, 0);
const r0 = report.results[0];
assert.equal(r0.reason, REASON.OK_PRISTINE_DRIFT_DETECTED,
`expected the untouched #3657 drift posture; got ${r0.reason}`);
assert.equal(report.drifted, 1);
});
/** Module unit: deterministic sorted-first match, symlink skip, absent dir. */
test('#4145: findPristineByHash returns the sorted-first match, skips symlinks, and null on an absent dir', () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-4145-unit-'));
const symRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-4145-sym-'));
const outsideRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-4145-out-'));
try {
const contentA = 'identical bytes that two snapshots happen to share\n';
const hashA = sha256(contentA);
writeFile(path.join(root, 'zz-dir', 'late-match.md'), contentA);
writeFile(path.join(root, 'aa.txt'), contentA);
assert.equal(findPristineByHash(root, hashA), 'aa.txt',
'sorted-first match wins deterministically');
assert.equal(findPristineByHash(root, hashA, 'aa.txt'), 'zz-dir/late-match.md',
'skipRel is never returned');
assert.equal(findPristineByHash(root, hashA, new Set(['aa.txt', 'zz-dir/late-match.md'])), null,
'every member of a skip Set is excluded (canonical-path protection)');
assert.equal(findPristineByHash(root, sha256('no such content anywhere here\n')), null,
'no match resolves to null');
assert.equal(findPristineByHash(path.join(root, 'absent'), hashA), null,
'absent dir resolves to null');
// A symlink is never followed, even when its target would hash-match.
// The target lives OUTSIDE symRoot so the only hashable entry inside the
// scanned tree is the symlink itself.
const outsideTarget = path.join(outsideRoot, 'outside-target.md');
fs.writeFileSync(outsideTarget, contentA);
fs.symlinkSync(outsideTarget, path.join(symRoot, 'sym.md'));
assert.equal(findPristineByHash(symRoot, hashA), null,
'symlinked candidates are skipped, not followed');
} finally {
cleanup(root);
cleanup(symRoot);
cleanup(outsideRoot);
}
});
});
});
}