You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
runtime + spec: composeStacks does not walk a nested multi-package artifact's bodies for action key collisions, so it admits a pair the install gate refuses (ADR-0150 D4) #32335
④ coordination card: a child of the ADR-0150 epic #31527.
Epic: ADR-0150 (#31527). Lane:domain:server (spec composeStacks). Reader: the server seat that claims it. Found by the #32245 dev (PR #32333, out-of-scope finding) and confirmed by reading in the contract review of PR #32333 at 8c9a7da4 (record 6777603795, ③). Pre-existing on main; PR #32333 neither opens nor closes it.
A nested artifact's actions are not walked
composeStacks step 6 (collectComposedActionKeyCollisions, packages/spec/src/stack.zod.ts) walks each input's top-level actions. A manifest: 'preserve' artifact strips its flattened copy and carries its actions only inside packages[] bodies. So composing such an artifact with a third package that declares the same object-less action name (or the same OBJECT:NAME) sees one declaring stack and admits the pair, whatever the namespaces. Installing the same packages one by one is judged by the install gate, which refuses a pair of one namespace. composedPackagesHoldApart refuses a nested artifact only when its own top level declares the key. This is NOT MEASURED; read from the tree.
Do:
Measure on main: compose a preserve artifact A (packages p1, p2) with a third stack q of p1's namespace that declares one of A's object-less action names; record what compose and the boot answer, and what install p1, p2, q one by one answers.
Pins: the one-namespace nested pair is refused; CONTROL a two-namespace nested pair composes; CONTROL a flat input unchanged. Ablate once.
Clause-②: the seat declares the narrowing (FROM compose admits → TO refused) with the first-party census (expect 0: app-multi-package declares no actions). Business reason: without this, an artifact built by composing a composed artifact can carry two same-namespace holders of one action name that install would refuse, and the boot binds one silently. Serial: after #32333 (#32245), which moves the predicate to spec. Epic red-line review before landing.
Dedupe (open, composeStacks nested artifact, collectComposedActionKeyCollisions): none found. Filed by server seat 1.
Question (domain:server#3, mailbox): the fix narrows composeStacks' accept set in packages/spec. Spec lane or server?
Answer: it stays domain:server, and the packages/spec hunk rides the server PR.
lanes/spec.md L12: packages/spec files belong to the spec seat, but a change that is not a metadata-protocol change may ride a product PR.
Narrowing what composeStacks (src/stack.zod.ts) admits, so that compose refuses the pairs install refuses, changes no authorable key or schema shape. The card's runtime half is server.
Claim: PM loop round 2 · #32335 · 2026-11-11T21:14Z · bug (p2)
Session: session_01Utp6CaAUev5V62cLHhQwBi (seat); dev = card thread session_01Utp6CagEpJKupV36gSK4mY
Account: os-bill
Branch: claude/project-thread-wrdkcr (the card thread's harness-pinned branch; platform exception to claude/issue-<n>-<slug>)
Worktree: objectstack-issue-32335
Domain: domain:server
Seat: domain:server#3
File surface: packages/spec/src/stack.zod.ts (composeStacks step 6: collectComposedActionKeyCollisions / composedPackagesHoldApart only) and its tests; packages/runtime/** only if the measurement shows the boot half needs the same read; one changeset (stop on breach; explain in the report)
Container & model: M, mode:thread, model: default tier (dispatch-gates --tier: no path-derived mandate; narrowing ⇒ CONTRACT_REVIEW_TIER review owed before the queue), effort: high
Clause-②: no (narrowing) (FROM compose admits a one-namespace nested pair → TO refused, as install already refuses)
Metadata-protocol: narrowed
Ruling-ref: 6796288046 (triage: stays domain:server; the spec narrowing rides this PR; a new authorable key, schema shape or api-surface export goes to a spec card instead)
Responsibility: platform code (packages/spec composeStacks step 6) admits what the install gate refuses; the install gate is the platform path that already refuses it one package at a time; any author composing a composed artifact reaches it (first-party census expected 0, measured by the dev)
Thread-read: 6796273876
Premise: holds (read at 7d82b2da: step 6 walks each input's top-level actions only; composedPackagesHoldApart returns false for a stack with packages)
Serial constraints cleared: no open PR touches packages/spec/src/stack.zod.ts (open PR file lists read 21:0xZ); #32333 merged
Claim amendment: #32335 · 2026-11-11T21:23Z · seat ruling on step 0 (note: /mnt/project-files/server-seat3-p2/32335/step0.md, read at 14754c70)
Premise holds. A nested stack whose packages declare the same out-of-object action composes clean today, then boots into a 409 on both call paths (preserve manifest) or silently runs only one holder (default manifest). The flat equivalent is already refused at compose.
Fix shape approved as proposed. Two private functions in packages/spec/src/stack.zod.ts: step 6 reads actions per package body for inputs that carry packages[] (the same per-body read boot does), holder = package body id || name + namespace, and composedPackagesHoldApart decides with namespacedHoldersMeet. Flat inputs unchanged; runtime unchanged. No new key, no schema shape change, no api-surface export, so the spec part rides this PR (triage 6796288046).
Declarations: Clause-②: no · Metadata-protocol: narrowed (compose now refuses what it used to pass) · Skills-docs: none · changeset @objectstack/spec minor. Census: 0 (the only caller, app-multi-package, composes two flat inputs with no actions).
Review tier: the narrowing owes a CONTRACT_REVIEW_TIER review before the queue. The PR body states the narrowing, the census, and the before/after for each of the four measured cases. If that review reads the change as BREAKING, it goes to the maintainer before landing.
Closing keyword: Fixes #32335.
{
"issue": 32335,
"status": "done",
"branch": "claude/project-thread-wrdkcr",
"pr": "https://github.com/objectstack-ai/objectstack/pull/32475",
"session": "session_01Utp6CagEpJKupV36gSK4mY",
"premise_still_valid": true,
"summary": "composeStacks step 6 now reads every package body a nested input (one carrying packages[]) declares and judges the object-less pair with each body as holder under namespacedHoldersMeet, so compose refuses what install refuses. Measured on main 14754c70: A(p1 alpha, p2 beta, preserve)+q(alpha) was admitted under both manifests while install refused q NAMESPACE_CONFLICT 422 and the boot answered 409 at both addresses (preserve) or ran q's body silently (default). Now refused 422 STACK_COMPOSE_ACTION_KEY_COLLISION; A+gamma still composes under preserve and is refused naming preserve under the default manifest, as the flat pair is; flat inputs unchanged. Only private functions in packages/spec/src/stack.zod.ts; runtime unchanged; no key, schema shape or export. Step 0 ruled by the seat (6807368544).",
"tests": "at 8be0f346: compose-stacks-action-key-collision.test.ts 41/41 (7 new). spec suite Test Files 836/836, Tests 26655 passed + 1 todo; spec typecheck exit 0; lock VERDICT command-exit 0. Ablation via scripts/ablation-replace.mjs (body-walk guard forced to skip; anchor 1->0, blob 424dab64->c79d323a): 4 failed / 37 passed (both A+q pins, A+gamma default, object-bound body pin red; controls green); restored blob == HEAD, git diff HEAD empty. spec tests import source, no dist leg.",
"gates": "dispatch-gates --commands at 8be0f346: 87 commands, exits captured before pipes; 85 exit 0; node scripts/check-plugin-teardown-shape.mjs --self-test exit 3 PREREQUISITE (shallow clone lacked pinned 621a4876; after fetching it the rerun exits 0, 48 cases); pnpm check:dual-build-cjs-loads exit 3 PREREQUISITE (11 packages outside the closure unbuilt) NOT MEASURED locally, CI Lint & Repo Gates runs it. --ran reconciliation: 87 derived, 85 run, 2 NOT-MEASURED. pnpm lint exit 0. PR check-runs: in_progress at report time.",
"dogfood_battery": "8be0f346 · os-verify-lock: VERDICT command-exit 0 · Test Files 451 passed | 3 skipped (454) · Tests 3775 passed | 42 skipped (3817) · no failures",
"mcp_calls": "0",
"api_writes": "1 relay pr_create (POST /repos/objectstack-ai/objectstack/dispatches, run 43112092252 success, PR #32475) + this report comment via post-stamped.mjs",
"open_questions": [],
"security_findings": "none",
"out_of_scope_findings": [
"class: none (noted, not filed) · read from the tree, NOT MEASURED: mergeObjects walks top-level objects only, so a stripped nested artifact's body objects are not judged against a third input's same-named object · carrier: none · noted in PR Acceptance notes"
],
"line_budget": "264 changed lines (+250/-14), 3 files, under 3000",
"deviations": [
"PR body line order follows the seat's common.md (attribution, signature, 维护者速读, then Fixes) rather than os-dev.md's Fixes-first",
"git fetch --depth=1 of the teardown self-test's pinned commit to clear its shallow-clone prerequisite"
],
"files_changed": [
".changeset/32335-compose-nested-artifact-action-bodies.md",
"packages/spec/src/compose-stacks-action-key-collision.test.ts",
"packages/spec/src/stack.zod.ts"
]
}
Contract review: CONTRACT_REVIEW_TIER PASS, comment 6809232138. The review's out-of-scope finding (the object door misses objects declared inside nested package bodies) is filed as #32477. Landing is held with pm:awaiting-merge behind the seat session's auto-mode classifier refusal (Merge Without Review), waiting for the maintainer's go-ahead or a manual merge.
Activity
objectstack-fleet commentedon Nov 10, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: confirmed ·
domain:server· p2 ·pm:queue→pm:blocked· 2026-11-10T19:56ZBlocked-by: #32245
⛔ Not a claim.
pm:blocking.domain:server(runtimecomposeStacks, nested multi-package bodies). p2 as filed.Generated by Claude Code
objectstack-fleet commentedon Nov 11, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: unblocked →
pm:queue·domain:server· p2 · 2026-11-11T00:05Z⛔ Not a claim.
main.Generated by Claude Code
objectstack-fleet commentedon Nov 11, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: route answered · stays
domain:server· p2 · 2026-11-11T12:49Z⛔ Not a claim.
domain:server#3, mailbox): the fix narrowscomposeStacks' accept set inpackages/spec. Spec lane or server?domain:server, and thepackages/spechunk rides the server PR.lanes/spec.mdL12:packages/specfiles belong to the spec seat, but a change that is not a metadata-protocol change may ride a product PR.composeStacks(src/stack.zod.ts) admits, so that compose refuses the pairs install refuses, changes no authorable key or schema shape. The card's runtime half is server.api-surfaceexports beyond the predicate already moved by fix(spec, runtime)!: composeStacks admits two namespaces' object-less action of one name under manifest 'preserve', and the boot binds each package's actions under its package (ADR-0150 D4) #32333. Then the spec hunk becomes its owndomain:speccard, withMetadata-protocol:declared.Generated by Claude Code
objectstack-fleet commentedon Nov 11, 2026
domain:server#3 · os-bill · session_01Utp6CaAUev5V62cLHhQwBi
Claim: PM loop round 2 · #32335 · 2026-11-11T21:14Z · bug (p2)
Session:
session_01Utp6CaAUev5V62cLHhQwBi(seat); dev = card threadsession_01Utp6CagEpJKupV36gSK4mYAccount:
os-billBranch:
claude/project-thread-wrdkcr(the card thread's harness-pinned branch; platform exception toclaude/issue-<n>-<slug>)Worktree:
objectstack-issue-32335Domain:
domain:serverSeat:
domain:server#3File surface:
packages/spec/src/stack.zod.ts(composeStacks step 6:collectComposedActionKeyCollisions/composedPackagesHoldApartonly) and its tests;packages/runtime/**only if the measurement shows the boot half needs the same read; one changeset (stop on breach; explain in the report)Container & model:
M,mode:thread,model: default tier (dispatch-gates --tier: no path-derived mandate; narrowing ⇒ CONTRACT_REVIEW_TIER review owed before the queue),effort: highClause-②: no (narrowing) (FROM compose admits a one-namespace nested pair → TO refused, as install already refuses)
Metadata-protocol: narrowed
Ruling-ref: 6796288046 (triage: stays domain:server; the spec narrowing rides this PR; a new authorable key, schema shape or api-surface export goes to a spec card instead)
Responsibility: platform code (
packages/speccomposeStacks step 6) admits what the install gate refuses; the install gate is the platform path that already refuses it one package at a time; any author composing a composed artifact reaches it (first-party census expected 0, measured by the dev)Thread-read: 6796273876
Premise: holds (read at
7d82b2da: step 6 walks each input's top-levelactionsonly;composedPackagesHoldApartreturns false for a stack withpackages)Serial constraints cleared: no open PR touches
packages/spec/src/stack.zod.ts(open PR file lists read 21:0xZ); #32333 mergedobjectstack-fleet commentedon Nov 11, 2026
domain:server#3 · os-bill · session_01Utp6CaAUev5V62cLHhQwBi
Claim amendment: #32335 · 2026-11-11T21:23Z · seat ruling on step 0 (note: /mnt/project-files/server-seat3-p2/32335/step0.md, read at
14754c70)Premise holds. A nested stack whose packages declare the same out-of-object action composes clean today, then boots into a 409 on both call paths (preserve manifest) or silently runs only one holder (default manifest). The flat equivalent is already refused at compose.
Fix shape approved as proposed. Two private functions in
packages/spec/src/stack.zod.ts: step 6 reads actions per package body for inputs that carrypackages[](the same per-body read boot does), holder = package bodyid || name+ namespace, andcomposedPackagesHoldApartdecides withnamespacedHoldersMeet. Flat inputs unchanged; runtime unchanged. No new key, no schema shape change, no api-surface export, so the spec part rides this PR (triage 6796288046).Declarations: Clause-②: no · Metadata-protocol: narrowed (compose now refuses what it used to pass) · Skills-docs: none · changeset
@objectstack/specminor. Census: 0 (the only caller, app-multi-package, composes two flat inputs with no actions).Review tier: the narrowing owes a CONTRACT_REVIEW_TIER review before the queue. The PR body states the narrowing, the census, and the before/after for each of the four measured cases. If that review reads the change as BREAKING, it goes to the maintainer before landing.
Closing keyword:
Fixes #32335.test(spec): pin composeStacks' read of a nested artifact's package bo…
chore(changeset): composeStacks nested artifact action bodies (#32335)
objectstack-fleet commentedon Nov 11, 2026
domain:server#3 · os-bill · session_01Utp6CagEpJKupV36gSK4mY
os-dev-report
{ "issue": 32335, "status": "done", "branch": "claude/project-thread-wrdkcr", "pr": "https://github.com/objectstack-ai/objectstack/pull/32475", "session": "session_01Utp6CagEpJKupV36gSK4mY", "premise_still_valid": true, "summary": "composeStacks step 6 now reads every package body a nested input (one carrying packages[]) declares and judges the object-less pair with each body as holder under namespacedHoldersMeet, so compose refuses what install refuses. Measured on main 14754c70: A(p1 alpha, p2 beta, preserve)+q(alpha) was admitted under both manifests while install refused q NAMESPACE_CONFLICT 422 and the boot answered 409 at both addresses (preserve) or ran q's body silently (default). Now refused 422 STACK_COMPOSE_ACTION_KEY_COLLISION; A+gamma still composes under preserve and is refused naming preserve under the default manifest, as the flat pair is; flat inputs unchanged. Only private functions in packages/spec/src/stack.zod.ts; runtime unchanged; no key, schema shape or export. Step 0 ruled by the seat (6807368544).", "tests": "at 8be0f346: compose-stacks-action-key-collision.test.ts 41/41 (7 new). spec suite Test Files 836/836, Tests 26655 passed + 1 todo; spec typecheck exit 0; lock VERDICT command-exit 0. Ablation via scripts/ablation-replace.mjs (body-walk guard forced to skip; anchor 1->0, blob 424dab64->c79d323a): 4 failed / 37 passed (both A+q pins, A+gamma default, object-bound body pin red; controls green); restored blob == HEAD, git diff HEAD empty. spec tests import source, no dist leg.", "gates": "dispatch-gates --commands at 8be0f346: 87 commands, exits captured before pipes; 85 exit 0; node scripts/check-plugin-teardown-shape.mjs --self-test exit 3 PREREQUISITE (shallow clone lacked pinned 621a4876; after fetching it the rerun exits 0, 48 cases); pnpm check:dual-build-cjs-loads exit 3 PREREQUISITE (11 packages outside the closure unbuilt) NOT MEASURED locally, CI Lint & Repo Gates runs it. --ran reconciliation: 87 derived, 85 run, 2 NOT-MEASURED. pnpm lint exit 0. PR check-runs: in_progress at report time.", "dogfood_battery": "8be0f346 · os-verify-lock: VERDICT command-exit 0 · Test Files 451 passed | 3 skipped (454) · Tests 3775 passed | 42 skipped (3817) · no failures", "mcp_calls": "0", "api_writes": "1 relay pr_create (POST /repos/objectstack-ai/objectstack/dispatches, run 43112092252 success, PR #32475) + this report comment via post-stamped.mjs", "open_questions": [], "security_findings": "none", "out_of_scope_findings": [ "class: none (noted, not filed) · read from the tree, NOT MEASURED: mergeObjects walks top-level objects only, so a stripped nested artifact's body objects are not judged against a third input's same-named object · carrier: none · noted in PR Acceptance notes" ], "line_budget": "264 changed lines (+250/-14), 3 files, under 3000", "deviations": [ "PR body line order follows the seat's common.md (attribution, signature, 维护者速读, then Fixes) rather than os-dev.md's Fixes-first", "git fetch --depth=1 of the teardown self-test's pinned commit to clear its shallow-clone prerequisite" ], "files_changed": [ ".changeset/32335-compose-nested-artifact-action-bodies.md", "packages/spec/src/compose-stacks-action-key-collision.test.ts", "packages/spec/src/stack.zod.ts" ] }objectstack-fleet commentedon Nov 11, 2026
domain:server#3 · os-bill · session_01Utp6CaAUev5V62cLHhQwBi
ACCEPT → PR #32475 comment 6809274990 · 2026-11-11T23:06Z · head
8be0f346Contract review: CONTRACT_REVIEW_TIER PASS, comment 6809232138. The review's out-of-scope finding (the object door misses objects declared inside nested package bodies) is filed as #32477. Landing is held with
pm:awaiting-mergebehind the seat session's auto-mode classifier refusal (Merge Without Review), waiting for the maintainer's go-ahead or a manual merge.