fix(spec): the 21 confirmed cloud citations in the agent/skill/action liveness ledgers carry a read date and a symbol anchor - #18313
Conversation
…action liveness ledgers Re-read every cited cloud consumer in a cloud checkout at cloud @cb8ee7ff60c097cc21a584fe9caf8ef4391cc0e8 and transcribe that reading into the ledgers: `verifiedAt: 2026-09-15`, `evidenceScope: "cross-repo"`, and a `#symbol` anchor on the consuming function in place of a package-root path or a drifted line number. - Three cited lines had already drifted onto unrelated prose while every mechanical check kept passing (`agent-runtime.ts:264`, `agent-access.ts:50`, `action-tools.ts:535`). A symbol moves with the consumer and goes red when the consumer is renamed or deleted, which is the rot a line citation can never report. - Five skill rows now anchor the framework half at `packages/mcp/src/skill-prompts.ts#projectSkillPrompt`. The `;` before that token ends the `cloud` realm's scope, so the anchor is repo-local and `checkEvidenceAnchors` resolves it on every run — the old parenthesised `(projectSkillPrompt)` was prose no check read. Cloud anchors are counted, never resolved, so their `verifiedAt` is what carries them. - The eight skill rows' `verifiedAt: 2026-08-06` is replaced rather than kept: it was stamped while the citation still named a path present in neither repository, so it was false confidence the 180-day staleness clock is structurally unable to see. - Two ledger assertions the reading falsified are repaired in the prose. `agent.role` does not reach the system prompt — `listAgents` projects it onto `AgentSummary` and `buildSystemMessages` never reads it. `agent.planning` is not read in `agent-runtime.ts` at all; its readers are `routes/agent-routes.ts`, `routes/assistant-routes.ts` and `eval/eval-runner.ts`. - `agent.tools` is deliberately left untouched — no stamp, no scope, no status change. The same reading found zero consumers in cloud while `AgentSchema` already declares the key `retiredKey(...)`, which is a liveness re-grade for triage rather than a stamping decision. `check:liveness` is green and its counters move exactly where the change predicts: 505 repo-local paths declared and resolved (unchanged), foreign 204 -> 207, symbol anchors 578 -> 583 all resolving, dated entries 516 -> 529. Claude-Session: https://claude.ai/code/session_01TAUTP6Yky8QWoHUAPDKNJQ Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift Check
What this run could not see
Coarse fallback — 136 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
…oud citations at their enclosing symbols `planning` and `active` each cited a cloud file with no `#symbol` suffix, so those four pointers still named a whole file. Read at cloud @cb8ee7ff60c097cc21a584fe9caf8ef4391cc0e8: the `agent.planning?.maxIterations` reads sit inside `buildAgentRoutes`, `buildAssistantRoutes` and `EvalRunner`, and the 403 branch `active` gates sits in `buildAgentRoutes` as well. Every one of these anchors is FOREIGN — it follows a `cloud @cb8ee7ff:` marker, and `scanEvidence` collects no anchor while a foreign realm is in force — so CI cannot resolve them and they rest on that dated reading alone, exactly like the other cloud anchors in this ledger. `check:liveness` exit 0 with every counter byte-identical to the run before this commit, which is the expected result: a foreign anchor is stripped from the path token before classification, so adding one must move nothing. A counter that had moved would have meant a path was being classified local. Claude-Session: https://claude.ai/code/session_01TAUTP6Yky8QWoHUAPDKNJQ Co-authored-by: Claude <noreply@anthropic.com>
|
The red That job is an aggregator: it has no compiler of its own, it reads the four lanes it declares in They were cancelled because the next commit ( Ruled out first, ⛔ not assumed: the same check is green on the base ( Current head On the Docs Drift Check advisory in the comment above: the three changed files are liveness ledger data, so yielding no documentable anchor is the expected outcome rather than a gap. The page that documents this ledger — Generated by Claude Code |
…re-protocol-17 world Both notes said the match is by `role`. `roles` left the producer's envelope in protocol 17 (framework #6011), after which `user.roles ?? []` degraded to the empty set — the cloud#1442 outage. Measured at cloud @cb8ee7ff60c097cc21a584fe9caf8ef4391cc0e8: `user.roles` occurs once in agent-access.ts and only inside the docblock recording its removal, while `user.positions` is the live read, twice, both executable. Two further repairs in the same two notes: - `access` cited the chat route at `agent-routes.ts:151`. That line now holds an unrelated ADR-0013 conversation-history guardrail; the call sits at `:494`, inside `buildAgentRoutes`. - Both notes claimed ONE enforcement point. `evaluateAgentAccess` has two production consumers: the chat route, which answers 403, and `agent-runtime.ts#listAgents`, which drops an inaccessible agent from the catalog silently. `agent-access-lint.ts:14` names the asymmetry as "two consumers with two denial shapes", and a note claiming one teaches that an agent missing from the picker is a different bug from a 403. Both notes keep their old wording as dated refutation prose, the same shape #13309 used and the same shape the `role` repair in this branch uses. Notes only. `status`, `verifiedAt`, `evidenceScope` and every `evidence` string in the file are byte-identical to the previous commit, asserted per row rather than eyeballed, and `check:liveness` exits 0 with every counter byte-identical to the run before this commit — a note is not scanned, so nothing may move. Claude-Session: https://claude.ai/code/session_01TAUTP6Yky8QWoHUAPDKNJQ Co-authored-by: Claude <noreply@anthropic.com>
Fixes #13272
The re-verification half of this card. #13309 repointed the 22 citations off
packages/services/service-ai/, a path present in neither repository, but could not confirm a single one of them — no session on that board had a cloud checkout. That reading has since been taken and published on the card as comment 5682851335, against cloudcb8ee7ff60c097cc21a584fe9caf8ef4391cc0e8: 21 confirmed, 1 falsified, 3 citations wrong in a way this repo's gate cannot see. This PR transcribes it.What changes
Every confirmed row in
packages/spec/liveness/{agent,skill,action}.jsonnow carriesverifiedAt: "2026-09-15",evidenceScope: "cross-repo", and a#symbolanchor on the consuming function instead of a package-root path or a line number.agent-runtime.ts:264,agent-access.ts:50,action-tools.ts:535— while every mechanical check kept passing. A symbol moves with the consumer and goes red when the consumer is renamed or deleted.packages/mcp/src/skill-prompts.ts#projectSkillPrompt. The;before that token ends thecloudrealm's scope, so the anchor is repo-local andcheckEvidenceAnchorsresolves it on every run — where the old parenthesised(projectSkillPrompt)was prose no check read. Theinstructionsrow used to cite(skillPromptResult), one hop too far downstream: that function consumes the projected value, while the record's own key is read inprojectSkillPrompt.verifiedAt: "2026-08-06"is replaced, not kept. It was stamped while the citation still named a path existing in neither repo — false confidence the 180-day clock inverification.mtsis structurally unable to see: 40 days old, never flagged, resting on nothing.agent.rolewas noted as "persona → system prompt.": it reachesAgentSummarythroughlistAgentsand nothing else —buildSystemMessagespushesinstructions, the date block, the UI context and the skills block, and neverrole.agent.planningwas cited atagent-runtime.ts, which does not read the key at all; its three readers areroutes/agent-routes.ts,routes/assistant-routes.tsandeval/eval-runner.ts. The note's other claim — that onlyplanning.maxIterationssurvives — is confirmed and stays. Every refutation sentence fix(spec): repoint the agent, skill and action liveness citations at the real cloud path #13309 added is kept; this adds to that record.What is deliberately NOT here
agent.toolsis untouched — no stamp, no scope, nostatuschange, no prose change. The same reading falsified it: zero consumers in cloud at that ref, where the only two mentions are comments recording the removal of the branch, while this repo's ownAgentSchemaalready declares the keyretiredKey(...). That is a liveness re-grade, which belongs to triage and is filed as objectstack#18304; averifiedAtthere would certify the wrong thing. Note the shape — the row satlivesince the 2026-06 audit becauseFOREIGN_PATH_PREFIXESexempted its citation from resolution, so the exemption this card was filed about had hidden a dead key, not only a misspelled path.FOREIGN_PATH_PREFIXESis untouched. Triage ruled that out on this card, and the census on the reading found it now matches zeroevidencefields anyway. An observation for a separate card, not a rider here.packages/spec/liveness/tool.jsonis untouched — it is [worklist] Re-close the tool.json liveness ledger — every citation points into the cloud runtime's service-ai, which this repo's checkout does not contain #13042's, and was read here only as the pattern authority for row shape.Fixesrather thanPart of: the card's remaining scope was read the 22, stampverifiedAt, add#symbolanchors. The reading is done and published, the stamping is done for all 21 rows a stamp can honestly carry, and the 22nd is not an unfinished half of this card but a different question on a card of its own. The title says 21 rather than the card's 22 for the same reason.Verification
pnpm --filter @objectstack/spec check:liveness— exit 0 at9a4a6d96, and its counters move exactly where the change predicts. Against the same command on the pristine base53dd5aa:53dd5aa9a4a6d96packages/mcp/…clause was already local before this edit, because the;already reset the realmagent.activeandagent.planningnow cite the routes and eval files their readers actually live in#projectSkillPromptverifiedAtevidenceScopedeclaredA discriminating control, because green alone proves nothing about the new anchors. One framework anchor was corrupted in place (
skill.name→#projectSkillPromptX), and the mutation was proven to reach disk before the gate was asked — blob hash50e2209dto9d5418dd, and the occurrence count forprojectSkillPromptXmoved 0 to 1:Restored with
git checkout HEAD -- …and proven byte-identical: hash back to50e2209de6f647e845ca8c6c1e0330a5d98bd2c3,git diff HEADempty,git status --porcelainempty. The gate can see these anchors and can fail on them. The cloud anchors are a different matter and always will be:scanEvidencecollects no anchor while a foreign realm marker is in force, so CI can re-derive none of them — which is why the dated reading is what carries them, and why a bare re-stamp without re-reading cloud is ruled out in the_noteof all three ledgers.Other evidence:
packages/spec/scripts/liveness/*.test.ts, 9 files, 229 tests passed (evidence.test.tswalks the real ledger directory).packages/lintreads@objectstack/spec/livenessat rule time; its two ledger-reading suites are green, 75 tests passed.pnpm --filter @objectstack/spec typecheck— exit 0.scripts/pm/dispatch-gates.mjs --ranaccounts for 59 derived families: 57 run green, 2 NOT MEASURED (check:dual-build-cjs-loadsandcheck:lean-entry-closureboth exit 3,PREREQUISITE NOT MET, because they load built output for the whole workspace; CI's Build Core covers them). Included and green:check:published-files,check:platform-checklist(it derives its kinds from this very directory),check:empty-state,check:generated's changeset family,check:nul-bytes.pnpm lintis CI's run, and the narrowing here is measured rather than assumed. eslint's own configured population is**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}, every later block a subset of those extensions; run over the four changed paths with--format jsonit reports 4 files, 0 errors, and one message each: "File ignored because no matching configuration was supplied." Three JSON files and one Markdown file are not in the linted set at all. The single occurrence ofparserOptions.projectineslint.config.mjsis a comment stating that typed linting is not enabled, so no rule reads across files and this diff cannot move the verdict on a file it did not touch.A
patchchangeset ships with it:packages/spec/liveness/is listed in that package'sfiles[], so these ledgers are published inside the npm tarball and the pointers an upgrading reader follows are exactly the ones this PR dates.Generated by Claude Code
The three commits, because the diff grew after the first review
The PM seat reviewed the first commit against the published reading rather than against this description, and found two gaps. Both are recorded here so the diff and the prose agree.
1 —
9a4a6d96the transcription described above: 21 rows dated, scoped and anchored;agent.toolsleft alone; theroleandplanningnotes repaired.2 —
97b17e9efour citations that still named a whole file are anchored at their enclosing symbols:agent.planning's three readers atroutes/agent-routes.ts#buildAgentRoutes,routes/assistant-routes.ts#buildAssistantRoutesandeval/eval-runner.ts#EvalRunner, andagent.active's route clause at#buildAgentRoutes. The dispatch table that drove commit 1 had left these four unanchored; that was the PM's omission, not a transcription error, and the anchors were read in a cloud checkout before being written.3 —
bdf441e0theagent.accessandagent.permissionsnotes stop describing the pre-protocol-17 world. Three false claims across the two rows, measured at cloudcb8ee7ffand published as card comment 5683627647: the match is by a held position, never byrole(the alias framework #6011 removed, after whichuser.roles ?? []degraded to the empty set — the cloud#1442 outage); the citedagent-routes.ts:151is now an unrelated ADR-0013 guardrail while the call sits at:494; andevaluateAgentAccesshas two enforcement points, the 403 chat route andagent-runtime.ts#listAgents, which drops an inaccessible agent from the catalog silently. Both notes keep their old wording as dated refutation prose.⛔ Neither later commit touches a
status, averifiedAt, anevidenceScopeor anevidencestring: commit 3 is+2 -2in one file, proved per row against the parent blob, and commits 2 and 3 each move thecheck:livenesscounters by exactly nothing — a foreign anchor is stripped before classification and a note is never scanned.A CI reading, so the red on the two superseded heads is not re-diagnosed
TypeScript Type Checkwent red on9a4a6d96and again on97b17e9e. Both are supersede artifacts. That job runs no compiler: it reads the four lanes named inOS_AGGREGATOR_MEMBERSand fails if any did not concludesuccess. On each superseded head the next push cancelled lanes still in flight, and the aggregator reported the cancellation exactly as designed. Basemainis green on all five type-check contexts, so neither was an inherited failure.97b17e9ereached 23 success / 3 skipped / zero failures — includingBuild CoreandGoverned Surface Queue Guard— before commit 3 superseded it.Generated by Claude Code