Repository navigation
feat(objectql)!: by-name readers of apps, pages, dashboards, reports, books, cubes, tools, skills and agents consult (package, name) - #31847
Conversation
…er under package addressing Adds the nine-type list the ADR-0150 E8 readers consult (package, name) for, and the 409 RESOURCE_CONFLICT refusal they raise, with the expected answers of every reader: the two-holder refusal, D3 (a), the outside-closure step, and the one-holder controls. The reader wiring waits for the resolver core to become reachable from the lean ./core entry; until then the narrowing cases are red by design and the controls green. Claude-Session: https://claude.ai/code/session_01ADHkmTH1oAwgDMigQ9pj37 Co-authored-by: Claude <noreply@anthropic.com>
… (package, name) getApp, MetadataFacade get/getEntry/exists and the protocol's package-less by-name read now answer through the one package-scoped resolver for app, page, dashboard, report, book, analytics_cube, tool, skill and agent. A name the resolver refuses throws a coded 409 RESOURCE_CONFLICT naming every candidate; one holder and zero holders answer as before. Claude-Session: https://claude.ai/code/session_01ADHkmTH1oAwgDMigQ9pj37 Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ADHkmTH1oAwgDMigQ9pj37 Co-authored-by: Claude <noreply@anthropic.com>
…ing both holders The AI-family boot pin asserted the first-registered package's agent and tool were served at GET /meta/TYPE/NAME. That read now answers 409 RESOURCE_CONFLICT naming both holders; the ?package= read serves each package's own, and the skill prompt list is unchanged. Claude-Session: https://claude.ai/code/session_01ADHkmTH1oAwgDMigQ9pj37 Co-authored-by: Claude <noreply@anthropic.com>
… clash reaches Claude-Session: https://claude.ai/code/session_01ADHkmTH1oAwgDMigQ9pj37 Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 2 package(s): 25 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 10 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 27 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 3321bda47a7063ca8f6e79611344679c09949c29 && git checkout 3321bda47a7063ca8f6e79611344679c09949c29
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin cc16008a1221cc0f676240c84a6b1a8285a38076 d981cf7c1ce46776fafe4b725f2a3ef9c77dec9e && git checkout -B drift-repro cc16008a1221cc0f676240c84a6b1a8285a38076 && git merge --no-ff d981cf7c1ce46776fafe4b725f2a3ef9c77dec9e
node scripts/docs-audit/affected-docs.mjs --json cc16008a1221cc0f676240c84a6b1a8285a38076
|
|
epic:#31527#1 · os-steve · session_01ADHkmP7o3uDTti21wLTfPf Epic red-line review: E8 #31680, head d981cf7 · 2026-11-09T06:56Z Verdict: PASS.
The forced dogfood edit is confirmed. Non-blocking, for a later cleanup card: plugin-sharing (E4) also defines a Seat 2 accepts and lands this after green CI. It is not a governed surface. After it lands, G2 #31692 still waits on K #31681 and P #31669. |
|
domain:server#2 · os-steve · session_01ADHkmQKZzp4DLtqiCkZZRM Contract reviewServed-tier: Inputs: card #31680 (body and all four comments: triage, the epic seat's unblock, the seat's claim, the dev report), PR #31847 (body, nine-file list, net diff against ① Derived judgmentsEvery accept-set and public-surface change the diff implies, each judged against the cited code on
Wrong: none. ② Semver level
③ Boundary flags
④ Design judgmentThe narrowing, read against ADR-0150 on Red lines over the floor.
Scenarios tried to break it.
Findings.
No must-fix. No should-fix. The narrowing takes no declared capability away at the consumer; each refused read is one the spec on Implemented-by: VERDICT: PASS |
|
domain:server#2 · os-steve · session_01ADHkmQKZzp4DLtqiCkZZRM 维护者速读
席位核对 — ACCEPT · domain:server#2 · Head-sha
|
Requested by Steve · project thread
Fixes #31680
Clause-②: no (narrowing)
Metadata-protocol: none
Skills-docs: none — no skill or doc teaches a bare read of a name two packages ship, an environment item read by a package caller, or a read of an undeclared package's item; ADR-0150 D3 already states the rule, and the
?package=qualifier the refusals point to is already documented on the meta read.Narrowing: (1) a bare name two packages ship, read with no package, is refused naming both (first-party count 0; for agent, skill and tool only on a deployment that booted already holding the clash); (2) a package caller's name only the environment holds is refused, D3 (a) (count 0); (3) a package caller's name whose one holder is outside its declared closure is refused (count 0, same-artifact siblings 0).
维护者速读
packages/objectql/src/package-scoped-read.ts,然后registry.ts的resolveItem,再看protocol-meta-item-reads.ts中的packagelessScopedRead。What changes
Child E8 of epic #31527 wires the by-name readers of nine types (
app,page,dashboard,report,book,analytics_cube,tool,skill,agent) to R's one resolver,resolvePackageScopedName(the lean core R2 #31733 landed). This follows the epic seat's ruling (option A): both arms ofgetAppandMetadataFacade, and no bindable reader.package-scoped-read.ts(new).PACKAGE_SCOPED_READ_TYPESlists the nine types.readPackageScopedItemturns the resolver's answer into a reader's: resolved returns the item, not-found returnsundefined, and a refusal throwsPackageScopedNameRefusedError. That error carriescode: 'RESOURCE_CONFLICT',status/httpStatus: 409, the resolver's own sentence naming every candidate, and therefusal. It restates no part of the rule and holds no closure walk of its own.SchemaRegistry.resolveItem(type, name, callerPackageId?, member?)andreadsByPackage(type). A wired type is read throughreadPackageScopedItem; every other type keepsgetItem.The readers, and the caller package each one passes:
SchemaRegistry.getApp(name, currentPackageId?)currentPackageIdMetadataFacade.get(type, name, currentPackageId?)currentPackageIdMetadataFacade.getEntry(type, name)MetadataFacade.exists(type, name)true(the holders exist)readMetaItemwith nopackageId(GET /api/v1/meta/TYPE/NAME, both faces, so MCPagent_prompttoo)null(a caller with no package)registry.resolveItem(type, name, null, member)once, before any layer is read, and step 3 serves that answer in place ofgetItemand its plural retry. A read with?package=keepsgetItem; that address is G1's exact qualifier, not a caller. metadata-protocol still cannot import objectql, so it reaches the resolver only through the registry'sresolveItem. No new import edge.agent_promptalready reads through the protocol read (mcp: agent_prompt reads the kernel metadata service and misses package-shipped agents that /meta/agent/NAME serves (bare-name phase 3, Q5) #31514). One new pin: a 409 refusal travels as a protocol error naming both holders. It is never answered as "not found" or as the outage text.@objectstack/objectql.Tests
packages/objectql/src/package-scoped-e8-readers.test.ts(new, 71 tests). It runs the realSchemaRegistry, the realObjectStackProtocolImplementationand the realMetadataFacade, and covers:resolveThrownHttpError;facade.get/getEntryandgetApp;?package=read untouched, and getApp's nav contributions;getItem.packages/mcp/src/mcp-server-runtime.agent-prompt-advertised.test.ts: one new test, the refusal travels.packages/qa/dogfood/test/bare-name-one-holder-ai-family.dogfood.test.ts(Bare-name phase 1: refuse a second package holding a non-object name whose consumers resolve without package context (ruling C on #30798) #30803 / objectql: two packages' tools under one name, /meta/tool/NAME silently serves the first-registered one; measure cloud's by-name tool read, then decide whether tool joins the bare-name one-holder list #31414, phase 1). Its cold boot holds an agent, skill and tool clash between two packages (a boot warns, it does not refuse), and it pinned thatGET /meta/agent|tool/NAMEwith no package "keeps serving the first-registered package's item". That is narrowing (1) on the wire, and the full dogfood battery went red on exactly those 3 tests (3 failed / 3659 passed) on head 9e37aef. The pin now asserts409 RESOURCE_CONFLICTnaming both packages for agent, skill and tool, and that?package=serves each package's own. The boot warning, the install refusals and the skill-prompt reading are unchanged.Red on main (b126c13, the probe in the card notes): for every uncovered type, with two holders,
/meta,facade.getandgetAppwith no package servedalpha, the first registered. A package caller was served an outside-closure single holder and an environment-only item. Before the wiring, the new file ran 25 failed and 48 passed; the 25 failures were exactly the three narrowing arms.Ablations. Each mutation was put in through
scripts/ablation-replace.mjsand its run went red. Each was then restored, with the blob back to HEAD.readsByPackage→ false (the protocol arm off)facade.get→getItemgetApp→getItemresolveItem→getItemexistsrefusal → falsereadAgentByNamereadsRESOURCE_CONFLICTas a missablation-dist-preflightmarker present in distFor A7, the restore leg rebuilt metadata-protocol, the preflight found the marker absent from dist with the tree clean, and the run went back to 71/71.
Acceptance notes
The four readings:
registry-bare-name-one-holder.test.ts,package-scoped-resolver.test.ts,core-boundary.ratchet.test.ts,registry-cross-package-collision.test.ts,engine-cross-package-collision.test.ts, and the full objectql suite. The one exception is the dogfood boot-clash pin named above: its "first registered is served" line is the behaviour E8 removes, so it is flipped. Its boot warning and install refusals are untouched.adr-0087: not-required (no-migration-prescription)marker).Clause-②: no (narrowing), per seat 2's ruling. The changeset carries the BREAKING banner, with FROM→TO and the one-line fix.Red lines:
readPackageScopedItem→resolvePackageScopedName. No reader walks a closure or falls back to the first composite.package-scoped-resolver.tswas not edited.packages/spec/**,ui/**,docs/adr/**or E6 readers were touched, andregistry.ts installPackagewas not touched.By-name reads not rewired here. Each keeps
getItem:getMetaItemLayered);readCodeLayerForCarryForward;protocol-artifact-lookup.ts;protocol-save.ts,protocol-authoring-gate.ts);diagnosedGetfor a host without a protocol;getAppNavContributions;getAllAppslist.The seat can file any of these as later cards.
ADR-0146 overrides: a pair's stored override row is hydrated into the registry's bare slot (
protocol-registry-hydration.ts), so a package-less read still finds the environment's item first, and the full ADR-0146 suites are green.New public names, which are the seat's call:
PACKAGE_SCOPED_READ_TYPES,PackageScopedReadType,isPackageScopedReadType,readPackageScopedItem,PackageScopedNameRefusedError,SchemaRegistry.resolveItemandSchemaRegistry.readsByPackage.Verification
Head
d981cf7c1ce46776fafe4b725f2a3ef9c77dec9e, which merges origin/main fdfcc7a. Every run below is on that head, underos-verify-lock, and each VERDICT was command-exit 0.pnpm build: green.srcand on the edited dogfood file.dispatch-gates.mjs --commandslisted 75 commands. All 75 exited 0, and--ranreconciles 75 derived, 75 run, 0 NOT-MEASURED.pnpm --filter @objectstack/dogfood test): Test Files 441 passed, 3 skipped (444). Tests 3662 passed, 42 skipped (3704). 0 failed.🤖 Generated with Claude Code
https://claude.ai/code/session_01ADHkmTH1oAwgDMigQ9pj37