Found while executing #11210 (PR #11449), which bounds a path:NNN evidence citation by
the cited file's line count. That bound closes the past-EOF case. It structurally cannot
see the complementary case: a consumer that moves within the file it is cited to,
or a citation written with no line at all. The line still exists, the file still exists,
and nothing fires.
#11210 offered a second signal for exactly this — warn when the cited file contains no
occurrence of the property's own key — and left the call to the implementer on
false-positive cost. I measured it instead of guessing, and the measurement says: the
signal is real, and it is not a one-line addition. Filing the census rather than
bolting a permanently-noisy warning onto a gate tightening.
The census (post-#11209 ledgers, the gate's own parser)
Walked every live entry, took each resolvable repo-local cited file, and asked whether
that file mentions the entry's own leaf key as a word:
- 403 (entry, cited local file) pairs checked
- 14 where the cited file never mentions the key — 3.5%
Why a naive matcher is wrong — the false positives are STRUCTURAL
At least 5 of the 14 are generated by a rule the platform mandates everywhere. AGENTS.md
Prime Directive #3: TS config keys are camelCase, machine names are snake_case. So for
every property persisted as a column, the consumer file names the snake_case form and
never the authoring key:
| entry | cited file | reads it as |
|---|
email_template.bodyHtml | plugin-email/src/email-service.ts | body_html |
email_template.bodyText | same | body_text |
email_template.fromOverride | same | from_* |
permission.managedBy | plugin-security/src/bootstrap-declared-permissions.ts | sys_permission_set.managed_by |
object.tenancy.organizationField | plugin-audit/src/audit-writers.ts | organization_* |
The bodyHtml entry's own evidence string even writes the mapping out —
bootstrap-declared-email-templates.ts:83 (bodyHtml -> body_html). A \bkey\b match on the
authoring key is guaranteed to miss the consumer for this entire class, so the warn needs a
real design (case-folding across the naming convention, destructured/renamed locals,
probably an opt-out key on the entry), not a grep.
The true positives — these are real rot, and one is confirmed
permission.objects.allowExport — cites
packages/plugins/plugin-hono-server/src/hono-plugin.ts (annotateEffectiveApiOperations, the /me/permissions projection the frontend renders). Measured: annotateEffectiveApiOperations
has 0 occurrences in hono-plugin.ts and now lives in
packages/plugins/plugin-hono-server/src/current-user-endpoints.ts. This is the same
code movement that rotted permission.systemPermissions (repaired in #11209) and
permission.tabPermissions (repaired in PR #11449) — and it is invisible to the line bound
because that citation carries no line at all. The other two pointers in the entry
(rest-server.ts enforceExportPermission, security-plugin.ts canExport) were not
re-measured here.
action.target / action.requiredPermissions / action.bodyShape / action.bodyExtra
— all four cite packages/runtime/src/http-dispatcher.ts. That file mentions action 48
times but the string target0 times in 2188 lines. Either the four keys are read
through renamed locals (false positives) or the consumer moved (a 4-entry rot cluster).
Unclassified — it needs a call-graph closure, which is the work, not a mechanical edit.
field.requiredWhen → objectql/src/validation/record-validator.ts (0 occurrences of
requiredWhen, 20 lines mentioning required) — same unclassified shape.
The remaining 3 of the 14 are the permission.tabPermissions pointers already repaired in
PR #11449.
Suggested shape
- Re-measure each of the 14 and repair or reclassify (the
allowExport one is already
measured above and is a repoint). - Only then decide the warn's design, with the repaired set as its baseline — the
census above is what makes a shrink-only ratchet possible without shipping a warning
list that is non-empty on day one. evidence.mts's header records why that matters: the
48-of-227 era produced a warning nobody read, and the one genuine rot inside it sat
unnoticed.
Filed unassigned. Reproduce with the parser PR #11449 adds — scanEvidence().localCitations
plus scan.local — over packages/spec/liveness/*.json.
Back-links: #11210, PR #11449, #11209 / #10959 (the sibling repair), #7133 / #7142 (the
objectui citation-repair bundle), #5623 (made a missing evidence FILE red).
Generated by Claude Code
Found while executing #11210 (PR #11449), which bounds a
path:NNNevidence citation bythe cited file's line count. That bound closes the past-EOF case. It structurally cannot
see the complementary case: a consumer that moves within the file it is cited to,
or a citation written with no line at all. The line still exists, the file still exists,
and nothing fires.
#11210 offered a second signal for exactly this — warn when the cited file contains no
occurrence of the property's own key — and left the call to the implementer on
false-positive cost. I measured it instead of guessing, and the measurement says: the
signal is real, and it is not a one-line addition. Filing the census rather than
bolting a permanently-noisy warning onto a gate tightening.
The census (post-#11209 ledgers, the gate's own parser)
Walked every
liveentry, took each resolvable repo-local cited file, and asked whetherthat file mentions the entry's own leaf key as a word:
Why a naive matcher is wrong — the false positives are STRUCTURAL
At least 5 of the 14 are generated by a rule the platform mandates everywhere. AGENTS.md
Prime Directive #3: TS config keys are
camelCase, machine names aresnake_case. So forevery property persisted as a column, the consumer file names the snake_case form and
never the authoring key:
email_template.bodyHtmlplugin-email/src/email-service.tsbody_htmlemail_template.bodyTextbody_textemail_template.fromOverridefrom_*permission.managedByplugin-security/src/bootstrap-declared-permissions.tssys_permission_set.managed_byobject.tenancy.organizationFieldplugin-audit/src/audit-writers.tsorganization_*The
bodyHtmlentry's own evidence string even writes the mapping out —bootstrap-declared-email-templates.ts:83 (bodyHtml -> body_html). A\bkey\bmatch on theauthoring key is guaranteed to miss the consumer for this entire class, so the warn needs a
real design (case-folding across the naming convention, destructured/renamed locals,
probably an opt-out key on the entry), not a grep.
The true positives — these are real rot, and one is confirmed
permission.objects.allowExport— citespackages/plugins/plugin-hono-server/src/hono-plugin.ts (annotateEffectiveApiOperations, the /me/permissions projection the frontend renders). Measured:annotateEffectiveApiOperationshas 0 occurrences in
hono-plugin.tsand now lives inpackages/plugins/plugin-hono-server/src/current-user-endpoints.ts. This is the samecode movement that rotted
permission.systemPermissions(repaired in #11209) andpermission.tabPermissions(repaired in PR #11449) — and it is invisible to the line boundbecause that citation carries no line at all. The other two pointers in the entry
(
rest-server.tsenforceExportPermission,security-plugin.tscanExport) were notre-measured here.
action.target/action.requiredPermissions/action.bodyShape/action.bodyExtra— all four cite
packages/runtime/src/http-dispatcher.ts. That file mentionsaction48times but the string
target0 times in 2188 lines. Either the four keys are readthrough renamed locals (false positives) or the consumer moved (a 4-entry rot cluster).
Unclassified — it needs a call-graph closure, which is the work, not a mechanical edit.
field.requiredWhen→objectql/src/validation/record-validator.ts(0 occurrences ofrequiredWhen, 20 lines mentioningrequired) — same unclassified shape.The remaining 3 of the 14 are the
permission.tabPermissionspointers already repaired inPR #11449.
Suggested shape
allowExportone is alreadymeasured above and is a repoint).
census above is what makes a shrink-only ratchet possible without shipping a warning
list that is non-empty on day one.
evidence.mts's header records why that matters: the48-of-227 era produced a warning nobody read, and the one genuine rot inside it sat
unnoticed.
Filed unassigned. Reproduce with the parser PR #11449 adds —
scanEvidence().localCitationsplus
scan.local— overpackages/spec/liveness/*.json.Back-links: #11210, PR #11449, #11209 / #10959 (the sibling repair), #7133 / #7142 (the
objectui citation-repair bundle), #5623 (made a missing evidence FILE red).
Generated by Claude Code