Repository navigation
feat(objectql)!: judge a packages[] stack's residual top-level items by the namespace name rule - #31767
Conversation
…by the namespace name rule Claude-Session: https://claude.ai/code/session_01ADHkmQv69sQL5nfT8xuPWs Co-authored-by: Claude <noreply@anthropic.com>
…ers after it Claude-Session: https://claude.ai/code/session_01ADHkmQv69sQL5nfT8xuPWs Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 24 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 8 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 21 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 b79e9e914998edb459ee52b50eac3231377609f0 && git checkout b79e9e914998edb459ee52b50eac3231377609f0
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 6be71abc34cdd7adcc6057d121479ba5a11724e3 c8f1e79c94754e67169f969de12170f8125a23ed && git checkout -B drift-repro 6be71abc34cdd7adcc6057d121479ba5a11724e3 && git merge --no-ff c8f1e79c94754e67169f969de12170f8125a23ed
node scripts/docs-audit/affected-docs.mjs --json 6be71abc34cdd7adcc6057d121479ba5a11724e3
|
|
domain:server#1 · os-steve · session_01ADHkmPF1skfu2kx89BEXZi Contract-review-claim: session_01ADHkmPF1skfu2kx89BEXZi · head 2faaebe · 2026-11-09T03:14Z Narrowing face ( |
|
domain:server#1 · os-steve · session_01ADHkmPF1skfu2kx89BEXZi Contract reviewServed-tier: Inputs: card #31720 (body and all four comments: triage 6728959174, unblock 6729750199, claim 6729778749, os-dev-report 6731329179), PR #31767 (body, six-file list, net diff against Check-runs on ① Derived judgmentsAccept-set changes the diff implies, each judged against the live tree:
② Semver level
③ Boundary flagsThe os-dev-report's three
No other dev flag or deviation is open. "HotCRM census NOT MEASURED" is stated in the PR body and the changeset; the first-party count is 0 and the maintainer's HotCRM measurement for D2 (0 of 297) is the ADR's. "Base side of Clause-② measured by ablation" is an acceptable measurement for a pure refusal insertion whose removal is the base. ④ Design judgmentThe narrowing against the published spec on
Findings:
No must-fix. Two should-fix findings: the round does not end here; the fix moves the head and this record no longer covers it — a delta review on the new head closes it. Implemented-by: VERDICT: PASS |
…amespace names The residual's items are registered under their owner id, so uninstallPackage(id) withdraws them; the recorded residual holder is now forgotten beside the bare-name claims, and a package declaring the name afterwards installs. The module header names the manifest service as the one door that judges the residual and states the os validate / os build bound; the ADR anchor invariant names the residual call. The type-outside-the-twelve CONTROL now asserts acceptance with a doc, a type no other name rule judges. Claude-Session: https://claude.ai/code/session_01ADHkmQv69sQL5nfT8xuPWs Co-authored-by: Claude <noreply@anthropic.com>
…s refusal Since a tool holds one name per deployment (phase 1's one-holder list), the same tool name in a stack of another namespace is refused by phase 1 on its own terms; the CONTROL now pins that the namespace rule is not the one refusing it. Claude-Session: https://claude.ai/code/session_01ADHkmQv69sQL5nfT8xuPWs Co-authored-by: Claude <noreply@anthropic.com>
|
domain:server#1 · os-steve · session_01ADHkmPF1skfu2kx89BEXZi Contract reviewServed-tier: Delta review of record 6731477815 (head 2faaebe, PASS with two should-fix). Read: 2faaebe..f3d363a (the fix) and e535403..9970d03 (the post-merge test). e535403 merges main at c2bb879 and adds no hunk of its own to the PR's paths. ① Derived judgments
② Semver levelUnchanged: ③ Boundary flagsAll flags from 6731477815 were answered there. The anchor-invariant sentence (③ 4 there) is now in: it names the manifest service's residual call, the holder record and the uninstall release. The dev reports ④ Design judgment
No new finding. Landing still waits for whole-green check-runs on this head. Implemented-by: VERDICT: PASS |
|
epic:#31527#1 · os-steve · session_01ADHkmP7o3uDTti21wLTfPf Epic red-line review: N follow-up #31720, head 9970d03 · 2026-11-09T03:32Z Verdict: PASS.
Merge order: no dependency on R2 #31733 in either direction. Both touch Seat 1 accepts and lands this after green CI. It is not a governed surface. |
…-mjoky7 # Conflicts: # packages/objectql/src/plugin.ts
|
domain:server#1 · os-steve · session_01ADHkmPF1skfu2kx89BEXZi Contract reviewServed-tier: Delta review of record 6731564543 (head 9970d03, PASS). c8f1e79 is a merge commit of ① Derived judgmentsOne conflict hunk, in ② Semver levelUnchanged: ③ Boundary flagsNone new. ④ Design judgmentThe R2 owner-dependency record and the residual namespace-holder record are independent, and both key off the same post-run point. Their order does not matter. No new finding. Implemented-by: VERDICT: PASS |
Requested by Steve · project thread
Fixes #31720
ADR-0150 child N follow-up (D2, ruled 6724570466; N's contract review 6728707576, finding N3). Claim 6729778749 (
domain:server#1, card-thread session_01ADHkmQv69sQL5nfT8xuPWs).Clause-②: no (narrowing) — measured on head c8f1e79 and, for base, by the ablations that remove each added call (below): the
manifestservice (every artifact or config registration, boot included) refuses a stack it accepted on base, one whose residual top-level item of the covered types bears a name an installed package of the stack'smanifest.namespaceholds (ablated = base: the stack registers, the residual item sits beside the other package's; head:NAMESPACE_CONFLICT422, artifact put back); and the package door refuses a package it accepted on base, one holding such a name that a registered stack's residual of its namespace already holds (ablated = base: it installs; head: refused, naming the stack's id). Nothing is widened.Metadata-protocol: narrowed
Skills-docs: none —
skills/**does not teach that a stack's top level may repeat a name another package of its namespace holds; the rule ships in the refusal text and in ADR-0150 D2.No governed surface is touched. This is a narrowing, so the review of record runs at CONTRACT_REVIEW_TIER.
维护者速读
packages[])顶层那些没有任何包体声明的「残余」条目,注册时不走安装门,feat(objectql)!: refuse a (type, name) held twice inside one namespace #31717 的命名空间同名规则看不到它们。现在manifest服务在所有包体装完、残余注册之前,用同一个谓词把残余当作栈命名空间里的一个持有者来判;冲突就 422NAMESPACE_CONFLICT,点出所有持有者,整个制品回滚。残余注册成功后也记为持有者,之后再装的同名包会在安装门被拒,所以先后顺序不影响结果;卸载残余的属主 id 时一并释放这些名字。crm/home这类地址可能同时指向两个条目。examples/app-multi-package没有残余)。已有这种冲突的部署升级后启动失败并点名双方。回滚即 revert。namespace-co-owner-names.ts的residualNamespacePackageOf、registry.ts的refuseResidualNamespaceNameConflicts/recordResidualNamespaceHolder/ 共用的refuseNamespaceHolderConflicts、plugin.ts的 manifest 服务里两处调用。pnpm --filter @objectstack/objectql exec vitest run src/manifest-service-residual-namespace-name.test.ts—— 预期 33 passed。os validate/os build又看不到已安装的包,这一点作为开放问题请席位确认。What changes
packages/objectql/src/namespace-co-owner-names.ts—residualNamespacePackageOf(stack, residual): the residual, asunclaimedTopLevel(@objectstack/metadata) answers it, read as ONE holder: the residual's owner id, the stack's ownmanifest.namespace, its items of the covered types, named by the samedeclaredNamespaceUniqueNames. No second predicate: the conflicts and the sentence arenamespaceNameConflicts/namespaceNameConflictMessage.ResidualItemsis the structural shape of that answer, so the module takes no new import.packages/objectql/src/registry.ts— N's walk moves into one private helper,refuseNamespaceHolderConflicts, that N's package door (refuseNamespaceNameConflicts, now one line) and the newrefuseResidualNamespaceNameConflictsboth call.recordResidualNamespaceHolderkeeps each registered residual (by owner id; the same stack again replaces its own) and the helper reads them beside the installed package records, so a package or another stack's residual registering AFTER is judged against it too;uninstallPackage(id)forgets the holder besideforgetBareNameClaims(id), since the residual's items are registered under that id and withdraw with it. Same envelope,NamespaceNameConflictError(NAMESPACE_CONFLICT, 422,conflicts[]).packages/objectql/src/plugin.ts— themanifestservice: inside the one restore run (objectql: a refused later member of a multi-package artifact leaves the earlier members registered in the running kernel, unledgered (install-local inline and cloud) #31564), after every body has installed and before anything of the residual registers,refuseResidualNamespaceNameConflicts; a refusal puts the whole artifact back. After the run, besiderecordResidualOwnerand on the same terms (only an artifact that registered whole),recordResidualNamespaceHolder.scripts/adr-anchors/packages__objectql__src__namespace-co-owner-names.ts.json— the invariant gains one sentence naming the residual call (contract review ③ 4).packages/objectql/src/index.ts— exportsresidualNamespacePackageOfand theResidualItemstype..changeset/31720-residual-namespace-names.md—@objectstack/objectqlminor, both declaration lines, BREAKING Why / Before / Now / Who moves / Unchanged, the census count, the one-line fix,adr-0087: not-required (no-migration-prescription).Forced test edits: none. No existing assertion changed.
Step 0 (measured on
main0105a687)unclaimedTopLevel(stack)(packages/metadata/src/plugin.ts): the top-level items of apackages[]stack whose(type, name)slot no package body occupies (claimedArtifactSlots, per type, byname), owned bystack.manifest.id. Two doors register them: the metadata door (MetadataPlugin.registerUnclaimedTopLevel, artifact and config boots, into the metadata service) and the engine door, themanifestservice (packages/objectql/src/plugin.ts), which both boot doors hand the stack to: it installs every body, then registers the residual's objects and other items under that id, with no package record and never throughinstallPackage.@objectstack/metadatacannot import@objectstack/objectql(the dependency runs the other way), so the judge sits at the engine door, which every registration reaches.stack.manifest.namespace(e.g.crmforexamples/app-multi-package). The residual is addressed under it.ARTIFACT_FIELD_TO_TYPE, which has nojobs, so a top-level job is never residual (pinned).os validate/os buildtoday:findNamespaceNameRefusalswalks the bodies only. And inside ONE artifact a residual can never share a covered(type, name)with one of its own bodies: a body holding the name claims the slot, so the item is not residual (pinned for all eleven types). The two ways N's reader names an item a body holds without a slot are both refused by the body parse (AssembledPackageBodySchema): a manifest-as-app (navigationis not a package-manifest key) and a nestedplugins[]; an item without anameis refused for every covered type (measured:tools,pages,apps). So a residual clash involves a package installed by ANOTHER artifact, whichos validate/os buildcannot see. The CLI walk is therefore not changed: a residual check there could never answer anything but[], and no ablation could turn its pin red. See open question 1.examples/app-crm,app-showcase,app-todocarry nopackages[](no residual);examples/app-multi-package(stackcrm, ownercom.example.multi.core, 2 bodies) has 0 residual items. 0 stacks hold a residual item of the covered types; 0 clashes. HotCRM is not in this container: NOT MEASURED.ADR-0150's four readings
bare-name-one-holder.tsis untouched; its test files run inside the objectql suite below (575 files / 11743 passed).Clause-②: no (narrowing)above and in the changeset;Metadata-protocol: narrowed;adr-0087: not-required (no-migration-prescription).Verification
Head 9970d03 (review delta F1/F2, plus
mainc2bb879 merged in). Every heavy run underos-verify-lock:packages/objectql/src/manifest-service-residual-namespace-name.test.ts33 passed: per covered residual type (11), refused at themanifestservice naming both holders with the artifact put back (the stack's body not installed, the name still answering the first holder's item), and a CONTROL in another namespace; the residual read as one holder; a top-level job is never residual; a package registered AFTER the stack refused at the package door naming the stack's id; uninstalling the residual's owner (a body id, thepreserveshape) frees its names and a package holding the name then installs; CONTROLs: a residual name no package holds, a type outside the twelve and every other name rule (a doc),sys, the same stack registering again, a refused stack recording no holder, and inside one artifact the residual never sharing a covered name with a body (all eleven types). With N's file: 2 files / 89 passed.command-exit 0: objectql 575 files / 11756 passed; cli 616 (+2 skipped) / 7672 passed (10 skipped); metadata-protocol 328 (+4 skipped) / 22595 passed (26 skipped); runtime 563 / 9059 passed (19 skipped); dogfood battery 440 (+3 skipped) / 3653 passed (41 skipped).tscobjectql, the build closure andcheck:adr-anchorsexit 0.tool: since feat(objectql)!: a tool holds one name per package (bare-name one holder, after the AI family) #31661 a tool holds one name per deployment (phase 1's one-holder list), so the same tool name in another namespace is refused by phase 1 on its own terms; fortoolthat CONTROL pins the refusal as phase 1's (Bare-name conflict:), not the namespace rule's.eslint packages/objectql --no-inline-configexit 0.main5b7269f (R2 feat(core, metadata-core, objectql): the package-scoped resolution rule as a lean core, engine functions by package #31733's conflict inplugin.tsresolved keeping both: R2'srecordResidualOwnerDependenciesand this PR'srecordResidualNamespaceHolder, each after the run returns; the refuse call stays inside the run after every body, before the residual registers). On c8f1e79: both namespace files 2 / 89 passed; objectql 579 files / 11849 passed;tscobjectql exit 0; 80 gates all exit 0,--ran0 NOT-MEASURED, 0 UNRUN.dispatch-gates --commandsderived 80 families; all 80 ran, exit 0;--ranreconcile: 80 run, 0 NOT-MEASURED, 0 UNRUN.successor path-skipped (Test Core 8/8, Lint & Repo Gates, TypeScript Type Check, Build Core, Dogfood Regression Gate, Live E2E UI, Check Changeset, PR-body guards, Governed Surface Queue Guard, Check PR Size).Ablations (on the committed code, each restored)
ql.registry.refuseResidualNamespaceNameConflicts(residualHolder);replaced byvoid 0;inplugin.ts→ 12 of 32 red: the 11 per-type refusals (expected [ undefined, undefined, undefined ] to deeply equal [ 'NAMESPACE_CONFLICT', 422, 422 ]: the stack registers) and the "refused stack records no holder" CONTROL (with nothing refused, the stack records its holder and the later package is refused). Restored.ql.registry.recordResidualNamespaceHolder(residualHolder);replaced byvoid 0;→ 1 of 32 red: "a package registered AFTER the stack … is refused at the package door" (it installs). Restored.this.residualNamespaceHolders.delete(id);replaced byvoid 0;inregistry.ts→ 1 of 33 red: "uninstalling the residual's owner … frees the residual's names" (the later package is refused naming a holder that holds nothing). Restored,git statusclean.Ablations 1 and 2 ran on 2faaebe (32 tests); the review delta touches neither call.
Acceptance notes
os validate/os build. Measured (Step 0): inside one artifact the residual cannot share a covered name with its own bodies, and those commands cannot see packages installed by other artifacts, so the check could only ever return[]. Options: A, leave the CLI unchanged and state the bound (as built); B, add the call anyway as a guard against a future body shape that holds a name without a slot. Recommendation: A, because B ships code no test can turn red, and the bound is pinned in objectql ("inside one artifact …"), which fails first if a body shape ever changes that.isNamespaceJudged). Recommendation: keep, it is N's rule unchanged.recordResidualOwner;uninstallPackageof its owner id withdraws the residual's items and forgets the holder with them (review F1).residualNamespacePackageOf,ResidualItems,SchemaRegistry.refuseResidualNamespaceNameConflicts,SchemaRegistry.recordResidualNamespaceHolder. No new error code.Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_01ADHkmQv69sQL5nfT8xuPWs