Uh oh!
There was an error while loading. Please reload this page.
feat(spec): make a liveness citation name the property it is evidence for - #12214
Merged
Conversation
… for The existence check (#5623) and the line bound (#11210) both bound a citation from the outside. Between them a consumer can move WITHIN the file it is cited to, leaving the file present and every named line in range while the pointer is wrong. Measured before switching anything on: 403 (entry, cited local file) pairs, 11 where the cited file never names the key, 7 of them real rot — all repaired here. The remaining 4 are the mandated camelCase -> snake_case convention; three are folded structurally by the matcher and the one compound child-key remap is a single explicit row in a shrink-only baseline that fails in both directions. Asked of `evidence` only, never `producer`: a producer cites who supplies a second input (#4837), a call site that need not name the key. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NDGG54XF5gbTLdQzCtnaVV
…three checks share Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NDGG54XF5gbTLdQzCtnaVV
Contributor
📓 Docs Drift Check
What this run could not see
Coarse fallback — 126 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
This was referenced Aug 25, 2026
os-litant
marked this pull request as ready for review
August 25, 2026 14:17
Uh oh!
There was an error while loading. Please reload this page.
This was referenced Aug 26, 2026
os-trump pushed a commit
that referenced
this pull request
Aug 28, 2026
…#symbol survives in-range line drift A line citation rots IN RANGE: the consumer moves within its cited file, the file exists, the line is inside it, the file names the key — all three citation checks stay green and the pointer is wrong. Measured on the two action.json entries PR #12214 repointed 2026-08-25: both drifted within a day. A path#symbol anchor moves WITH the consumer; when the consumer is renamed or deleted the gate goes red — a direction a stale line cannot produce. Census before switch-on: zero pre-existing path#symbol tokens, so the check starts green (the orphan-proof zero-census precedent), and the line-window detector alternative was censused at 117-173/298 misses — unshippable noise (the 48-of-227 lesson). Both rotted entries re-anchored. Part of #12516 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes#11457
The existence check (#5623) bounds a citation's file; the line bound (PR #11449, #11210) bounds its line. Both bound it from the outside, and between them sits a case neither can see: a consumer that moves within the file it is cited to, or a citation written with no line at all. The file is present, every named line is in range, and the pointer is wrong.
This implements the second signal #11210 designed and left to the implementer — the key-mention check — with the exception classes the census measured rather than a
\bkey\bgrep.The census, re-measured on today's tree
The issue's census is dated 2026-08-23. Re-run against
origin/main@22c42c9with the PR #11449 parser (scanEvidence().localCitations+scan.local):The three that left are the
permission.tabPermissionspointers PR #11449 repaired, exactly as the issue predicted. The other 11 reproduce identically.One of the issue's classifications does not survive the re-measure. It listed
object.tenancy.organizationField→audit-writers.tsamong the naming-convention false positives ("reads it asorganization_*"). It is real rot:resolveRecordOrganizationFieldwas promoted into@objectstack/metadata-coreby #10101, andaudit-writers.ts:220says so itself in the re-export comment it left behind. So the split is 7 real / 4 convention, not 6/5.The seven repairs
permission.objects.allowExporthono-plugin.tscurrent-user-endpoints.ts:493-502annotateEffectiveApiOperationsmoved — the same movement that rottedsystemPermissionsandtabPermissionsobject.tenancy.organizationFieldaudit-writers.tsmetadata-core/src/record-organization.ts:177-180action.targethttp-dispatcher.tsaction-execution.ts:725,:472http-dispatcher.tsretains 0 occurrencesaction.requiredPermissionshttp-dispatcher.tsdomains/actions.ts:172-180action.bodyShapehttp-dispatcher.tsobjectui @a76b18cf2useConsoleActionRuntime.tsx:359-360type:'api'actions are client-dispatchedaction.bodyExtrahttp-dispatcher.tsobjectui @a76b18cf2useConsoleActionRuntime.tsx:397-398,:474-475field.requiredWhenrecord-validator.tsrule-validator.ts:1792-1810,:762-763record-validator.tsenforces the staticrequiredcontract, the CEL predicate lives one file overTwo of these say something worth keeping.
bodyShape/bodyExtrawere never readable in this repo at all —action-execution.ts:468-477refusestype:'api'server-side with "it dispatches ontarget, not through the action registry" — so the in-repo citation was never right, rather than having rotted. Andfield.requiredWhenis the plausible-neighbour shape: both files exist, neither citation carried a line, so no check the gate had could reach it.Why the matcher is not a grep
The remaining 4 are the convention Prime Directive #3 mandates — TS keys
camelCase, machine namessnake_case— so a property persisted as a column is read asbody_html/managed_byand never as the authoring key. Three are handled structurally: the matcher folds the key across the naming convention, in both directions. They are not exemptions and there is nothing to maintain for them.The match is word-bounded, which is load-bearing rather than tidy: an unbounded match lets
requiredsatisfyrequiredWhen, and that is precisely the citation that stayed hidden — its cited file mentionsrequiredon twenty lines.The single residual is a compound child-key remap:
fromOverride.address→from_address,fromOverride.name→from_name. No fold of the parent key reaches it, and widening the matcher to a prefix would buy this one exemption at the cost of blinding the check to every future pointer landing in a file merely adjacent to the reader — therequiredWhenshape again. It is one explicit row inscripts/liveness/key-mention.baseline.json, and itswhynames the spelling the file actually uses.That baseline is shrink-only in both directions, like
undrilled-containers.baseline.jsonbeside it: a row whose pair later anchors fails, so the debt cannot be overstated for free.The check asks
evidenceonly, neverproducer. A producer cites who supplies a second input (#4837) — by definition a call site that need not name the key (seed.env's producer points atresolveEnvConfig). Demanding the key there would report that field's whole population as rot the day it was switched on.On the ledger-format fork
The triage said: if a clean design needs a ledger-format addition, stop and report rather than invent one inline. No ledger-format addition was needed — authors'
liveness/*.jsonis untouched as a format, the one exemption is gate-side, and the precedent for that isundrilled-containers.baseline.jsonin the same directory. The residual design question (whether that exemption would eventually read better as an entry-level anchor key) is recorded in the report rather than decided here.Verification
check:livenessbefore/after, both at the same tree except for this branch:22c42c9, a clean comparison worktree): exit 0, green — 403 local paths, all resolved; 293 line citations, all in range. It was green while 11 citations were unanchored and 7 of those were real rot.938512b): exit 0 —key-mention anchoring: 402 (entry, cited file) pair(s) asked, 401 anchored, 1 exempt.Reverse verification, two legs, each mutation proven on disk before the reading and restored by an
EXIT INT TERMtrap (no build/distinvolved —tsxruns the.mtsgate from source and reads the ledger JSON at runtime):field.requiredWhento its rotted citation → gate exit 1,1 UNANCHORED1 stale key-mention exemption(s)Gates, all at final HEAD
938512band read from each gate's own verdict line:check:liveness,check:empty-state,check:strictness-ledger,pnpm --filter @objectstack/spec typecheck(incl.check:scripts-typecheck+check:test-typecheck), the liveness suite (235/235, 22 of them new),pnpm lintwhole repo (no narrowing),check:published-files,check:engine-double-contract,check:where-matcher,check:cross-package-test-inputs,check:query-options-erasure,check:nul-bytes, and the changeset family. Gate list re-derived from the actual diff withscripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack.One pre-existing self-test assertion needed repair: it pinned "not reported as a missing file" by the bare
entry → pathline, which is not unique to one check. It now pins the missing-file heading — the claim actually being made.Generated by Claude Code