Files
msd-core/tests/no-phantom-issue-refs.test.cjs
Jakub Zych 12ee75a509
Some checks failed
Tests / PR mergeability (push) Successful in 1m37s
Tests / Base branch health (push) Successful in 11s
Tests / Detect test scope (push) Successful in 17s
Tests / lint-tests (push) Failing after 2m16s
Tests / plugin-validate (push) Successful in 1m6s
Tests / test (ubuntu-latest, 24, shard 1/3) (push) Failing after 25s
Tests / test (ubuntu-latest, 24, shard 2/3) (push) Failing after 20s
Tests / test (ubuntu-latest, 24, shard 3/3) (push) Failing after 20s
Tests / test (ubuntu-latest, 24) (push) Failing after 19s
Tests / test (inert CI) (push) Has been skipped
Tests / QA loop walk (smell ratchet) (push) Failing after 19s
Tests / Coverage gate (merged shards) (push) Has been skipped
Tests / Publish emitted-baseline artifact (push) Has been skipped
Dismiss Unauthorized PR Approvals / dismiss-unauthorized-approval (push) Successful in 9s
Close Draft PRs (sweep) / Sweep open draft PRs (push) Successful in 8s
Tests / conformance test (macos-latest, 24) (push) Has been cancelled
Tests / conformance test (windows-latest, 24, shard 1/3) (push) Has been cancelled
Tests / conformance test (windows-latest, 24, shard 2/3) (push) Has been cancelled
Tests / conformance test (windows-latest, 24, shard 3/3) (push) Has been cancelled
Tests / Required tests (push) Has been cancelled
chore: point MSD at git.golem15.com/golem15/msd-core
The fork lives on the golem15 Gitea forge, not GitHub. Package identity now
parses either host and derives in-place raw URLs for Gitea; the identity-drift
lint accepts the new host; README drops GitHub-only badges and the npm
quickstart in favour of the checkout installer.
2026-10-06 10:41:56 +02:00

188 lines
8.3 KiB
JavaScript

