From 08cb985c21416e022ef304f6661f05916a58985b Mon Sep 17 00:00:00 2001 From: Lily Shen <115414357+lilyshen0722@users.noreply.github.com> Date: Sat, 19 Sep 2026 21:12:06 -0700 Subject: [PATCH] test(scripts): assert the AGENTS.md symlink the guard's coverage depends on (vera 70645, wren 70661) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The scan names CLAUDE.md, and AGENTS.md is covered only because git mode 120000 makes it the same file. That is a convention the comment described and nothing checked. Two events take AGENTS.md out of scope, and both are asserted because `isSymbolicLink()` alone only catches the first: - replaced by a real copy -> not a symlink at all - retargeted (`ln -s README.md`) -> still a symlink, pointing somewhere the scan does not follow, so `isSymbolicLink()` passes while AGENTS.md has left scope. The target is now resolved against the link's own directory and must be one of SCANNED_ROOT_FILES, derived from that constant rather than hard-coded, so the title and the assertion cannot drift apart. Also names, in the same comment, the other form the boundary rejects — a parent-relative `../docs/….md` — as a known blind spot rather than leaving it to be discovered; its target depends on the referencing file's directory, which this scan does not model, and there are zero instances in the scanned dirs. Mutations: replace AGENTS.md with a real copy -> RED; retarget it to README.md -> RED (wren's case, and the one the first version of this test let through); invert the symlink assertion -> RED. Restored clean, 6/6. Disclosed survivor: loosening the target assertion to `target.length > 0` survives, because removing a check is undetectable on a tree whose link target is correct — the retarget mutation is what supplies the failing input, and it is the case that goes RED. Held as a follow-up rather than folded into #1807: that head carried two reviewers' stamps and the press was authorised on it. --- .../unit/scripts/docReferences.test.js | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/backend/__tests__/unit/scripts/docReferences.test.js b/backend/__tests__/unit/scripts/docReferences.test.js index 34f83ff5d..4eaaec1df 100644 --- a/backend/__tests__/unit/scripts/docReferences.test.js +++ b/backend/__tests__/unit/scripts/docReferences.test.js @@ -17,7 +17,13 @@ * link checker: prose inside `docs/` is the docs room's inventory to keep, and * this suite has to stay green while that wash runs. A docs-like tail inside a * URL (`…/skills/servicenow-docs/SKILL.md`) is not a repo path, and the - * boundary in DOC_REF is what excludes it. + * boundary in DOC_REF is what excludes it. Two forms stay out and are named + * here rather than left to be discovered: the URL tail above, and a + * parent-relative `../docs/….md`, which the lookbehind rejects at both of its + * possible start positions. The first is excluded on purpose; the second is a + * blind spot with zero instances in the scanned dirs (measured 2026-09-20), and + * it stays out because its target depends on the referencing file's directory, + * which this scan does not model. * * Widened the same day, on the class's own remainder: the first sweep scanned * code only and its boundary rejected a leading slash, so the two dead pointers @@ -113,6 +119,27 @@ describe('code does not point at docs that are not there', () => { )).toEqual([]); }); + // The scan names `CLAUDE.md`; `AGENTS.md` is covered only because git mode + // 120000 makes it the same file. That is a convention, and the coverage above + // depends on it — so assert the convention rather than describing it. + // + // Both ways it can break are asserted, because either one takes `AGENTS.md` + // out of scope and this is the only test that notices: + // - replaced by a real copy -> not a symlink at all + // - retargeted (`ln -s README.md`) -> still a symlink, now pointing + // somewhere the scan does not follow. `isSymbolicLink()` alone passes + // here, which is a test that agrees with its own title and not with the + // tree. + it('AGENTS.md is a symlink to the scanned file, not a second copy', () => { + const link = path.join(REPO_ROOT, 'AGENTS.md'); + expect(fs.lstatSync(link).isSymbolicLink()).toBe(true); + // `lstat`/`readlink` deliberately, not `realpath`: the question is what the + // link declares, and the target is resolved against the link's own + // directory so a relative target is read the way the filesystem reads it. + const target = path.resolve(path.dirname(link), fs.readlinkSync(link)); + expect(SCANNED_ROOT_FILES.map((file) => path.join(REPO_ROOT, file))).toContain(target); + }); + it('covers the front-door file agents are told to follow', () => { expect(references.some((ref) => ref.file === 'CLAUDE.md')).toBe(true); });