You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[finding] A liveness citation can rot IN RANGE: action.target and action.requiredPermissions point at the wrong lines one day after being repointed, and every check stays green #12516
Origin: surfaced by the os-dev run for #12215 (repointing the type/body/method anchors in packages/spec/liveness/action.json) while spot-checking the sibling entries that PR #12214 repointed the day before. Filed unassigned, recording only — awaiting triage.
The gap
Three checks now bound a live entry's evidence citation, and each closed the previous one's blind spot:
A fourth case satisfies all three and is still wrong: the cited line is in range, the file does name the key, and the consumer has simply moved to a different line of the same file. #11210's own guidance names this ("a consumer that moved WITHIN the file it is cited to leaves every line in range"), and #11457 answered the no-line-at-all form of it — but a citation that carries a specific, now-incorrect line is caught by neither.
Measured, on origin/main @ 7b95536
Both entries below were repointed by PR #12214 on 2026-08-25 with fresh line numbers, and both had drifted by 2026-08-26:
entry
cited
what is actually there now
the real consumer
action.target
packages/runtime/src/action-execution.ts:725 — "type:'flow' server dispatch — automation.execute(action.target, …)", with :718 "rejecting an unknown flow name by that same value"
:716-726 is the FlowActionRefusal brand assignment and the recognition-predicate docblock — no automation.execute, no flow-name rejection
automation.execute(action.target, { is at :809, inside dispatchFlowAction (declared :780)
action.requiredPermissions
packages/runtime/src/domains/actions.ts:172-180 — "ADR-0066 D4 dual-surface gate — resolves the action's declared requiredPermissions and rejects 403"
:172-180 is path-part parsing (parts[1]/parts[2]) and the invalidBody validation-failure constructor
the requiredPermissions reads are at :393-400, :473 and :526
pnpm --filter @objectstack/spec check:liveness is green on both, and says so explicitly: every 'path:NNN' citation names a line that file actually has and every cited file names the property it is evidence for. It is telling the exact truth; the pointers are still wrong.
Note the shape: the more precisely an entry is cited, the faster it rots. action-execution.ts is 1670 lines and actively edited, so a citation pinned to a line there has a short half-life — the two repaired entries drifted within roughly one day of being written, which is a stronger drift rate than the rot #12214 was repairing.
Why this is worth a card rather than a shrug
The ledger's whole contract is that a live verdict is its evidence pointer. A pointer that resolves to real code which is not the consumer is worse than a missing one: it reads as verified, and the next reader who spot-checks it finds plausible code at the address and moves on. That is the same failure the ledger README spends its length warning about, one level in.
Possible directions (not adjudicated — for triage)
A symbol anchor instead of / beside the line. Cite dispatchFlowAction and let the check locate it, so the pointer survives movement within the file. Costs a symbol-resolution step; kills the whole class.
A drift warning keyed on line churn: flag a verifiedAt citation whose cited file has been modified since that date. Cheap, no parsing, but noisy on hot files.
Accept it and shorten the clock for entries citing hot files — the weakest option, since nothing measures "hot".
Explicitly do nothing: rule that a line is a convenience and the file+key anchoring is the contract. Legitimate, but then the line numbers should probably stop being written as if they were load-bearing.
Scope note
Deliberately NOT folded into the #12215 PR: that card's accept set is the type/body/method entries, and these two are different entries whose citations came from an already-merged PR. #12215's own repoint writes line numbers too, so it inherits this same exposure and is not a fix for it.
Dedup (searched 2026-08-26)
#11210 (closed, built the line bound) and #11457 (closed, built the key-mention check) each name this residual in passing but neither addresses it; #7133 / #7142 (closed) are cross-repo citation repair; #12462 (open) is about the shrink-only baseline being pinned non-empty, not about citation accuracy. No open card covers in-range line drift.
⚠️ Dedup channel deviation: the REST list endpoint returns 403 from the dev seat this was filed from (verified: GET /repos/objectstack-ai/objectstack/issues → HTTP 403), so the REST-first dedup rule could not be followed; this search used the MCP semantic search plus a local grep instead.
Origin: surfaced by the os-dev run for #12215 (repointing the
type/body/methodanchors inpackages/spec/liveness/action.json) while spot-checking the sibling entries that PR #12214 repointed the day before. Filed unassigned, recording only — awaiting triage.The gap
Three checks now bound a
liveentry's evidence citation, and each closed the previous one's blind spot:check:liveness的 stale-evidence 检测只 ⚠ 不判红 —— ADR-0087 证据路径可以静默腐烂,且摘要行的 “N resolved” 数的是路径总数不是解析成功数 #5623)A fourth case satisfies all three and is still wrong: the cited line is in range, the file does name the key, and the consumer has simply moved to a different line of the same file. #11210's own guidance names this ("a consumer that moved WITHIN the file it is cited to leaves every line in range"), and #11457 answered the no-line-at-all form of it — but a citation that carries a specific, now-incorrect line is caught by neither.
Measured, on
origin/main@7b95536Both entries below were repointed by PR #12214 on 2026-08-25 with fresh line numbers, and both had drifted by 2026-08-26:
action.targetpackages/runtime/src/action-execution.ts:725— "type:'flow' server dispatch —automation.execute(action.target, …)", with:718"rejecting an unknown flow name by that same value":716-726is theFlowActionRefusalbrand assignment and the recognition-predicate docblock — noautomation.execute, no flow-name rejectionautomation.execute(action.target, {is at :809, insidedispatchFlowAction(declared :780)action.requiredPermissionspackages/runtime/src/domains/actions.ts:172-180— "ADR-0066 D4 dual-surface gate — resolves the action's declared requiredPermissions and rejects 403":172-180is path-part parsing (parts[1]/parts[2]) and theinvalidBodyvalidation-failure constructorrequiredPermissionsreads are at :393-400, :473 and :526pnpm --filter @objectstack/spec check:livenessis green on both, and says so explicitly:every 'path:NNN' citation names a line that file actually has and every cited file names the property it is evidence for. It is telling the exact truth; the pointers are still wrong.Note the shape: the more precisely an entry is cited, the faster it rots.
action-execution.tsis 1670 lines and actively edited, so a citation pinned to a line there has a short half-life — the two repaired entries drifted within roughly one day of being written, which is a stronger drift rate than the rot #12214 was repairing.Why this is worth a card rather than a shrug
The ledger's whole contract is that a
liveverdict is its evidence pointer. A pointer that resolves to real code which is not the consumer is worse than a missing one: it reads as verified, and the next reader who spot-checks it finds plausible code at the address and moves on. That is the same failure the ledger README spends its length warning about, one level in.Possible directions (not adjudicated — for triage)
dispatchFlowActionand let the check locate it, so the pointer survives movement within the file. Costs a symbol-resolution step; kills the whole class.verifiedAtcitation whose cited file has been modified since that date. Cheap, no parsing, but noisy on hot files.Scope note
Deliberately NOT folded into the #12215 PR: that card's accept set is the
type/body/methodentries, and these two are different entries whose citations came from an already-merged PR. #12215's own repoint writes line numbers too, so it inherits this same exposure and is not a fix for it.Dedup (searched 2026-08-26)
#11210 (closed, built the line bound) and #11457 (closed, built the key-mention check) each name this residual in passing but neither addresses it; #7133 / #7142 (closed) are cross-repo citation repair; #12462 (open) is about the shrink-only baseline being pinned non-empty, not about citation accuracy. No open card covers in-range line drift.
GET /repos/objectstack-ai/objectstack/issues→HTTP 403), so the REST-first dedup rule could not be followed; this search used the MCP semantic search plus a local grep instead.Generated by Claude Code