// allow-test-rule: source-text-is-the-product (see #1073) — this guard asserts the
// ABSENCE of phantom pre-migration issue references in repo text (docs, tests,
// workflows). The file *content* is the product surface here (#1073): dangling
// refs that don't exist in golem15/msd-core mislead triage and manufacture
// phantom blockers. This test fails CI if such a ref is reintroduced.
//
// MAINTENANCE — this list ROTS and must be pruned (#2653).
// GitHub numbers issues and pull requests from one shared counter, so every
// entry below is phantom only until this repo's counter reaches it. Two of the
// original three have already gone real:
// 2551 -> merged PR "fix(#2366): scope parseCoverageMatrix to recognized coverage tables"
// 2361 -> merged PR "docs(#2357): fix Registry Discussions category name and state its format"
// While they remained listed this guard rejected legitimate citations of those
// PRs — it fired on a build-artifact-drift fix that named PR 2551 as the
// provenance of the drift, which is exactly the sort of accurate reference the
// repo wants. Before adding a number, confirm it sits above the current
// counter; once the counter passes an entry, delete it.
'use strict';
const { test } = require('node:test');
const assert = require('node:assert');
const fs = require('node:fs');
const path = require('node:path');
const os = require('node:os');
const ROOT = path.resolve(__dirname, '..');
// Phantom pre-migration (get-shit-done-redux) issue numbers with NO equivalent
// in golem15/msd-core. Matched only with a leading '#' or in an issues/ URL so
// digit-bearing strings like an `id_ed25519` SSH key fingerprint are NOT
// false-positives.
//
// Re-verified against the live repo on 2026-08-07: the shared issue/PR
// counter has now passed 3182 — it is the real Phase-0 sub-issue of epic
// #3180 — so it was pruned per the maintenance rule above, leaving the list
// empty.
const PHANTOM = [];
// Builds the phantom-ref regex from a list of numbers. Returns null when the
// list is empty — that null is load-bearing, not defensive: interpolating an
// empty list into the alternation produces `(?:#(?:)\b)|(?:issues/(?:)\b)`,
// whose empty alternative matches EVERY issue reference (`#1073`,
// `issues/2653`, etc.), turning the guard into a false-positive machine.
// Callers must treat null as "no phantom numbers defined, nothing to scan".
function buildRefRe(phantom) {
if (!phantom.length) return null;
return new RegExp(
'(?:#(?:' + phantom.join('|') + ')\\b)|(?:issues/(?:' + phantom.join('|') + ')\\b)',
);
}
const REF_RE = buildRefRe(PHANTOM);
const SCAN_EXT = new Set(['.md', '.cjs', '.js', '.cts', '.ts']);
const SKIP_DIRS = new Set(['node_modules', '.git', 'dist', 'coverage', '.changeset', '.claude', '.planning']);
// This guard file itself names the phantom numbers (by necessity); exclude it.
const SELF = path.relative(ROOT, __filename);
function walk(dir, acc) {
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
if (entry.isDirectory()) {
if (!SKIP_DIRS.has(entry.name)) walk(path.join(dir, entry.name), acc);
// entry.isFile() excludes symlinks (and other non-regular dirents) so a broken symlink like
// a gitignored CLAUDE.md worktree symlink is skipped deterministically on every platform —
// it can't be read and isn't shipped repo text (#1545).
} else if (entry.isFile() && SCAN_EXT.has(path.extname(entry.name))) {
acc.push(path.join(dir, entry.name));
}
}
return acc;
}
test('no phantom pre-migration issue references remain in repo text (#1073)', (t) => {
if (!REF_RE) {
t.skip('PHANTOM list is empty — no phantom numbers to guard against');
return;
}
const offenders = [];
for (const file of walk(ROOT, [])) {
const rel = path.relative(ROOT, file);
if (rel === SELF) continue;
const lines = fs.readFileSync(file, 'utf8').split(/\r?\n/);
lines.forEach((line, i) => {
if (REF_RE.test(line)) offenders.push(`${rel}:${i + 1}: ${line.trim().slice(0, 120)}`);
});
}
assert.strictEqual(
offenders.length,
0,
`Phantom issue refs (${PHANTOM.map((n) => '#' + n).join('/')}) found — repoint to a real ` +
`successor (#717/#720) or rewrite as prose (see #1073):\n` + offenders.join('\n'),
);
});
test('walk() skips broken symlinks and does not throw ENOENT (#1545)', (t) => {
const fixture = fs.mkdtempSync(path.join(os.tmpdir(), 'nophantom-symlink-'));
let symlinkCreated = false;
try {
fs.writeFileSync(path.join(fixture, 'real.md'), '# real, no phantom refs\n');
try {
fs.symlinkSync(
path.join(fixture, 'does-not-exist-target'),
path.join(fixture, 'broken.md'),
);
// Verify the symlink actually exists (lstat succeeds even for dangling symlinks)
fs.lstatSync(path.join(fixture, 'broken.md'));
symlinkCreated = true;
} catch (e) {
// Windows without symlink privilege — genuine skip
}
if (!symlinkCreated) {
t.skip('platform cannot create symlinks unprivileged');
return;
}
const found = walk(fixture, []).map((f) => path.basename(f));
assert.ok(found.includes('real.md'), 'walk() must include real.md');
assert.ok(!found.includes('broken.md'), 'walk() must NOT include broken.md (broken symlink)');
// Mirror the production read loop — must not throw ENOENT
assert.doesNotThrow(
() => found.length && walk(fixture, []).forEach((fp) => fs.readFileSync(fp, 'utf8')),
'readFileSync on every walk() result must not throw (no broken symlinks returned)',
);
} finally {
// eslint-disable-next-line local/no-raw-rmsync-in-tests -- local cleanup in standalone guard test; no helpers import available (would introduce a test-dep cycle)
fs.rmSync(fixture, { recursive: true, force: true });
}
});
test('walk() does not scan ambient .claude/.planning content (#3321)', () => {
const fixture = fs.mkdtempSync(path.join(os.tmpdir(), 'nophantom-ambient-'));
try {
fs.writeFileSync(path.join(fixture, 'real.md'), '# real, no phantom refs\n');
const claudeDir = path.join(fixture, '.claude', 'worktrees', 'some-agent-worktree');
fs.mkdirSync(claudeDir, { recursive: true });
fs.writeFileSync(path.join(claudeDir, 'offender.md'), '#94729 sentinel phantom ref\n');
const planningDir = path.join(fixture, '.planning', 'notes');
fs.mkdirSync(planningDir, { recursive: true });
fs.writeFileSync(path.join(planningDir, 'offender.md'), '#94729 sentinel phantom ref\n');
const found = walk(fixture, []).map((f) => path.relative(fixture, f));
assert.ok(found.includes('real.md'), 'walk() must still include real.md at fixture root');
assert.ok(
!found.some((f) => f.startsWith('.claude' + path.sep)),
'walk() must NOT descend into .claude/',
);
assert.ok(
!found.some((f) => f.startsWith('.planning' + path.sep)),
'walk() must NOT descend into .planning/',
);
// Prove the offending content WOULD have matched if scanned -- i.e. this isn't a vacuous
// "no file happened to match" pass. Use a test-local sentinel, never the module PHANTOM.
const sentinelRe = buildRefRe(['94729']);
const offenderContent = fs.readFileSync(path.join(claudeDir, 'offender.md'), 'utf8');
assert.ok(
sentinelRe.test(offenderContent),
'sanity: sentinel ref pattern must actually match the offender content',
);
} finally {
// eslint-disable-next-line local/no-raw-rmsync-in-tests -- local cleanup in standalone guard test; no helpers import available (would introduce a test-dep cycle)
fs.rmSync(fixture, { recursive: true, force: true });
}
});
test('buildRefRe() boundaries — empty list is inert, single/double entries match exactly', () => {
assert.strictEqual(buildRefRe([]), null, 'empty list must build no regex');
const single = buildRefRe(['9999']);
assert.ok(single.test('#9999'), 'single-entry list must match #<n>');
assert.ok(single.test('issues/9999'), 'single-entry list must match issues/<n>');
assert.ok(!single.test('#99990'), 'word boundary must reject a longer number sharing the prefix');
assert.ok(!single.test('9999'), 'bare digits with no # or issues/ prefix must not match');
const double = buildRefRe(['9999', '8888']);
assert.ok(double.test('#9999'), 'two-entry list must match the first entry');
assert.ok(double.test('#8888'), 'two-entry list must match the second entry');
});
module.exports = { buildRefRe };