Repository navigation
fix(mcp)!: the objectstack://objects resources read the stdio transport's merged object view, so a package's objects are listed and described - #25268
Conversation
…rt's merged object view The two object resources read the metadata service, which on a kernel with an environmentId holds no copy of an object a package registers during the boot: objectstack://objects listed none of them and objectstack://objects/NAME answered 404, while list_objects and describe_object listed and described them. stdio-data-bridge.ts now exports stdioObjectView, the one listing/by-name view list_objects and describe_object answer from. plugin.ts gives the same view to its runtime through the package-internal setRuntimeObjectView, and both resource builders read objects from it at each read; the published bridgeResources signature is unchanged. The completeness verdict stays the metadata service's. Claude-Session: https://claude.ai/code/session_01TgbP2QERdKmh147ohacHbK Co-authored-by: Claude <noreply@anthropic.com>
…be_object answer from Claude-Session: https://claude.ai/code/session_01TgbP2QERdKmh147ohacHbK Co-authored-by: Claude <noreply@anthropic.com>
…he pin type-checks The record reader called this.readResource inside the returned object literal, whose this tsc types as Booted | PromiseLike<Booted>. Claude-Session: https://claude.ai/code/session_01TgbP2QERdKmh147ohacHbK Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 12 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 6744a8d9e89a850d615039884ef9e746a96494ee && git checkout 6744a8d9e89a850d615039884ef9e746a96494ee
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 284e04810a309d5a7c8c3654b036f9ebf07a53d4 5cf1aa4ce32c79362b70f9c52d972cba65a9de83 && git checkout -B drift-repro 284e04810a309d5a7c8c3654b036f9ebf07a53d4 && git merge --no-ff 5cf1aa4ce32c79362b70f9c52d972cba65a9de83
node scripts/docs-audit/affected-docs.mjs --json 284e04810a309d5a7c8c3654b036f9ebf07a53d4
|
dogfood_battery
Owed because the title carries
Generated by Claude Code |
Contract reviewServed-tier: Read against the net diff ① Derived judgmentsPublished declaration surface of The merged view is shared, not a second copy of the merge (the card's ⛔). Right. One merge implementation remains: Hoisting The numbered Contract list — every input named, each old/new reading right per the code:
Outage / partial verdict unchanged. Right. Discovery stays ungated. Right. Neither builder calls Accept-set changes implied by the diff, each named: (a) the two object resources now accept the merged listing as their item source — correct; (b) a name absent from the merged listing is now refused with the coded miss even where a copy exists — correct, and it is the declared narrowing; (c) a protocol throw now refuses the read instead of answering the copy — correct and declared; (d) a host with no What the head's check-runs measured and what they did not. Test Core (6/6, aggregate ② Semver level
ADR-0087: exactly one marker, ③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
席位核对ACCEPT ·
Generated by Claude Code |
Requested by Sales · project thread
Fixes #25245
Clause-②: no (narrowing)
Metadata-protocol: none
The stdio MCP transport's two object resources,
objectstack://objectsandobjectstack://objects/{objectName}, now read the same objects itslist_objectsanddescribe_objecttools answer from since #25243: the protocol's merged listing, the oneGET /api/v1/meta/objectserves. They read themetadataservice until now, so on a kernel with anenvironmentIdthey listed none of a package's boot-registered objects and answered404for each, while the tools listed and described them. The merged read is shared, not copied:stdio-data-bridge.tsexports one view, the tools answer from it, andplugin.tsgives the same view to its runtime, whose two object resources read it. No published signature moves.The defect, measured on base
0b8bce40e0bridgeResources(packages/mcp/src/mcp-server-runtime.ts) builtobjectstack://objectsfrommetadataService.listObjects()andobjectstack://objects/NAMEfrommetadataService.getObject(NAME). That service holds a copy of an object only where something put one there. On a kernel with anenvironmentId(createStandaloneStack:os devasenv_local,os serve,os start; and the cloud single-environment runtime) nothing copies an object a package registers in theSchemaRegistryduring the boot.Real kernels (
ObjectKernel+MetadataPlugin+ObjectQLPlugin), driven through the realMCPServerPluginstdio session over an in-process transport speaking a stdio client's JSON-RPC, with the fixture package #25243's pins use (two objects, one declaredapiEnabled: false):resources/read objectstack://objectsresources/read objectstack://objects/qa_pkg_openlist_objects/describe_objecton the same kernelenv_local, during the boot{ objects: [], totalCount: 0 }{ error: 'Object "qa_pkg_open" not found', code: 'RESOURCE_NOT_FOUND', status: 404 }env_local, installed after the boot (source: 'marketplace')environmentId, during the bootenvironmentId, installed after the bootA disabled package (
registry.disablePackage). On the shape without anenvironmentIdthe boot copied both objects into themetadataservice, and the copy outlives the disable: both resources still listed and described them, whilelist_objectsanddescribe_objectomit them since #25243 andGET /api/v1/meta/objectomits them. Onenv_localthere is no copy, so the resources already omitted them.What changed
packages/mcp/src/stdio-data-bridge.ts. NewstdioObjectView(reads)returns{ listObjects, getObject }: the protocol's merged listing (readMergedObjects, unchanged from fix(mcp)!: the stdio data bridge reads objects from the protocol's merged view, so a package's objects are listed, described and gated #25243), and a name looked up in that same listing, so a name the listing omits is absent on both reads. A host with no merged read gets themetadataservice'slistObjects()/getObject(), as before. The bridge'slistObjectsanddescribeObjectnow answer through this view; their answers do not change (the fix(mcp)!: the stdio data bridge reads objects from the protocol's merged view, so a package's objects are listed, described and gated #25243 unit pins pass unchanged). Exported within the package only;index.tsdoes not publish it.packages/mcp/src/mcp-server-runtime.ts. New typeMcpObjectView: thelistObjectsandgetObjectmembers ofIMetadataService, picked.buildObjectListResourceandbuildObjectSchemaResourcetake it as an optional last parameter and read their objects from it; it defaults to themetadataservice, so every existing caller reads exactly as before. New package-internalsetRuntimeObjectView(runtime, view)records a runtime's view in a module-levelWeakMap; the two object resources registered bybridgeResourceslook it up at eachresources/readand fall back to themetadataservice.bridgeResourceskeeps its published signature: a third parameter would have made a new type reachable from the exportedMCPServerRuntime, a public-surface widening for wiring between this package's own plugin and runtime. The completeness verdict is still asked of themetadataservice (diagnoseListReadon the listing,diagnoseEmptyReadon a miss), as thelist_objectstool asks it.packages/mcp/src/plugin.ts. Theprotocolaccessor fix(mcp)!: the stdio data bridge reads objects from the protocol's merged view, so a package's objects are listed, described and gated #25243 added (resolved at each use,undefinedwhen absent or throwing, never captured instart()) moves out of the stdio branch, because the resources are bridged whether or not this process starts the stdio transport. BeforebridgeResources, the plugin callssetRuntimeObjectView(this.runtime, stdioObjectView({ metadataService, protocol })). The data bridge and the record resource's gate keep reading the same accessor.packages/mcp/README.md. One paragraph under Resources saying what the two object resources read..changeset/25245-mcp-stdio-object-resources-merged.md. Apatchfor@objectstack/mcp, with the BREAKING banner and the ADR-0087 dispositionnot-required (no-migration-prescription); both declaration lines are in the body.Contract
Every input whose answer changes, old to new, on the stdio transport:
resources/read objectstack://objectson a kernel with anenvironmentId, for an object a package or plugin registered during the boot. Old: omitted,totalCountshort. New: listed; the non-sys_set is whatlist_objectslists.resources/read objectstack://objects/NAMEon such an object. Old:{ error: 'Object "NAME" not found', code: 'RESOURCE_NOT_FOUND', status: 404 }. New: the schema body, equal to whatdescribe_objectanswers for NAME.metadataservice still holds a copy (the narrowing). Old: listed, and described from the copy. New: omitted, andobjectstack://objects/NAMEanswers the404 RESOURCE_NOT_FOUNDbody, asdescribe_objectanswers "not found".metadataservice's copy. Old: the resources described the copy. New: they describe the definition REST serves.metadataservice's answer. New: the protocol's error travels to the client as a JSON-RPC error, aslist_objectslets it travel since fix(mcp)!: the stdio data bridge reads objects from the protocol's merged view, so a package's objects are listed, described and gated #25243; nothing falls back. Pinned with a throwing double, not on a real store outage.Unchanged: objects that already had a copy (every object on a kernel without an
environmentId, a marketplace install on any kernel, a runtime-authored object) are listed and described as before. A loader outage still withholdstotalCount(partial: true,returnedCount,503 SERVICE_UNAVAILABLE), and a miss during an outage still answers503 SERVICE_UNAVAILABLE. A host with noprotocolservice, or one without the merged listing, reads themetadataservice, byte-identically (pinned). A host that builds its ownMCPServerRuntimeand callsbridgeResourcesreads themetadataservice, as before. The published surface does not move: the builtdist/index.d.tsdeclaresbridgeResourceswith the same two parameters, and names none ofMcpObjectView,setRuntimeObjectViewandstdioObjectViewas declarations. The record resource is untouched. Discovery stays ungated: the resource lists and describes an object declaredapiEnabled: false, aslist_objectsanddescribe_objectdo.Pins
packages/qa/dogfood/test/mcp-stdio-package-object-discovery.test.ts, 10 new cases on real kernels, beside fix(mcp)!: the stdio data bridge reads objects from the protocol's merged view, so a package's objects are listed, described and gated #25243's tool pins (80 cases in the file). For each of the four rows above:objectstack://objectslists both package objects, has nopartial, hastotalCountequal to its length, and lists the same non-sys_nameslist_objectslists;objectstack://objects/NAMEanswers both objects,enableFeatures.apiEnabled: falseincluded, each body EQUAL todescribe_object's, and a name nothing serves answers exactly{ error: 'Object "qa_pkg_nowhere" not found', code: 'RESOURCE_NOT_FOUND', status: 404 }. For a disabled package on both shapes: neither object is listed, and each answers that404body.packages/mcp/src/mcp-server-runtime.merged-object-resources.test.ts, 14 cases, doubles. The listing from the merged read where the copy is absent, themetadataservice'slistObjectsnever asked; a disabled package's objects omitted from both resources although the copy holds them; thepartialverdict kept fromlistDiagnosed; the503on a miss kept fromgetDiagnosed; a throwing merged read propagated by both builders, the copy never asked; the schema body equal todescribeObject's for both package objects; no protocol, and a protocol withoutgetMetaItems, byte-identical to themetadataservice alone; a protocol registered after the bridge read on the next call. Over a realresources/readon anMCPServerRuntime: both resources answer from the viewsetRuntimeObjectViewgave, never themetadataservice; a view given afterbridgeResourcesis read on the next call; a runtime given no view reads themetadataservice, and another runtime's view is not its own.Red before, green after. Each leg's direction was predicted before it ran. The dogfood file consumes
@objectstack/mcp'sdist/, so every leg rebuilt it, andscripts/ablation-dist-preflight.mjsproved the leg's marker reacheddist/(exit 0) and, after the restore and a rebuild at HEAD, that none remained (--absent, exit 0 for each). Mutations were landed and restored byscripts/ablation-replace.mjs(anchor 1 to 0, blob changed; restored blob equals HEAD,git diff HEADempty).0b8bce40e0(source blobs6ca41ee187,f6d8800263,e71d1f1c81) with the new dogfood pinsenv_localduring-boot listing and schema; the no-environmentIddisabled package's resourcesruntimeObjectViews.get(this)replaced)bridgeResourcesis readplugin.tsgives its runtime no viewgetObjectfalls back to the copy for a name the listing omitsenvironmentIddisabled package's resources, and itsdescribe_object(#25243's row, which reads the same view)a697b07f34The legs and the fix row were measured on
a697b07f34. The final head differs from it by one doc comment inmcp-server-runtime.ts(a{@link}to the package-internal setter, which the published declaration file would have carried as a dangling link, now plain code text); both pin files are re-run on the final head by the unit suite and the battery below. An earlier local revision passed the view as a third parameter ofbridgeResources; it was replaced before the first push for the public-surface reason above, and its legs read the same way.Local verification
On
5cf1aa4ce3, the final commit, overorigin/main0b8bce40e0(it has since moved two commits, #25258 and #25259, neither touchingpackages/mcp,packages/metadata-protocolor this dogfood file), or ondc7bee12dcwhere a line says so; the one commit between them (5cf1aa4ce3) changes how the dogfood pin file's resource helper is typed, not what any case asserts. Every heavy run went through the verify lock, and each line cites itsVERDICT.@objectstack/mcpunit suite (pnpm --filter @objectstack/mcp test), ondc7bee12dc:VERDICT command-exit 0; Test Files 42 passed (42), Tests 532 passed (532).pnpm --filter @objectstack/mcp typecheck, ondc7bee12dc:VERDICT command-exit 0(tsc --noEmit, andcheck:test-typecheckOK: 6 files, 53 errors, 8 pinned signatures held in the ledger).turbo run build --filter=!@objectstack/docs, ondc7bee12dc:VERDICT command-exit 0, 76 of 76 tasks.pnpm --filter @objectstack/dogfood typecheckwithNODE_OPTIONS=--max-old-space-size=8192:VERDICT command-exit 0. The first run, ondc7bee12dc, answered exit 2 with one error (TS2339): the pin's record reader calledthis.readResourceinside the returned object literal, whosethistsc types asBootedor aPromiseLikeof it.5cf1aa4ce3reads both through a local helper.pnpm lint(eslint . --no-inline-config), ondc7bee12dc:VERDICT command-exit 0;eslint --no-inline-configon the one file5cf1aa4ce3changes:VERDICT command-exit 0.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack: 70 families derived; each run, every oneexit 0(the second pass, on the final head; the first pass, ondc7bee12dc, also answered 70 of 70exit 0).--ranreconciliation:70 derived, 70 run, 0 NOT-MEASURED, 0 UNRUN, exit 0.node scripts/check-changeset-no-major.mjs --base origin/main --event PAYLOAD, with PAYLOAD apull_requestevent carrying this title and body: exit 0 (✓ LEVEL AXIS:no (narrowing),Metadata-protocol: none, a non-protocol change atpatch).node scripts/check-closing-keyword-parity.mjs --bodyon this body: exit 2, its "closing declarations found" answer:Fixes #25245and nothing else, bound by the duplicate-fix guard's and the part-of guard's parsers.pnpm --filter @objectstack/dogfood test, no shard), ondc7bee12dc:VERDICT command-exit 0; Test Files 289 passed, 3 skipped (292); Tests 2491 passed, 18 skipped (2509); no failure. It was not re-run for5cf1aa4ce3, which touches that one pin file's helper; the file was re-run on the final head:VERDICT command-exit 0, 80 of 80.Acceptance notes
bridgePromptsreads skills throughmergedRead, whichplugin.tsresolves once instart()(The MCP prompt bridge's listSkills reads the un-merged registry listing, so a runtime meta PUT never reaches prompts #8328), not at each use as the object reads now are. Not this card's defect (skills, not objects), and not measured here.objectstack://objects/NAMEreads the whole merged listing per call, asdescribe_objectdoes since fix(mcp)!: the stdio data bridge reads objects from the protocol's merged view, so a package's objects are listed, described and gated #25243 and the HTTP bridge since fix(runtime)!: the /mcp bridge reads objects from the protocol's merged view, so a package's objects are listed, described and gated #25136.mcp-server-runtime.tsandplugin.ts, the fix exports the shared view fromstdio-data-bridge.ts(the card's "not a second copy of the merge"), adds one README paragraph, and adds the real-kernel pins to the file fix(mcp)!: the stdio data bridge reads objects from the protocol's merged view, so a package's objects are listed, described and gated #25243's tool pins live in. Claim revision 6346855817 names this surface and the(narrowing)arm, which the first claim (6346581465) wrote as a barenobefore the disabled-package reading was measured.claude/project-thread-dkk3hd, the name the session was given, notclaude/issue-25245-…; the Duplicate Fix Guard may say so.Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_01TgbP2QERdKmh147ohacHbK