Proposal (rationale)
Promoted from F-005. linkIntegrity abandons a file on its first {{token}}
(src/engines.ts:155):
if (/\{\{[a-z_.]+\}\}/.test(text)) continue;
One token anywhere in a document exempts every link in it from both gates-links-resolve and
gates-paths-exist. The documents most likely to discuss file paths are the ones explaining
templating, so the exemption lands precisely where link checking is most needed.
Measured 2026-08-15: 16 non-excluded files are currently exempt, including
modules/README.md,
ADR-0003,
docs/design/parameters.md, and eight skills and rules that ship into
consumer repos. The gate reports 72 examined and green; with the exemption removed it examines
88.
The exemption is invisible. A skipped file and a clean file produce identical output, so the gate reads as covering a corpus it is not.
How it was found is the argument for fixing it. Two items in the same directory carried
identically-shaped broken links. WI-007’s was caught; WI-006’s was not, because WI-006’s item
discusses {{repo.dirname}}. It went further than that: editing WI-007’s item to describe this
bug introduced the word {{token}} into it, which switched off its own link checking and hid a
second broken link that had been there all along. The bug concealed itself while being written up.
The heuristic is probably unnecessary. Its own comment says it exists because
modules/*/fragments/AGENTS.md was reported for {{path}}/README.md — but link_integrity.exclude
already excludes modules/*/fragments/** and modules/*/files/** by path. The case that
motivated it is covered twice.
Decision
accepted — 2026-08-15. Severity high in the register, and it ships in the gates module, so every
scaffolded repo currently believes its links are checked.
Plan
Requirements
- A
{{token}}suppresses only the link that contains it, never its neighbours or its file. - A link written inside an inline code span is prose quoting a link, and is not checked.
gates-links-resolveandgates-paths-existexamine every non-excluded markdown file.- The two links currently hidden in the corpus are either fixed or shown to be false positives.
rungs check→ 20 pass, 0 fail, withexaminedup from 72 to 88.
Impacts
src/engines.tslinkIntegrity— the file-level skip and the link loop.- Every scaffolded repo, on upgrade. This makes a shipped gate stricter, so a consumer repo that was quietly passing may start failing — correctly, and that is the point.
modules/gates/gates/structural.toml— a self-test for the newly covered case.- No ADR. Criterion 5: the code states the rule more precisely than prose could.
Approach
Move the token test from the file to the link, and strip inline code spans before scanning.
Both are needed together. Removing the file skip alone makes the gate fire on
docs/backlog/items/WI-007-…, which quotes a broken link inside backticks as an example — a
false positive, and F-001’s lesson is that a gate crying wolf on the happy path is one people learn
to ignore. A code span is a quotation, not a link.
Rejected: path-ok: on the affected files. That exemption is file-level too, so it would trade
one blanket exemption for another, and it is meant for a deliberate stale path with a stated reason
— not for the checker mis-reading a code span.
Rejected: deleting the token test entirely. The redundancy argument is strong but not proven for every file, and a per-link skip costs one line while removing the need to be sure.
Acceptance criteria / tests
examinedrises from 72 to 88 on this repo for both link gates.- A broken link in a file containing
{{token}}is caught — verified by re-breaking the exact WI-006 link that F-005 was filed on. - A link inside backticks is not reported, so WI-007’s quoted example stays clean.
- A link whose own text contains
{{…}}is not reported. rungs check→ 20 pass, 0 fail; the site’s independentcheck:linksstill reports 0 broken.structural.tomlgains a self-test for the token case.
Out of scope
- Self-tests are declared but never executed.
gateMetachecks only that a[[self_test]]block exists for each direction; nothing runs theinputfixtures. So criterion 6 adds a declaration, not an assertion — recorded as its own finding rather than fixed here, because building a fixture runner is a different piece of work. backticked_pathsis listed in the table’scheckarray and is not implemented.gates-paths-existruns the same markdown-link scan asgates-links-resolve. A separate finding.- Fixing links this newly surfaces in consumer repos. Not ours to fix.
Execution
Branch feature/WI-008-link-gate-checks-every-file, cut from main 2026-08-15.
src/engines.tslinkIntegrity— the token test moved from the file to the individual link, and inline code spans are blanked before scanning. Blanked with spaces rather than removed, so every offset after a code span is unchanged and the reported link text still matches what the author sees.modules/gates/gates/structural.toml— two self-tests fixing the boundary: a broken link is caught with a token elsewhere in the file, and a link that is itself a template is not.
The corpus contained exactly one hidden defect, and it was not a broken path. FINDINGS.md
quoted gates-links-resolve’s own fixture — See [the plan](./does-not-exist.md). — in italics,
which is a real markdown link that does not resolve. Fixed by moving the quotation into a code span,
which is the correct authoring, rather than by widening the stripper to emphasis: a link inside
emphasis is an ordinary link and should stay checked.
Deviation from the plan, in the numbers. The Proposal predicted examined would go 72 → 88.
It is 89, because WI-008’s own item file was added to the corpus between measuring and fixing.
Recorded rather than amended: the prediction was made against a corpus that no longer exists, and
89 is what the check reports today.
F-001 recurred, fifth occurrence.
Review
Checked 2026-08-15.
- Pass, with the count corrected. Both link gates examine 89 files, up from 72 — 16 files were exempt, plus this item’s own file. See the deviation above for why not 88.
- Pass, and this is the criterion that matters. Re-breaking the exact link F-005 was filed on —
](../design/parameters.md)in WI-006’s item, a file dense with{{tokens}}— now fails both gates by name. It reported green before this change. - Pass. WI-007’s item quotes a broken link inside backticks as an example and is not reported.
- Pass. A probe link of
[readme]({{path}}/README.md)beside a resolvable one was not reported; the probe was reverted withgit checkout, verified by diff. - Pass.
rungs check→ 20 pass, 0 fail once the branch carried a commit. The site’s independent checker → 44 routes, 491 links, 0 broken. - Pass as a declaration, not as an assertion. Two self-tests added and
gates-self-tests-both-directionsis green over 21 gates — but F-006 records that nothing executes a fixture, so these assert nothing until that lands. Stated plainly here because a self-test that reads as verification and is inert is the same failure this item is about.