Skip to content

docs(spec): re-close the tool.json liveness ledger against the real cloud runtime - #13275

Draft
os-project-manager wants to merge 3 commits into
mainfrom
claude/issue-13042-tool-liveness-reclose
Draft

docs(spec): re-close the tool.json liveness ledger against the real cloud runtime#13275
os-project-manager wants to merge 3 commits into
mainfrom
claude/issue-13042-tool-liveness-reclose

Conversation

@os-project-manager

@os-project-manageros-project-manager commented Aug 29, 2026

Copy link
Copy Markdown
Collaborator

Fixes#13042

Measured at framework 4bc1737c (this branch's tip) against cloud origin/main@15f55df and objectui origin/main@26896c6. The card's execution constraint — a reader with the cloud repo checked out — was satisfied for the first time in this container, and an objectui checkout came into reach mid-review, which settled the one entry the cloud walk alone could not. Every foreign reading was taken with git -C … show/grep origin/main, never a working tree; zero writes to either sibling repo. Session: https://claude.ai/code/session_01KX8wnyjStaZcuMyAMNsy3N

What the card asked, and what the walk found

The premise held, and then some. All six entries cited packages/services/service-ai/…. That path exists in neither repo:

  • Framework:git ls-files | grep -ic service-ai returns 0; find . -name service-ai returns nothing; packages/services/ holds 16 members, none of them service-ai.
  • Cloud: the real layout is packages/service-ai/… and packages/service-ai-studio/….

Those six pointers stayed green for as long as they existed because packages/spec/scripts/liveness/evidence.mts hardcodes exactly the wrong spelling:

exportconstFOREIGN_PATH_PREFIXES=['packages/services/service-ai/'];

so the stale form was classified foreign and never resolved. The real cloud path is repo-rooted in shape and is not in that list, so a naive repoint resolves it as LOCAL and fails CI. Every repointed citation therefore carries the cloud realm marker. That trap is now recorded in the _note for whoever repairs the sibling ledgers.

Per-entry verdicts — all six now dated

propbeforeafterwhat the code said
namelive, undatedlive, stamped, framework-local anchor addedread at both ends: framework packages/mcp/…#registerToolFromDefinition, cloud #register (registry map key) and #buildVercelOptions (LLM tool set key)
labellive, no evidence field at alllive, stamped, first evidence pointer ever, both sides of the round tripcloud #toToolLabel composes the persisted record; objectui #ToolPreview reads it back and renders it as the header
descriptionlive, undatedlive, stamped, framework-local anchor addedcloud #buildVercelOptions puts it in front of the model; the framework MCP bridge reuses it verbatim
parameterslive, undatedlive, stampedcloud #buildVercelOptions wraps it with jsonSchema as the SDK inputSchema. Deliberately not co-cited to the MCP bridge — see below
objectNamelive, undatedlive, stamped — same verdict, inverted reasoningsee below
outputSchemaexperimental, undatedexperimental, stampedthe fold is real (#outputSchemaKeys + #buildToolDescription); the negative half is now measured, not asserted

objectName — the verdict survives, the reasoning does not

Its stated basis is falsified: both cited sites read action.objectName (the ActionSchema key, whose action.json row carries that citation word for word), and the same-named AIToolDefinition.objectName is written by #actionToToolDefinition and read by nothing in cloud or framework. It never reaches persisted metadata via cloud either — the single metadataService.register tool site ingests only the shipped Studio definitions, none action-backed. The key gates, binds and routes nothing.

The consumer that does exist is objectui's registered metadata-admin preview: ToolPreview reads it off the persisted record and renders it as the header's object pill, dropping the row when the key is absent. It is reachable (registerMetadataPreview('tool', …), resolved by ResourceEditPage#getMetadataPreview) and deliberately pinnedToolPreview.test.tsx asserts the pill over a comment reading "objectName is NOT residue — ToolSchema still accepts it".

Under the #7131 ruling that settles a display key, and here the render is not standing in for a behaviour: ToolSchema's own describe says a metadata-authored tool is a read-only projection no executor loads, so there is no second layer to proxy. ⇒ liveas display, never as a binding, and explicitly not an ADR-0049 retirement candidate — grading it dead would have retired a key the renderer deliberately renders and tests, the requiresService / colorVariant lesson from the other end.

The _note was false four ways

Repaired: (a) the framework tree is absent, not "a stale build artifact with no src/"; (b) the cited path prefix was wrong for cloud too; (c) "the OPEN framework edition does not consume them" is false for name and description; and (d) "tool metadata is WRITE-ONLY … not metadata read-back" is falseToolPreview reads all six props off the stored record, and that claim is precisely why nobody had looked at the renderer.

Note the pattern: every one of the four was a negative claim no gate could falsify, and three were wrong. The type therefore has two live bases — the AIToolDefinition surface (behavioural) and the metadata read-back (display) — and every row now names the one it rests on.

parameters carries the remaining asymmetry as a finding: registerToolFromDefinition registers each bridged tool with noinputSchema while its docblock claims the JSON Schema is passed as annotations metadata. Filed as #13271; not touched here.

Scope

File surface held: packages/spec/liveness/tool.json plus the changeset the dispatch's own gate derivation asked for. No verdict moved, so state-counts.md is untouched and the gate confirms it is still current.

liveness/ is in this package's published files array, so this is shipped data — hence a real changeset (@objectstack/spec: patch), on the action-execute-ledger-dead precedent rather than skip-changeset.

Verification — all at 4bc1737c, working tree clean

Gate family derived with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack on the real 2-path change set (not a hand-written diff), re-derived after each round. Twelve families, each exit code captured directly from the command and never through a pipe, re-run on this exact head after the final commit:

GATE liveness EXIT=0 GATE published-files EXIT=0
GATE empty-state EXIT=0 GATE merge-driver EXIT=0
GATE strictness-ledger EXIT=0 GATE objectui-cs EXIT=0
GATE variant-docs EXIT=0 GATE empty-changeset EXIT=0
GATE nul-bytes EXIT=0 GATE cs-no-major EXIT=0
GATE keyed-text EXIT=0 GATE adr0087-reg EXIT=0

The liveness gate's own verdict lines, against the pre-edit baseline:

  • evidence paths: 469 repo-local … 469 resolved (was 467/467) — +2 local, the two packages/mcp citations. The ledger could not resolve a single tool.json pointer before; now CI can falsify two of them from this checkout, which is precisely the condition the card was filed for. Foreign 154 to 158.
  • symbol anchors: 505 … 505 naming a symbol the cited file contains (was 501).
  • key-mention anchoring: 469 pair(s) asked, 468 anchored, 1 exempt.
  • re-verification clock: 453 … 251 undated (was 447/257) — +6, every entry in this file now dated.
  • evidence quality: 16 live entr(ies) cite a PRODUCER … 70 cross-repo (was 12 / 64).
  • line citations: 0 — unchanged; anchors only, per [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.
  • ✓ packages/spec/liveness/state-counts.md is current — the fence held.

Narrowing declared: repo-wide scans matched only by the coarse packages/** glob (dual-build, test-source-alias, type-source-resolution, page-declaration-shape, slot-lookup, docs-drift, shard attestation) were left to CI, which runs the farm exactly once regardless.

Findings filed unassigned, none touched here: #13271 (MCP bridge drops parameters) and #13272 (the same stale path spelling and the same falsified _note sentence survive in agent.json (11 entries), skill.json (8) and action.json (3)). #13276 tracked the objectName question and is now closed by the objectui reading above.

…loud runtime
Every entry cited `packages/services/service-ai/...`, a path that exists in
neither repo: the framework has no service-ai tree at all, and cloud's real
layout is `packages/service-ai/...`. The citations stayed green only because
the gate hardcodes exactly that wrong spelling in FOREIGN_PATH_PREFIXES and so
never resolved them.
Re-closed against cloud origin/main@15f55df. `name` and `description` gain
framework-local ANCHORED evidence (the packages/mcp bridge reads both off each
AIToolDefinition), so CI can falsify them from this checkout for the first
time. `label` gains its first evidence pointer ever. `parameters` and
`outputSchema` are confirmed with their negative halves measured rather than
asserted. `objectName` is left `live` and deliberately UNDATED: the cloud walk
falsified its stated basis, but the only remaining candidate reader is an
objectui read-back that this container could not walk, and publishing a
negative cross-repo verdict without that walk is the app.homePageId failure
shape.
The file-level `_note` is repaired: its "stale build artifact with no src/"
claim is now simply false (the tree is absent outright), its cited path prefix
was wrong for cloud too, and its "the OPEN framework edition does not consume
them" claim was false for two keys.
No verdict moved, so state-counts.md is untouched and still current.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KX8wnyjStaZcuMyAMNsy3N
The entry records a measured falsification that this container could not act
on (the re-grade needs an objectui reader). The measurement now has its own
card, so the note names it rather than leaving the reader to re-derive it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KX8wnyjStaZcuMyAMNsy3N
…ote's write-only claim
An objectui checkout came into reach, so the one entry the cloud walk could not
settle is now settled — and the answer inverts the reasoning rather than the
verdict. `objectName` stays `live`, but not for the reason the row gave: its
"action-tool dispatch" basis is falsified (both cited sites read the ActionSchema
key, and AIToolDefinition.objectName is written and read by nothing), so the key
binds and routes nothing. The consumer that does exist is objectui's registered
metadata-admin preview, which reads it off the persisted record and renders it as
the header object pill — the #7131 display-key rule, pinned in ToolPreview.test.tsx
over a comment reading "objectName is NOT residue". Live as a DISPLAY key, and
explicitly not an ADR-0049 retirement candidate.
That reading also falsifies the _note's fourth claim, "tool metadata is WRITE-ONLY
... not metadata read-back" — which is precisely why nobody had looked at the
renderer. ToolPreview reads all six props off the stored record. The type has two
live bases (the AIToolDefinition surface, behavioural; and the metadata read-back,
display) and every row now names the one it rests on. The stale "objectui not
walked" hedges on `label` and `outputSchema` are corrected in the same pass.
All six entries are now dated. No verdict moved, so state-counts.md stays untouched.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KX8wnyjStaZcuMyAMNsy3N
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️1 changed file(s) yielded no anchor (packages/spec/liveness/tool.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
  • 1 changed file(s) yielded no anchor (packages/spec/liveness/tool.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 — 126 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 74049254d47bd0edd2a2fcd732dcc01c91504f10packageMentionDocs.

@github-actionsgithub-actionsBot added size/s documentation Improvements or additions to documentation tooling labels Aug 29, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/stooling

Projects

None yet

2 participants

@os-project-manager@claude