Skip to content

fix(spec): the 21 confirmed cloud citations in the agent/skill/action liveness ledgers carry a read date and a symbol anchor - #18313

Merged
hotlong merged 3 commits into
mainfrom
claude/issue-13272-liveness-verifiedat-anchors
Sep 15, 2026
Merged

hotlong merged 3 commits into
mainfrom
claude/issue-13272-liveness-verifiedat-anchors

Conversation

@hotlong

@hotlong hotlong commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

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 cloud cb8ee7ff60c097cc21a584fe9caf8ef4391cc0e8: 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}.json now carries verifiedAt: "2026-09-15", evidenceScope: "cross-repo", and a #symbol anchor on the consuming function instead of a package-root path or a line number.

  • A symbol instead of a line, because a line rots in range. Three cited lines had already drifted onto unrelated prose — 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.
  • The framework half is now gate-checked. Five skill rows anchor 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 — where the old parenthesised (projectSkillPrompt) was prose no check read. The instructions row used to cite (skillPromptResult), one hop too far downstream: that function consumes the projected value, while the record's own key is read in projectSkillPrompt.
  • The eight skill rows' 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 in verification.mts is structurally unable to see: 40 days old, never flagged, resting on nothing.
  • Four ledger assertions the reading falsified are repaired in the prose — two in the first commit, two more in the third; see the commit log below. agent.role was noted as "persona → system prompt.": it reaches AgentSummary through listAgents and nothing else — buildSystemMessages pushes instructions, the date block, the UI context and the skills block, and never role. agent.planning was cited at agent-runtime.ts, which does not read the key at all; its three readers are routes/agent-routes.ts, routes/assistant-routes.ts and eval/eval-runner.ts. The note's other claim — that only planning.maxIterations survives — 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.tools is untouched — no stamp, no scope, no status change, 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 own AgentSchema already declares the key retiredKey(...). That is a liveness re-grade, which belongs to triage and is filed as objectstack#18304; a verifiedAt there would certify the wrong thing. Note the shape — the row sat live since the 2026-06 audit because FOREIGN_PATH_PREFIXES exempted its citation from resolution, so the exemption this card was filed about had hidden a dead key, not only a misspelled path.
  • FOREIGN_PATH_PREFIXES is untouched. Triage ruled that out on this card, and the census on the reading found it now matches zero evidence fields anyway. An observation for a separate card, not a rider here.
  • packages/spec/liveness/tool.json is 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.

Fixes rather than Part of: the card's remaining scope was read the 22, stamp verifiedAt, add #symbol anchors. 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:livenessexit 0 at 9a4a6d96, and its counters move exactly where the change predicts. Against the same command on the pristine base 53dd5aa:

counter base 53dd5aa 9a4a6d96 why
repo-local evidence paths 505 declared, 505 resolved 505 declared, 505 resolved unchanged, as predicted — the packages/mcp/… clause was already local before this edit, because the ; already reset the realm
attributed to another repo 204 207 agent.active and agent.planning now cite the routes and eval files their readers actually live in
symbol anchors 578 written, 578 resolving 583 written, 583 resolving the five skill rows anchored at #projectSkillPrompt
entries carrying verifiedAt 516 529 +13: 10 agent rows and 3 action rows; the 8 skill rows were already dated and are re-dated in place
evidenceScope declared 90 cross-repo, 608 undeclared 111 cross-repo, 587 undeclared +21

A 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 hash 50e2209d to 9d5418dd, and the occurrence count for projectSkillPromptX moved 0 to 1:

MUTATED VERDICT command-exit = 1
symbol anchors: 583 pointer(s) written `path#symbol`, 582 naming a symbol the cited file contains, 1 UNRESOLVED.
✗ 1 anchored citation(s) name a symbol the cited file does not contain:
    skill/name → packages/mcp/src/skill-prompts.ts#projectSkillPromptX

Restored with git checkout HEAD -- … and proven byte-identical: hash back to 50e2209de6f647e845ca8c6c1e0330a5d98bd2c3, git diff HEAD empty, git status --porcelain empty. The gate can see these anchors and can fail on them. The cloud anchors are a different matter and always will be: scanEvidence collects 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 _note of all three ledgers.

