Skip to content

[finding] A liveness citation can rot WITHIN its file — 14 measured candidates the new line bound structurally cannot see, incl. permission.objects.allowExport citing a symbol that moved repos-internally #11457

Description

@os-steve

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:

entrycited filereads it as
email_template.bodyHtmlplugin-email/src/email-service.tsbody_html
email_template.bodyTextsamebody_text
email_template.fromOverridesamefrom_*
permission.managedByplugin-security/src/bootstrap-declared-permissions.tssys_permission_set.managed_by
object.tenancy.organizationFieldplugin-audit/src/audit-writers.tsorganization_*

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.requiredWhenobjectql/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

  1. Re-measure each of the 14 and repair or reclassify (the allowExport one is already
    measured above and is a repoint).
  2. 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

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions