Skip to content

[finding] check:liveness validates an evidence citation's FILE but never its line, so a moved consumer rots silently — permission.tabPermissions cites hono-plugin.ts:1200 in a 718-line file that no longer mentions the key #11210

Description

@os-sam

Found while executing #10959 (the proof→ledger binding adoption, PR #11209). That card
required re-verifying one entry's note; doing so exposed a gate gap that the entry's
staleness was only a symptom of. Filed unassigned, not fixed in that PR — repairing a
second entry's evidence is outside its declared file surface, and picking the correct
replacement citation is its own measurement, not a mechanical edit.

The gap

check:liveness resolves an evidence pointer with existsSync on the file path
only
packages/spec/scripts/liveness/check-liveness.mts:442:

constev=checkEvidence(led.evidence,(p)=>existsSync(join(repoRoot,p)));

Two things it therefore never checks, for a citation written as path/to/file.ts:NNN:

  1. whether line NNN is inside the file at all, and
  2. whether the cited file mentions the property it is offered as evidence for.

So when a consumer moves out of a file that still exists, the citation keeps passing.
The summary line reports it under "resolved" and the entry reads as freshly verified. This
is the residue left by #5623 (which made a missing file red) and by the #7133/#7142
citation-repair sweep (which fixed objectui readers): the in-repo, file-still-exists,
line-and-content-are-wrong case has no gate.

Measured extent — small, which is the useful part

Walked every live entry in packages/spec/liveness/*.json, extracted each in-repo
evidence citation carrying a line number, and compared the line against the file's length:

So this is a two-instance class, not a sweep — but both instances share one cause, and
neither was catchable by any gate.

The standing instance

permission.tabPermissions (packages/spec/liveness/permission.json), a bound
high-risk entry carrying the me-apps-and-everyone-baseline proof:

"evidence": "packages/plugins/plugin-hono-server/src/hono-plugin.ts:1200 (tabRank merges
all four values — hidden/default_off/default_on/visible — across resolved
sets, most-visible wins; app hidden ⇒ dropped from /me/apps) + ..."
  • hono-plugin.ts is 718 lines — there is no line 1200.
  • grep -c tabPermissions on that file returns 0. The described tabRank merge is not
    there at all.
  • The /me/apps machinery moved to
    packages/plugins/plugin-hono-server/src/current-user-endpoints.ts, whose filters are
    documented at ~line 897 as AppSchema.requiredPermissions ⊆ ctx.systemPermissions and
    ctx.tabPermissions[app.name] !== 'hidden'.

The verdict is not in question — the entry is live, it carries a real dogfood proof,
and the behaviour still ships. What is dead is the citation, and the note's confident
"Re-verified 2026-07-30" makes it read as measured.

This is the same file and the same code movement that made
permission.systemPermissions' pointer (hono-plugin.ts:1222) dead; that one is corrected
in PR #11209 because that entry was in #10959's scope. This one was not.

Why it is worth a gate, not just a repair

A dead line-citation is worse than a missing one: it survives review because it looks
precise, and the next agent re-verifying the entry follows it, finds nothing, and has to
reconstruct where the consumer went — which is exactly the cost #10959 paid. The check is
cheap and mechanical for the path:NNN form:

  • fail when NNN exceeds the file's line count (unambiguous, zero judgment, catches both
    known instances);
  • optionally warn when the cited file contains no occurrence of the property's own key
    (catches the move-within-bounds case that a line check cannot, at the cost of some false
    positives where the consumer reads a renamed local).

Ratcheting it shrink-only over the current 252 citations would let it land without a
repair sweep blocking it.

Suggested shape

  1. Extend checkEvidence so a path:NNN citation also asserts NNN is within the file.
  2. Repair permission.tabPermissions' evidence to the real consumer in
    current-user-endpoints.ts, measured rather than assumed, and stamp verifiedAt — the
    gate's own prescribed remedy for a moved consumer ("the consumer MOVED inside this repo
    → repoint the path, and stamp verifiedAt", check-liveness.mts:728).

Back-links: #10959, PR #11209, #5623 (made a missing evidence FILE red), #7133 / #7142
(the objectui citation-repair bundle).

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions