Filed from the re-census done for #12802. That card corrected the anchors; it deliberately did not build the enforcement, because a new CI gate needs .github/workflows/lint.yml and the root package.json, both held by open PR #12942. This card is the enforcement half, with the measurement that should size it.
What was measured
content/docs/permissions/system-context.mdx declares itself "the authority" and "built by census over the whole repo, not by recall". Its census was written in ece8730 (2026-08-09). Re-measured against origin/main at c94be6291 (2026-08-28), 19 days later:
Anchor reconciliation — all 111 anchors of the previous edition (not 64: a grep -c counts lines carrying an anchor; continuation anchors like the backticked :1409 form and range ends bring the real population to 111):
| Bucket | Count |
|---|
| matched-and-correct — line still names the construct | 10 |
| matched-and-moved — construct exists, line number rotted | 101 |
| unmatched-on-page — anchor's construct gone from the code | 0 |
| unmatched-in-code — read site in the code with no row on the page | 32 |
Site population, independently measured at both commits (TypeScript AST, not grep — a regex pass silently lost 6 real sites in report-service.ts to a quoting desync):
| at ece8730 | at c94be6291 |
|---|
reads of ExecutionContext.isSystem in non-test sources | 83 | 109 |
| packages containing at least one | 18 | 20 |
Split of the 83 sites that existed when the census was written: 81 survived (75 of them at a different line), 2 were deleted, and 28 arrived.
Why this matters for the anchor shape
The two deletions are the load-bearing part of this card. They are:
packages/services/service-storage/src/attachment-access-hooks.ts:79 — the callerContext() helper's isSystem: exec.isSystem propagationpackages/plugins/plugin-audit/src/comment-access-hooks.ts:192 — the same helper, same shape
Both helpers still exist under the same names. Only the isSystem read inside them is gone. A symbol-name anchor pointed at callerContext would still resolve, and would still be green — so the "cheaper shape" from #12802's own fork does not, on this page, catch the failure that matters most: a site that vanished from the code while its row survives on the page. This page's entire value is that it enumerates all elevated write sites for someone deciding whether elevation is safe; a row describing a protection that no longer exists is worse than a rotted line number, because a rotted line number is visibly wrong the moment a reader follows it.
(Both deleted sites happened to be propagation-only, and neither was on the page. That is luck, not a property of the shape.)
What the triage grading on #12802 ruled, and what the measurement adds
The grading comment ruled that the two shapes compose: symbol-name anchors to stop the rot, then a gate that resolves each symbol and fails when it is absent, and explicitly said not to ship the first alone without filing the second with a reason. This is that filing.
The measurement adds one thing to that ruling: the gate has to check the population, not only the anchors. Resolving every anchor on the page would have passed at c94be6291 under a symbol scheme, while the page was 32 sites short and carried a headline count that was 29 too low. A gate that only resolves what the page already says can never find what the page omits.
Suggested shape, for the implementer to price
- Census script, committed rather than throwaway: enumerate
ExecutionContext.isSystem reads by AST. Regexes are not adequate here and this is measured, not asserted — see above. - Gate A — resolution: every anchor on the page resolves to a real file and a real line, and to a line the census also identifies as a read site (or to an explicitly allow-listed non-read anchor, of which the corrected page has 11).
- Gate B — population: the census's site count, package count and per-section counts match the numbers the page states. This is the half that catches deletions and arrivals, and it is cheap once step 1 exists.
- Anchor spelling: the corrected page already spells an ambiguous basename far enough to be unique (
objectql/src/engine.ts, not engine.ts). 41 of the previous edition's 111 anchors named a basename matching two files and could not be resolved without reading the row's Package column — worth keeping mechanical.
A working prototype of Gate A already exists as a throwaway from #12802's PR and is quoted in that PR body; it parses the page's inline-code spans (including continuation and range-end anchors), resolves each against git ls-files, and exits non-zero on any failure. It reported resolved 146 failures 0 on the corrected page and resolved 70 failures 41 on the previous edition.
Not prescribing where the gate is wired
lint.yml and the root package.json were held by #12942 when this was filed. If a cheaper host exists (folding the check into an existing docs gate rather than adding a check:* entry), that is likely better than a new farm member for one page — but note the mechanism generalises: any docs page carrying file:line anchors has this defect, and there are others.
Filed from the re-census done for #12802. That card corrected the anchors; it deliberately did not build the enforcement, because a new CI gate needs
.github/workflows/lint.ymland the rootpackage.json, both held by open PR #12942. This card is the enforcement half, with the measurement that should size it.What was measured
content/docs/permissions/system-context.mdxdeclares itself "the authority" and "built by census over the whole repo, not by recall". Its census was written inece8730(2026-08-09). Re-measured againstorigin/mainatc94be6291(2026-08-28), 19 days later:Anchor reconciliation — all 111 anchors of the previous edition (not 64: a
grep -ccounts lines carrying an anchor; continuation anchors like the backticked:1409form and range ends bring the real population to 111):Site population, independently measured at both commits (TypeScript AST, not grep — a regex pass silently lost 6 real sites in
report-service.tsto a quoting desync):ece8730c94be6291ExecutionContext.isSystemin non-test sourcesSplit of the 83 sites that existed when the census was written: 81 survived (75 of them at a different line), 2 were deleted, and 28 arrived.
Why this matters for the anchor shape
The two deletions are the load-bearing part of this card. They are:
packages/services/service-storage/src/attachment-access-hooks.ts:79— thecallerContext()helper'sisSystem: exec.isSystempropagationpackages/plugins/plugin-audit/src/comment-access-hooks.ts:192— the same helper, same shapeBoth helpers still exist under the same names. Only the
isSystemread inside them is gone. A symbol-name anchor pointed atcallerContextwould still resolve, and would still be green — so the "cheaper shape" from #12802's own fork does not, on this page, catch the failure that matters most: a site that vanished from the code while its row survives on the page. This page's entire value is that it enumerates all elevated write sites for someone deciding whether elevation is safe; a row describing a protection that no longer exists is worse than a rotted line number, because a rotted line number is visibly wrong the moment a reader follows it.(Both deleted sites happened to be propagation-only, and neither was on the page. That is luck, not a property of the shape.)
What the triage grading on #12802 ruled, and what the measurement adds
The grading comment ruled that the two shapes compose: symbol-name anchors to stop the rot, then a gate that resolves each symbol and fails when it is absent, and explicitly said not to ship the first alone without filing the second with a reason. This is that filing.
The measurement adds one thing to that ruling: the gate has to check the population, not only the anchors. Resolving every anchor on the page would have passed at
c94be6291under a symbol scheme, while the page was 32 sites short and carried a headline count that was 29 too low. A gate that only resolves what the page already says can never find what the page omits.Suggested shape, for the implementer to price
ExecutionContext.isSystemreads by AST. Regexes are not adequate here and this is measured, not asserted — see above.objectql/src/engine.ts, notengine.ts). 41 of the previous edition's 111 anchors named a basename matching two files and could not be resolved without reading the row's Package column — worth keeping mechanical.A working prototype of Gate A already exists as a throwaway from #12802's PR and is quoted in that PR body; it parses the page's inline-code spans (including continuation and range-end anchors), resolves each against
git ls-files, and exits non-zero on any failure. It reportedresolved 146 failures 0on the corrected page andresolved 70 failures 41on the previous edition.Not prescribing where the gate is wired
lint.ymland the rootpackage.jsonwere held by #12942 when this was filed. If a cheaper host exists (folding the check into an existing docs gate rather than adding acheck:*entry), that is likely better than a new farm member for one page — but note the mechanism generalises: any docs page carryingfile:lineanchors has this defect, and there are others.