Other evidence:

  • Liveness suitepackages/spec/scripts/liveness/*.test.ts, 9 files, 229 tests passed (evidence.test.ts walks the real ledger directory).
  • The ledger's cross-package consumerpackages/lint reads @objectstack/spec/liveness at rule time; its two ledger-reading suites are green, 75 tests passed.
  • pnpm --filter @objectstack/spec typecheck — exit 0.
  • Derived gate familiesscripts/pm/dispatch-gates.mjs --ran accounts for 59 derived families: 57 run green, 2 NOT MEASURED (check:dual-build-cjs-loads and check:lean-entry-closure both 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.
  • Repo-wide pnpm lint is 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 json it 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 of parserOptions.project in eslint.config.mjs is 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 patch changeset ships with it: packages/spec/liveness/ is listed in that package's files[], 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 — 9a4a6d96 the transcription described above: 21 rows dated, scoped and anchored; agent.tools left alone; the role and planning notes repaired.

2 — 97b17e9e four citations that still named a whole file are anchored at their enclosing symbols: agent.planning's three readers at routes/agent-routes.ts#buildAgentRoutes, routes/assistant-routes.ts#buildAssistantRoutes and eval/eval-runner.ts#EvalRunner, and agent.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 — bdf441e0 the agent.access and agent.permissions notes stop describing the pre-protocol-17 world. Three false claims across the two rows, measured at cloud cb8ee7ff and published as card comment 5683627647: the match is by a held position, never by role (the alias framework #6011 removed, after which user.roles ?? [] degraded to the empty set — the cloud#1442 outage); the cited agent-routes.ts:151 is now an unrelated ADR-0013 guardrail while the call sits at :494; and evaluateAgentAccess has two enforcement points, the 403 chat route and agent-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, a verifiedAt, an evidenceScope or an evidence string: commit 3 is +2 -2 in one file, proved per row against the parent blob, and commits 2 and 3 each move the check:liveness counters 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 Check went red on 9a4a6d96 and again on 97b17e9e. Both are supersede artifacts. That job runs no compiler: it reads the four lanes named in OS_AGGREGATOR_MEMBERS and fails if any did not conclude success. On each superseded head the next push cancelled lanes still in flight, and the aggregator reported the cancellation exactly as designed. Base main is green on all five type-check contexts, so neither was an inherited failure. 97b17e9e reached 23 success / 3 skipped / zero failures — including Build Core and Governed Surface Queue Guard — before commit 3 superseded it.


Generated by Claude Code

…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>
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tooling labels Sep 15, 2026
@github-actions

github-actions Bot commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️ 3 changed file(s) yielded no anchor (packages/spec/liveness/action.json, packages/spec/liveness/agent.json, packages/spec/liveness/skill.json), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)).

What this run could not see
  • 3 changed file(s) yielded no anchor (packages/spec/liveness/action.json, packages/spec/liveness/agent.json, packages/spec/liveness/skill.json) — pages documenting those are invisible to this run
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 136 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 52c1a8c4af411ce759158bd938474903d42dc6b2packageMentionDocs.

…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>

hotlong commented Sep 15, 2026

Copy link
Copy Markdown
Contributor Author

The red TypeScript Type Check on 9a4a6d96 is a supersede artifact, not a type error. Recorded so nobody chases it.

That job is an aggregator: it has no compiler of its own, it reads the four lanes it declares in OS_AGGREGATOR_MEMBERS and fails if any of them did not conclude success. Its log at 2026-09-15T15:59:36Z shows all four arriving as cancelled:

type-check lane `typecheck-consumers`    concluded `cancelled` -- expected `success`
type-check lane `typecheck-debt`         concluded `cancelled` -- expected `success`
type-check lane `typecheck-source-gates` concluded `cancelled` -- expected `success`
type-check lane `typecheck-workspace`    concluded `cancelled` -- expected `success`
check-typecheck-lanes: FAILED (4 problem(s) above).

They were cancelled because the next commit (97b17e9e, the four remaining symbol anchors) superseded that run — 17 of 9a4a6d96's runs went cancelled in the same instant. ⇒ The aggregator reported exactly what it saw and was right to; ⛔ there is no type error in this diff to fix, and a re-run of the superseded head would be spending a re-run on a commit that is no longer the head.

Ruled out first, ⛔ not assumed: the same check is green on the base (main 53dd5aaa, all five type-check contexts success), so this was never an inherited failure either.

Current head 97b17e9e: no failures. 12 green, 17 still running, and the two gates this diff actually exercises are already through — Spec property liveness ✅ and Check Changeset ✅. This PR stays a draft and is ⛔ not enqueued until every check is green.

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 — packages/spec/liveness/README.md — is not free prose but a generated, reconciled state table (scripts/liveness/readme-table.mts), and no row's status changed in this diff, so the table and state-counts.md are unmoved; Spec property liveness is the check that would have said otherwise, and it is green.


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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tooling

Projects

None yet

2 participants