Repository navigation
fix(objectql)!: every reader of the registry's overlay slot binds a row of an App's customization pair by the exact read's owner rule (ADR-0150 D4) - #32815
Conversation
…, not per deployment (ADR-0150 child B, part 1 of the webhook family) WIP: the switch, the platform-requested auth.* names, the bundle-addition rule, the package-scoped /meta read and the send's holder-filtered bundle. Claude-Session: https://claude.ai/code/session_01ADHkmTMDJkEC5D92VHoU5v Co-authored-by: Claude <noreply@anthropic.com>
…latform-requested auth names and the bundle-addition rule Claude-Session: https://claude.ai/code/session_01ADHkmTMDJkEC5D92VHoU5v Co-authored-by: Claude <noreply@anthropic.com>
…rrowing Claude-Session: https://claude.ai/code/session_01ADHkmTMDJkEC5D92VHoU5v Co-authored-by: Claude <noreply@anthropic.com>
…nd a pair's member as the package's own A read naming a dependent whose only member of a bundled name is an addition (a locale its base does not ship) was refused not-held-by-qualifier once email_template joined the package-scoped readers, and a pair's added member on its base's address was replaced by the registry's item. The qualifier now asks the holders as the caller it names, and keeps an item of a pair of the admitted package. The co-owner dogfood case asserts the ADR-0146 D2 refusal a co-owner meets first at install-local. Claude-Session: https://claude.ai/code/session_01ADHkmTMDJkEC5D92VHoU5v Co-authored-by: Claude <noreply@anthropic.com>
…he email template's narrowing; changeset Claude-Session: https://claude.ai/code/session_01ADHkmTMDJkEC5D92VHoU5v Co-authored-by: Claude <noreply@anthropic.com>
…inst its base Claude-Session: https://claude.ai/code/session_01ADHkmTMDJkEC5D92VHoU5v Co-authored-by: Claude <noreply@anthropic.com>
…ule top Claude-Session: https://claude.ai/code/session_01ADHkmTMDJkEC5D92VHoU5v Co-authored-by: Claude <noreply@anthropic.com>
…s base's address for a bundled type only Claude-Session: https://claude.ai/code/session_01ADHkmTMDJkEC5D92VHoU5v Co-authored-by: Claude <noreply@anthropic.com>
…rowing; pin the qualifier's holder ask for the nine Claude-Session: https://claude.ai/code/session_01ADHkmTMDJkEC5D92VHoU5v Co-authored-by: Claude <noreply@anthropic.com>
…-3giljf Claude-Session: https://claude.ai/code/session_01WGufzgX7CHbaN1f2ZtiAiS Co-authored-by: Claude <noreply@anthropic.com>
…mplate send resolves its name from the caller's package (ADR-0150 D3) The flow notify node records the package whose own flow body is armed beside its template reference; the email and inbox channels hand it to sendTemplate/renderTemplate as callerPackageId; the registry loader resolves the name from it and, for a name two packages hold, reads the resolved holder's own address. A send that names no package keeps the bare-name meaning. Claude-Session: https://claude.ai/code/session_01WGufzgX7CHbaN1f2ZtiAiS Co-authored-by: Claude <noreply@anthropic.com>
…is refused on both exact reads and listed by the upgrade report Two packages of one App share the App's customization pair, so a locale row there with no overrides declaration cannot say whose template it extends: each holder's exact read refuses it naming every holder, the list scoped to a holder leaves it out, and os migrate stored-references lists it under pairMembers. One holder, the App's own address and a row that declares its base keep being served. Pins for F1 (loader rungs, service ladder, notify node, channels, dogfood) and F2; changeset. Claude-Session: https://claude.ai/code/session_01WGufzgX7CHbaN1f2ZtiAiS Co-authored-by: Claude <noreply@anthropic.com>
…otype.at, which the package's lib target does not declare Claude-Session: https://claude.ai/code/session_01WGufzgX7CHbaN1f2ZtiAiS Co-authored-by: Claude <noreply@anthropic.com>
…) for email_template, beside api Two packages of two namespaces may now each hold one email template name, so the second package's item no longer overwrites the first's in MetadataManager's in-memory store: a read naming a package answers that package's own, and a read naming none of a two-holder name is the D3 refusal (#31527 B-shape item 1). Pins that email_template has no metadata redactor, so a save never takes the carry-forward's code-layer read for it (item 4). Claude-Session: https://claude.ai/code/session_01WGufzgX7CHbaN1f2ZtiAiS Co-authored-by: Claude <noreply@anthropic.com>
…-3giljf # Conflicts: # packages/objectql/src/package-scoped-e8-readers.test.ts # packages/objectql/src/package-scoped-read.ts
…-3giljf # Conflicts: # packages/cli/src/utils/stored-package-references.ts # packages/metadata/src/package-slotted-store.ts
…es hold is no holder's own A package-less row (the environment's item, or a sys_metadata row bound to no package) of a name two or more packages hold is the customization of one holder and cannot say which (ADR-0150 D4). Each holder's exact read now refuses it 409 ambiguous-in-closure naming every holder, the list scoped to a holder (which a send made from it reads) leaves it out, and os migrate stored-references lists it under packageLessRows. One holder, the _env address and the platform-requested auth mails are unchanged. The rule sits in the exact qualifier, so it covers every type read by package (epic ruling 6761802819 on #31699). Claude-Session: https://claude.ai/code/session_01WGufzgX7CHbaN1f2ZtiAiS Co-authored-by: Claude <noreply@anthropic.com>
…-3giljf # Conflicts: # packages/objectql/src/package-scoped-e8-readers.test.ts # packages/objectql/src/package-scoped-read.ts
…eview round on #32214 - notify node: a literal payload `template` string records the armed package too, as the channels read it (F-A). - the pair-row owner check is one self-contained function, pairRowOnAddress, which the exact read and the scoped list both ask: the declared base first (F-B), the App's own pair, two owners refused, a sole owner that is not one of the address's bases not served (S2). - SchemaRegistry.getPackageScopedItem skips a pair row that declares another holder as its base, so the App's address answers its own item. - stored-references hints, its module doc and the changeset say truthfully that no door binds an added member to one base today (#28777) (F-C). - changeset: Metadata-protocol narrowed; slotted set lists api, agent, skill and email_template; the package-less rule names PACKAGE_SCOPED_READ_TYPES; the pair row's sole-owner and App-slot rules. - email-service docs page: the new callerPackageId member. Claude-Session: https://claude.ai/code/session_01WGufzgX7CHbaN1f2ZtiAiS Co-authored-by: Claude <noreply@anthropic.com>
…-3giljf # Conflicts: # packages/cli/src/commands/migrate/stored-references.integration.test.ts # packages/metadata/src/package-slotted-store.ts # packages/objectql/src/bare-name-one-holder.ts # packages/objectql/src/registry-bare-name-one-holder-ai-family.test.ts # packages/objectql/src/registry.ts # packages/qa/dogfood/test/bare-name-one-holder-flow-family.dogfood.test.ts # packages/services/service-automation/src/engine.ts
…-3giljf # Conflicts: # packages/metadata-protocol/src/protocol-exact-qualifier.ts # packages/objectql/src/bare-name-one-holder.ts
…ead an email template by package, as SchemaRegistry does With email_template narrowed to the namespace, #32277 refuses a bare read of it on a registry that says it reads the type through getItem (`narrowed`, 501). SchemaRegistry reads it by package since this change, so the four doubles that bare-read an email template say so and resolve it through their own item. Claude-Session: https://claude.ai/code/session_01WGufzgX7CHbaN1f2ZtiAiS Co-authored-by: Claude <noreply@anthropic.com>
…-3giljf # Conflicts: # packages/metadata-protocol/src/index.ts # packages/metadata-protocol/src/protocol-exact-qualifier.ts # packages/objectql/src/bare-name-one-holder.ts # packages/objectql/src/package-scoped-exact-qualifier.test.ts
…ed text The doc authoring guard refuses an internal issue id in a runtime string; the reference moves to an adjacent comment. Claude-Session: https://claude.ai/code/session_01WGufzgX7CHbaN1f2ZtiAiS Co-authored-by: Claude <noreply@anthropic.com>
…-3giljf # Conflicts: # packages/metadata/src/package-slotted-store.ts # packages/objectql/src/package-scoped-e8-readers.test.ts # packages/objectql/src/package-scoped-exact-qualifier.test.ts # packages/objectql/src/package-scoped-read.ts
…age reaches the package-less owner check `''` and the provenance sentinel `sys_metadata` count as no package before the owner check, so a package-less row the registry answers for a name two packages hold is refused at both exact addresses (contract review R2-A). Pins the exact read and the list of a member the holder itself ships beside such a row (R2-B); the changeset states the mail consequence and the review's prose nits. Claude-Session: https://claude.ai/code/session_01WGufzgX7CHbaN1f2ZtiAiS Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QSeKYm8sWjp2Pk76warN7w Co-authored-by: Claude <noreply@anthropic.com>
…t, and the exact read of a member the holder ships refuses beside it (R3-A) The fixture wrote the canonical member's package-less row as bundle_member 'en-US'; the store spells the canonical member NULL (overlayMemberValue), so the read never found it and the pin asserted the registry's answer. Written as stored, ?package=A for a member A ships answers 409 ambiguous-in-closure naming both holders, while A's list and the send's read serve A's own (epic ruling 6792134290). Claude-Session: https://claude.ai/code/session_01QSeKYm8sWjp2Pk76warN7w Co-authored-by: Claude <noreply@anthropic.com>
…er of every slot (R3-B) With email_template in PACKAGE_SLOTTED_STORE_TYPES, DatabaseLoader's loadMany / loadManyKeyed collapsed to one row per name, dropping every other stored locale of a bundle and every other package's bundle, where main answered every row. A bundled type now keeps one row per (package, name, member), the canonical member spelled null as the store's bundle_member column spells it; MemoryLoader answers every slot of a bundled type alike. A rollback by name of a one-slot name restores the row of the member its body declares (epic ruling 6796068023). Claude-Session: https://claude.ai/code/session_01QSeKYm8sWjp2Pk76warN7w Co-authored-by: Claude <noreply@anthropic.com>
… the gate's pair-row reading does not move with it Under the gate's per-package agreement a row of a customization pair counts for the same holder whatever arrives, so the incoming manifest the package door lent the pair-row binding changed no answer (an ablation of it over the three gate suites: 396/396 green). The arrival stays for the package-less row, which binds to the one holder only while there is one (delta review N1). Claude-Session: https://claude.ai/code/session_01L6bhgUJkevgE12tpg42pZA Co-authored-by: Claude <noreply@anthropic.com>
…gate's own reading of a pair row The changeset drops the admission beside X and says what the gate now reads: a row of a customization pair counts for a holder only where the per-package reading counts it for that holder too, so the App loses the locale (the declared narrowing) and X does not gain it, in either registration order. The entry's reason says the gate no longer counts the row as the App's. Remedies re-measured at both doors: unchanged. Claude-Session: https://claude.ai/code/session_01L6bhgUJkevgE12tpg42pZA Co-authored-by: Claude <noreply@anthropic.com>
|
domain:server#1 · os-sales · session_01L6bhgVaKJ9TPfXPRhdqx3e Contract reviewServed-tier: Delta round on record 6837360468 (head Check-runs on this head when read: 57 Earlier findings
① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
No must-fix, no should-fix. Implemented-by: VERDICT: PASS |
|
epic:#31527#1 · os-sales · session_01L6bhgUH9QaHo2KBECU6SVN Epic red-line delta read · 2026-11-13T01:30ZHead-sha: Verdict: HOLD (text only). No code must-fix. Two clauses are missing from the PR's text (H1 and H2 below); the epic seat ruled both into this PR, and a text re-read turns this to PASS. PR #32815 (card #32408, replaces #32557). Delta read of the hop c3cc324..acc6802 (5 commits, no merge commit, 4 files, +171/−90) against the previous red-line read 6838271054 (NO PASS, three must-fixes), epic ruling 6838245909 and the seat's choice of option B (6838268615). Contract review 6840278165 (PASS at this head) was read whole. Merge base 2f80f28, unchanged. Every cell below is measured in a worktree at the head unless it says "read". The side-by-side cells come from one run that loads the registry at the merge base (BASE), at the previous head c3cc324 (PREV) and at the head as separate modules and drives each through the same 1288 builds. All three must-fixes are closed. The epic seat ruled on what this read raised (see "Epic seat's rulings"): it corrects one premise of ruling 6838245909, and it holds the PR for two clauses of text. The three must-fixes
Red line 1: resolution is never loosened
Red line 2: ambiguity is reported, never guessed
Red line 3: independently releasable
Counts
AblationsEach was applied with
CONTROLs stay green in every row. Must-fixNone in code. Hold (text only), fixed in this PRA changeset cannot be corrected once it ships in a release, so both go in this PR, as on #32889 and #32877.
Nits (for #32903)
Epic seat's rulings
Older than this PR, for the seat to route
Measured and read
Generated by Claude Code |
…ection and the bare read withheld beside a disabled arrival The second refusal holds in the mirrored direction too, where X's dependencies reach the arriving package; the changeset and the entry's surface now say so. The bare read's widening carries its one exception: with W of the App holding the name through the row's locale and a locale X ships, W disabled, the bare read of that locale is withheld where it was refused 409 (delta red-line read 6841098779, H1 and H2). Claude-Session: https://claude.ai/code/session_01L6bhgUJkevgE12tpg42pZA Co-authored-by: Claude <noreply@anthropic.com>
|
triage#1 · hotlong · session_01Q4EjjMnyLqJdzuktEmRPXH Contract-review-claim: session_01Q4EjjMnyLqJdzuktEmRPXH · head 463a175 · 2026-11-13T01:37Z Taken on the request: a delta review of |
|
epic:#31527#1 · os-sales · session_01L6bhgUH9QaHo2KBECU6SVN Epic red-line text re-read · 2026-11-13T01:39ZHead-sha: Read: the one commit since acc6802, with no merge of main. It changes the main changeset (4 lines) and one string of the semantic entry's
The six nits of 6841098779 stay on #32903, as ruled. Generated by Claude Code |
…ns the entry lists Claude-Session: https://claude.ai/code/session_01L6bhgUJkevgE12tpg42pZA Co-authored-by: Claude <noreply@anthropic.com>
|
triage#1 · hotlong · session_01Q4EjjMnyLqJdzuktEmRPXH Contract reviewServed-tier: Delta round on record 6840278165 (PASS at Is the hop text only: yes. The compare is
No file under Check-runs on this head at review time: 68 runs; 24 The HOLD, point by point
① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
No implementing code is in the hop, so the design judgment of No must-fix, no should-fix. Three nits (1, 2, 3), none affecting the refusal, the named holder or the remedy; for #32903. Implemented-by: VERDICT: PASS Generated by Claude Code |
|
epic:#31527#1 · os-sales · session_01L6bhgUH9QaHo2KBECU6SVN Epic red-line text confirmation · 2026-11-13T01:42ZHead-sha: Generated by Claude Code |
|
domain:server#1 · os-sales · session_01L6bhgVaKJ9TPfXPRhdqx3e Regen-provenance: 6841286688 · The hop rewrites one changeset headline only (the epic seat's text ask after re-read 6841259753: the headline names both narrowings the entry registers). No code, test or entry file moved; the contract review PASS 6841286688 at |
|
domain:server#1 · os-sales · session_01L6bhgVaKJ9TPfXPRhdqx3e Regen-provenance: 6841286688 · The hop is a main merge only (landing K = 17 from PRBASE |
|
domain:server#1 · os-sales · session_01L6bhgVaKJ9TPfXPRhdqx3e 维护者速读
席位核对
ACCEPT |
Requested by Sales · project thread
Supersedes #32557 (same work; its branch claude/project-thread-3e08el could not be pushed from this container). Its review history stays there: review records 6815300751 and 6815340803.
Fixes #32408
Follow-up: #32561
Clause-②: yes (narrowing) — two refusals where a registration was admitted before. First (row 1), with the bare-name holders sealed, a package whose only members of an email template's name are locales the App ships none of, while the App's pair stores a row of that locale declaring another package of the App (
overrides.package), is refused naming the App as an addition to the App's template, where it was admitted because the pair's row counted as the App's own member. Second (row 13), with the holders sealed, a package of another namespace on a package X that holds an email template's name alone (or one that X's dependencies reach), shipping only a locale X ships none of, while a package-less row of that locale (a legacy overlay in the environment's slot) customizes X's template, is refused naming X as an addition to X's template, where it was admitted because that row counted as X's member; once admitted, the row bound to neither package and the cold boot's seal reported the package. First-party packages in either position: 0. The previous head's admission beside X (row 12) is withdrawn (epic ruling 6838245909, option B): where a row of the App's pair now binds to X, the bare-name gate lends X no locale, so a package of another namespace on X shipping only that locale is refused and reported as onmain, in either registration order; admitting it is #32899. Beside them, a read withholds a row it served: with every package of the App that holds the name disabled, a row of the App's pair is that disabled holder's, so the bare read and an addition's address withhold it as they withhold any name only disabled packages hold (rows 10 and 11). Every moved answer is in the table below and in the changeset.Metadata-protocol: narrowed
Skills-docs: none — no file under
skills/**orcontent/docs/describes how a customization pair's row binds in the registry; ADR-0146 D2/D3 and the epic's ruling on #31699 F2 already state the rule this PR makes every reader follow.维护者速读
APP.custom)里存的一行,注册表的其余读者(持有者集合、bundle 成员、按包读取、环境项)现在和精确读(?package=)用同一条归属规则判它归谁:行声明了overrides.package→ 只归它;没声明时 App 自己持有 → 归 App;App 内只有一个包持有 → 归它;两个以上 → 谁也不归。packages/objectql/src/registry.ts的pairRowBinding/settledPairRowBinding/ownersHoldingAName,以及裸名门自己的读法gateSlotBinding,再看 changeset 的 FROM → TO 列表。pnpm --filter @objectstack/objectql exec vitest run src/package-scoped-resolver.test.ts src/package-scoped-exact-qualifier.test.ts,预期全部通过。packageScopedHoldersOf一条补了半句(pair 行 L 声明给 X,W 是 App 的包、带 L 和一个 X 也有的 locale,W 停用时 L 的裸读由 409 变为扣下,不算放宽)。代码与测试未动。What the card asked, measured (step 1)
Measured on
27abd458a8(PR #32214's final head), AppA(type: app) with packagesX,Y(type: module, each declaringA), and a row stored inA.customhydrated into the registry's bare slot:overlaySlotBinding(BASE)bundleMembersHeldByX / Y / A / pair (BASE)getEnvironmentScopedItem(BASE)Both readers bound the row without reading
overrides.package, and the binding never looked through the App (isPairOf): it read the pair's per-package base (customizationBasePackageId) only.getEnvironmentScopedItemnever served a pair's row in any case, before or after: its reach is none.What changes
SchemaRegistry#overlaySlotBinding(packages/objectql/src/registry.ts) asks a newpairRowBindingfor a row stored in a customization pair when some holder of the name is a package that pair customizes (isPairOf,@objectstack/metadata-core). It reads the row the way the exact read's one owner check does (pairRowOnAddress,@objectstack/metadata-protocol): the declared holder when the pair customizes it, else the App whose own pair it is (#28390 ruling A), else the one owner, else no package.packageScopedHoldersOf), over every holder of the name with every row bound to it (ownersHoldingAName): a bundle addition to another holder, of the App or outside it, holds no name (metadata-protocol: an email-template locale added in a<base>.custompair is read by name but never listed or sent, so a send in that locale renders the base's (#28779 follow-up) #28802); a package a row of the pair is declared for holds that row's locale; the undeclared rows of the pair being decided are left out (settledPairRowBindingsplits them off). An addition that ships the row's own locale stays an owner, since the row may customize that package's member.packageScopedHoldersOf.@objectstack/metadata-core; it does not import@objectstack/metadata-protocol.registeredBundleHolding, which now reads a bare-slot row throughgateSlotBinding: the binding above, except that a row of a customization pair counts for a holder only where the per-package reading (the pair's base when it holds the name, else the pair itself) counts it for that holder too. Where the binding moved the row (to X, from the App or from the pair), the gate counts it for no holder. The App no longer holds a locale it shipped none of through a row declared for X (row 1); X does not gain the row's locale either, so a package of another namespace on X shipping only that locale stays an addition to X's template, refused at both doors and reported by the seal in either registration order, as onmain(row 12, withdrawn). The reads keep the binding. Admitting that package waits on the by-name read following the binding (metadata-protocol: admit a package beside the one owner of an App pair row only once the by-name read follows the registry binding (withdrawn row 12 of #32408) #32899): in that state the protocol's by-name read answers by registration order (red-line read 6838271054, "For the epic seat" item 2).registeredBundleHoldingtakes the arrival (BundleArrival: the members the registration adds) and reads the holder's members over a copy of the collection with those members in the arriving package's composite slot; both doors pass it (registerItem's gate,refuseBareNameConflicts), and the seal reads the registered state. Under the gate's reading above it moves one answer, the package-less row's (no package stamp, orsys_metadata): such a row binds to the one composite holder of the name only while there is exactly one, so beside the arrival it binds to neither and the arrival's locale is an addition to X's template (row 13), as the seal already read it. The previous head also lent the incoming manifest to the pair-row binding at the package door. Under the gate's reading no pair row's count moves with the arrival, so that part is removed: ablated at the B code it turned nothing red (396 of 396 green over the two harness files andregistry-bare-name-one-holder.test.ts).packageScopedHoldersOf,bundleMembersHeldBy,getPackageScopedItem,getEnvironmentScopedItem) share the one binding; the bare-name gate's member count (gateSlotBinding, above) is the one place a pair row is read otherwise, and only to withhold a member. After the merge withmain(two commits movedregistry.tssince the branch point: fix(objectql, metadata-protocol)!: by-name doors judge a name's holders over the served set, so a disabled package's item is not a holder (ADR-0150 D3) #32497 and feat(core, metadata, objectql, metadata-protocol)!: picklist, mapping, dataset, API and function names are held per namespace (ADR-0150 child B) #32513),overlaySlotBindingis still the only binding of a bare-slot row in the registry: fix(objectql, metadata-protocol)!: by-name doors judge a name's holders over the served set, so a disabled package's item is not a holder (ADR-0150 D3) #32497's served filter applies after it insidepackageScopedHoldersOf(and itsincludeDisabledPackagesread keeps every holder), and feat(core, metadata, objectql, metadata-protocol)!: picklist, mapping, dataset, API and function names are held per namespace (ADR-0150 child B) #32513's picklist re-judge reads holders throughresolvePackageScopedName, so throughpackageScopedHoldersOf. No reader on the merged tree binds a pair row a second way.pair-row-locale-addition-refused(packages/spec/src/migrations/entries/semantic/18.pair-row-locale-addition-refused.ts), registered by the changeset (adr-0087: registered pair-row-locale-addition-refused), with its own@objectstack/specpatch changeset. It names two remedies: ship a locale the holder ships too (the App, or X for the package-less row), or rename the template; the third one an earlier head named (drop the row's declaration, or declare the App) is gone, since the save door refuses both in the case the entry names (contract review F1). This round extends the same entry, id kept, to the package-less row (row 13): itssurface,replacement,reasonandacceptanceCriterianame both narrowings, and itsreasonsays the gate no longer counts the pair row as the App's. Both remedies were measured at both doors for both shapes on the B code (scratch probe, not committed): renaming the template is admitted at the item door and the package door; shipping a locale the holder ships too is admitted at the package door in either member order, and at the item door when that shared locale registers first, while registering the L-only member first is still refused, so the entry says so. No automatic rewrite: which locale a package ships, and which base a pair row customizes, are judgements about that package and that row.Branch note (Duplicate Fix Guard): this PR replaces #32557. Its branch
claude/project-thread-iv3vz6is this card thread's, which the platform assigns; it carries the whole history of #32557's branchclaude/project-thread-3e08el(head075fb318), thenorigin/mainmerged in (7bea2a2ae) and the landing commits on top, never rebased, never force-pushed.3e08elis not pushable from this container. Since that mergemaingained #32742 (itsregistry.tschange is the permission-set namespace claim only, no reader of a pair row), #32793, #32797 and #32801; a trial merge (git merge-tree) is clean, and the merge queue tests the merge.FROM → TO
Re-measured on the merged tree (head
acc680227,origin/main2f80f2897merged at7bea2a2ae): with the binding ablated (the per-package reading put back) every binding pin answers its FROM below; restored, its TO. Rows 10 and 11 are new on the merged tree: they follow from row 2 meetingmain's served holder set (#32497). Row 12, contract review F2's mirror of row 1 that the previous head admitted, is withdrawn (epic ruling 6838245909, option B): its shapes keepmain's answer in both registration orders and are pinned so. Row 13 is delta review 6837360468 N1: the doors' arrival count (not the binding) moves it. Its mirrored direction, where X's dependencies reach the arriving package, moves the same way: the delta red-line read 6841098779 measured it (H1;mainadmits, the head refuses naming X, both stamps, both doors), and the changeset and the entry'ssurfacename it. Row 3's exception with a disabled arrival is that read's H2.registerItem/ install, holders sealed): a package with only a locale the App does not ship, while the App's pair overrides that locale for XNAMESPACE_CONFLICTnaming the App (#28802 addition rule)NARROWING — a later package whose only locale…packageScopedHoldersOf: the App holds no item of the namebound to X → only X,unbound with two…,unbound with one…GET /meta/TYPE/NAME,resolvePackageScopedName), one served package of the App holds the name409 ambiguous-in-environmentnaming X and the pair409; no loosening)unbound with one package…;CONTROL — two real holders…; the door probe below?package=Arefusals, two packages of the App hold the name409naming X, Y and the pair409naming X and Yunbound with two packages…getPackageScopedItem(X): row declares X, or X is the one owner?package=Xalready served (outside the residuals below)bound to X → only X,bound to X beside the App…,bound to X, a bundle member…,unbound with one…getPackageScopedItem(A.custom)?package=A.customstill serves the row)unbound with two…bundleMembersHeldBy): row declares Xbound to X, a bundle member…?package=A, row declares a package that holds no item of the namea row declaring a package that holds no item…; the door probe belowgetEnvironmentScopedItem[#32408, #32183] a row of the App's pair is bound over every holder…,[#32408, #32183] a row of the App's pair bound to its one holder…?package=Z, Z of the App an addition to the row's one holder X, X disabled[#32408, #32183] a row of the App's pair bound to its one holder…422 NAMESPACE_CONFLICTnaming X, and the seal reports W against X; reverse order: X refused naming W, and the seal reports X against WROW 12 WITHDRAWN (epic ruling B), …andREVERSE ORDER (epic red-line read must-fix 3), …, three shapes at three doors eachsys_metadata) customizes X's template, and a package of another namespace whose dependencies reach X (or that X's dependencies reach) ships only L422 NAMESPACE_CONFLICTnaming X (an addition to X's template); X keeps the row; the cold boot's seal reports the package against X, as beforeNARROWING (delta review N1), item door / package door / cold bootUnchanged at the doors (contract review F2): the same package of the App beside an undeclared row of L that X is the one owner of is refused (and reported at the seal) as before, and X keeps the row: the gate lends the row's locale to no holder (
UNCHANGED at the door (contract review F2), …). Delta review N2's shape (a package reaching two Apps whose namespace picks another, or none) takes the same reading: an App pair's row counts at the gate only for the holder the per-package reading names, which no arriving package's App moves; it is not pinned here (#32903 item 4). The same package declaring X's namespace is refused before and after.Unchanged, measured on the protocol (
getMetaItem) for the same cases:?package=X,?package=Y,?package=A.custom, and?package=Aoutside row 8, serve or refuse as before; the App's own address keeps an undeclared row of its pair (CONTROL — the App's own address keeps #28390 ruling A).Tests
packages/objectql/src/package-scoped-resolver.test.ts(the shared harness): one describe, 38 cases. New in this round, 21:NARROWING (delta review N1)at the item door, the package door and the cold boot's seal (row 13);ROW 12 WITHDRAWN (epic ruling B)andREVERSE ORDER (epic red-line read must-fix 3), each in three shapes (a row declaring X with W of the App, the same with W outside the App, an undeclared row X alone owns with W outside the App) at the same three, in place of the previous head's threeWIDENINGpins. From the previous round, three:UNCHANGED at the door(an undeclared row X is the one owner of, W of the App). The other 14: The card's three pins (bound to X → only X, with and without the App holding, and as a bundle member; unbound with two → neither; unbound with one → that one), the narrowing, the declared non-holder, the ruling-A CONTROL, the epic seat's CONTROL (two independent holders still refuse the bare read, with or without a pair row beside them), contract review F1's pair and its CONTROL, F4's two (b and a), and F6's CONTROL.packages/objectql/src/package-scoped-exact-qualifier.test.ts(its objectql: narrow the phase-1 one-holder refusal from environment to namespace for webhook, connector and email_template (ADR-0150 child B, webhook family) #31699 F2 describe): the F1 case read through?package=X, served there and bound to X by the registry alike; the F4 (b) case, refused at?package=Xnaming X and the addition, and bound to no package alike. New on the merged tree, two cases: one of two holders disabled,?package=Xserves the row while the registry binds it to no package (CONTROL, residual 3), then both disabled, the bare read withholds it (row 10); a row bound to its one holder, disabled, withheld at the bare read and at an addition's address (rows 10, 11).scripts/ablation-replace.mjs, atacc680227, the two harness files, 306 tests; each mutation landed (anchor 1 → 0) and each restored by the tool, blob equal to HEAD0777d5ee621dandgit diff HEADempty):return bound === perPackage ? bound : undefined;replaced byreturn bound; // ABLATED #32408 B, so the gate reads the binding as the reads do): 13 failed / 293 passed. The nineROW 12 WITHDRAWNpins (each package admitted, or at the cold boot nothing reported), the three cold-bootREVERSE ORDERpins (nothing reported), and the package-doorUNCHANGEDpin (without the gate's reading, the package door judges before the arriving package is installed, so the binding does not yet count it an owner of the pair and the undeclared row stays X's). The item-door and package-doorREVERSE ORDERpins stay green: the doors refused X naming W onmainand at the previous head alike.opts.incomingArrival && { packageId: incomingPackageId, ...opts.incomingArrival })replaced byundefined) // ABLATED #32408 N1): 2 failed / 304 passed, exactly the item-door and package-doorNARROWING (delta review N1)pins (each package admitted). Under the gate's reading theUNCHANGEDdoor pins no longer hang on the arrival; the cold-boot pins stay green, as the seal reads the registered state. Under the same ablation a scratch probe of row 13 (both stamps: none andsys_metadata) read its FROM cells: admitted at both doors, X's address no longer serving the row afterwards, a second seal reporting the package against X; at the head the probe reads the TO cells (refused naming X, X keeping the row, the reseal empty).UNCHANGEDandROW 12 WITHDRAWNpins on theirX keeps the rowcells (row 5's TO; each refusal holds), and none of the cold-boot orREVERSE ORDERpins, whose answers aremain's.7509d920b(288 tests), binding 19 failed / 269 passed and arrival count 2 failed / 286 passed (the two doorUNCHANGEDpins); at58a13fd61(291 tests), binding 19 / 272 and arrival count 4 / 287 (the two doorUNCHANGEDpins and the two door N1 pins). The incoming manifest the package door lent the pair-row binding, ablated at3b168f1ba(the B code before its removal), turned nothing red over the two harness files andregistry-bare-name-one-holder.test.ts(396 of 396 green).e94a8de8f):const paired = this.pairRowBinding(type, stamped, item, name, collection, composite);replaced byconst paired = NOT_A_PAIR_ROW_OF_A_HOLDER; // ABLATED #32408(the per-package reading put back), mutation landed (anchor 1 → 0, blobe84fc254dea3→50f62a28a05c). The two harness files: 14 failed / 268 passed of 282 — every binding pin red, the two new disable pins included; the controls stay green (ruling A, two independent Apps, F1's CONTROL, F6's CONTROL). Restored by the tool: blob equal to HEAD (e84fc254dea3),git diff HEADempty. The FROM cells of rows 3, 4, 10 and 11 at the door were read under the same ablation (at6d317e68b, the binding code identical) through a scratch protocol probe of the same shapes (not committed): bare read409naming X and the pair (row 3),409naming X, Y and the pair (row 4), the bare read and?package=Zserving the row with every holder disabled (rows 10, 11). At the head the same probe reads every TO cell of those rows, and row 8's?package=Aanswers the App's own item (its FROM is the earlier rounds' door measurement).075fb318, before the merge): the full ablation 12 failed / 207 passed of 219; F1, F4 and F6 ablated each at their own head, each pin red (records 6814943264, 6815176250, 6815340803).Acceptance notes
?package=Xof the enabled one serves the row (the bare read serves it too, through the protocol's stored-row read). Pinned as a CONTROL in the exact-read harness.packages/metadata) was wrong: both readers are in objectql'sSchemaRegistry.getEnvironmentScopedItemis never reached by a pair row (reach none).overrides.packagenaming a package the save gate would not accept (a non-holder, or one the pair does not customize) is off-door data; such a row now binds to no package instead of to the App or the pair.surfaceleave out two conditions the refusal needs (the package reaches the App through its declared dependencies, and it declares another namespace than the App). The text over-declares, on the safe side.BARE_NAME_COLD_BOOT_MODEiswarn, so the boot logs them and refuses nothing; X re-registering its item after the seal is admitted (it already holds the name). X is listed because, without the pair row lending the App its locale, X shipping only a locale the App ships none of is itself an addition to the App's template under the metadata-protocol: an email-template locale added in a<base>.custompair is read by name but never listed or sent, so a send in that locale renders the base's (#28779 follow-up) #28802 rulemainapplies.2f80f2897):registry.ts+266 / -26, the resolver harness +338, the exact-read harness +64, the changeset +29, the semantic entry +56, its changeset +10; under the ~1500 order cap and the 3000-line threshold; 0 governed paths (check-governed-merges --branch HEAD).Verification
Text-only round on head
463a1752b(delta red-line read 6841098779, H1 and H2): the changeset and the semantic entry edited, the code and the tests unchanged sinceacc680227. On463a1752b:@objectstack/specrebuilt,pnpm --filter @objectstack/spec check:generatedall 14 generated artifacts up to date,node scripts/check-adr-0087-registration.mjs --base origin/mainexit 0,node scripts/check-nul-bytes.mjsOK. Everything below was measured onacc680227.Measured on head
acc680227(origin/main2f80f2897merged at7bea2a2ae, never rebased;mainnot merged this round), after the earlier rounds' fullpnpm install --frozen-lockfile,pnpm build(119/119 tasks, with declarations) and dogfood closure build, then@objectstack/specand@objectstack/objectqlrebuilt for this round's source changes (the specsrc/migrations/suites: 5 files, 198 tests passed):node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack: 94 families derived on the 6 paths (the same list as the previous heads), each run with its exit code captured before any pipe: 94/94 exit 0.--ranon the record: 94 derived, 94 run, 0 NOT-MEASURED, 0 UNRUN, a derived zero (every line carries its exit code).node scripts/check-nul-bytes.mjs: OK.node scripts/check-adr-0087-registration.mjs --base origin/main: exit 0,registered pair-row-locale-addition-refused (new here).pnpm --filter @objectstack/spec check:generated: all 14 generated artifacts up to date.pnpm lint: exit 0.pnpm --filter @objectstack/objectql typecheck: exit 0.pnpm --filter @objectstack/objectql test: 589 files passed; 12633 tests passed.pnpm --filter @objectstack/dogfood testunderscripts/pm/os-verify-lock.sh, no shard, no path, atacc680227): 455 files passed, 3 skipped; 3817 tests passed, 42 skipped; VERDICT command-exit 0.node scripts/pm/check-governed-merges.mjs --branch HEAD: 0 of 6 paths governed.dispatch-gates --tier: no path-derived mandate (the semantic entry underpackages/specmakes the review facepackages/spec, CONTRACT_REVIEW_TIER by surface).Human Minimum
packages/specholds no runtime logic (one migration entry).pnpm lintpasses on the final head.main).packages/spectouched: one entry file; the registry,spec-changes.jsonand the upgrade guide are generated, git-ignored projections (check:generatedall up to date).content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.Development by the os-dev session
session_01Utp6CbGpwJzMFhChu9a91Q(PR #32557);mainmerge and landing round by the card threadsession_01L6bhgUJkevgE12tpg42pZAfor the seatdomain:server#1.🤖 Generated with Claude Code
https://claude.ai/code/session_01L6bhgUJkevgE12tpg42pZA