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
[finding] composeStacks still raises a bare TypeError in collectComposedActionKeyCollisions on a malformed actions (a null entry, or an object whose actions is a non-array) — the collision pass reads before any shape guard #19816
Filing gate: ① (a product defect with a named site and a repro). Reader: the domain:spec seat that dispatches it — the fix site is collectComposedActionKeyCollisions in packages/spec/src/stack.zod.ts. ⛔ Filed bare for triage: no domain:*, no priority:*, no type asserted.
Filed by domain:spec seat 4 (session_01VWsFyWDp8Rjb2Ma6a3Cyo8, seat post #18917) from the os-dev round implementing #19799 (PR #19815), class (a). ⚠️ The repro is the implementing round's (built dist at PR #19815's head 1a10885247), ⛔ not re-run by the seat.
Repro — composeStacks over two stacks, one malformed (hand-built), one valid
malformed input
result
top-level actions: [null]
TypeError: Cannot read properties of null (reading 'objectName')
an object's actions: 5
TypeError: (obj.actions ?? []).entries is not a function
an object's actions: [null]
TypeError reading name
code / status undefined — outside the ADR-0112 envelope.
Site — re-read by the seat at origin/main
packages/spec/src/stack.zod.ts:4225collectComposedActionKeyCollisions: :4247 iterates (declared as Action[]).entries() and :4253 reads action.objectName / action.name with no shape guard; the same function walks each object's actions the same way. The pass runs in composeStacks before step 7 (where #19799's mergeActionsIntoObjects guard would refuse the same input), so the collision pass crashes first.
Contract
Ruling 5690859601 (#18239): 「an ADR-0112 envelope error (closed error.code, status), ⛔ not a bare TypeError」; the family's landed refusals (#19783 / #19794 / #19798 / #19815) refuse these shapes with STACK_SCHEMA_INVALID / 422 at the strict door's paths.
Dedupe
REST list of the 437 most recently updated issues (open + closed) grepped locally: collectComposedActionKeyCollisions 1 (#16348, closed — the conflict-refusal envelopes, not this shape crash) · entries is not a function 0 · composeStacks … actions 1 (#19799, the sibling fixed at mergeActionsIntoObjects).
Dedupe words: collectComposedActionKeyCollisions TypeError · composeStacks object actions non-array · entries is not a function · composeStacks actions null entry
Claim: PM loop — composeStacks' action-key collision pass (collectComposedActionKeyCollisions) crashes with a bare TypeError on a malformed actions element, outside the ADR-0112 envelope, dispatched at 2026-09-23T14:48Z
Session: session_013RDBh5DqXd2xnLwvHLgLFr
Branch: claude/issue-19816-compose-action-collision-envelope
Worktree: objectstack-issue-19816
Domain: domain:spec
Seat: domain:spec#1
File surface: packages/spec/src/stack.zod.ts, region only: collectComposedActionKeyCollisions and its tests; .changeset/. ⛔ Every other region of stack.zod.ts is out, including #19815's step-7 mergeActionsIntoObjects guard, which is read, not edited.
Container & model: S, mode:subagent, model: opus (default judgment tier)
Clause-②: no
Thread-read: 5792328149
Serial constraints cleared: census over all 22 open PRs at 2026-09-23T14:48Z (254 file rows). packages/spec/src/stack.zod.ts is CLEAR: no open PR touches it, which is the serial condition triage set. The function sits at origin/mainstack.zod.ts by symbol, after PR #19815 landed.
Why this qualifies while product P0/P1s are open
A malformed stack gets a bare TypeError with no code and no status, instead of the platform's error envelope. The strict doors refuse these shapes with STACK_SCHEMA_INVALID / 422; composeStacks should not answer the same input with an uncoded crash. Triage graded it p3 Bug. It is a product defect in a published API, ⛔ not tooling or contract hygiene.
os-dev-report
{
"issue": 19816,
"status": "done",
"branch": "claude/issue-19816-compose-action-collision-envelope",
"pr": "#19903",
"session": "session_013RDBh5DqXd2xnLwvHLgLFr",
"premise_still_valid": true,
"summary": "Route (a). collectComposedActionKeyCollisions (composeStacks step 6, packages/spec/src/stack.zod.ts) now skips a non-object top-level actions entry, a composed object's non-array actions, and a non-object entry inside one. It throws nothing new, and step 7's existing mergeActionsIntoObjects guard gives the one STACK_SCHEMA_INVALID / 422 refusal for every such input. Measured first at origin/main 1f89ba0 with tsx over src. The card's 3 rows reproduced, plus top-level [undefined], [valid, null], and an object actions of 'abc', {} or true, all bare TypeErrors with code/status undefined. The pass also mis-coded two stacks that each carried a readable non-object entry (['x']): it keyed them global:undefined and refused them as STACK_COMPOSE_ACTION_KEY_COLLISION. Bypass probe: with step 6 neutralised in-tree (ablation-replace in WRAP mode, restore proven: blob == HEAD, git diff HEAD empty), step 7 alone answered EVERY malformed row with the envelope. So (a) keeps one wording for one condition. The fixed tree matches the bypass probe byte-for-byte on every malformed row; only the real-collision control differs, as intended. Trade-off stated in the PR: a top-level issue path carries the composed-artifact index, not the input stack's local index. Only the collectComposedActionKeyCollisions region, one new test file and a changeset changed; step 7's guard and message were read, not edited. The assignee field was os-support-ai at pickup, set by the PM dispatch; the newest Claim (5797020861) names this branch. origin/main moved 8 commits after the base (to 44ce049), none touching stack.zod.ts or the compose/define-stack tests, so no merge was needed. A size/m label on the PR was set by another actor, not by me. Cleanup: after this comment is posted, the worktree's node_modules is removed and the worktree is removed with a plain git worktree remove (no --force); the handback says whether that held.",
"tests": "All at head 91bc309, every heavy run through os-verify-lock (VERDICT command-exit 0 each). New file packages/spec/src/compose-stacks-action-collision-shape.test.ts: 19/19 pass. It asserts not-a-TypeError, code STACK_SCHEMA_INVALID, status 422, exact zod issue paths with invalid_type/expected, and that the message names the location (the wording is not pinned). Controls: a valid pair composes, a real collision is still STACK_COMPOSE_ACTION_KEY_COLLISION, and the walk continues past a skipped entry (stack.actions[1], objects['b_item'].actions[1]). Firing control, fix committed first: BASE's stack.zod.ts was restored into the working tree only via git restore --source=BASE, with guard-line counts 0/0 on disk, under a trap restoring with git checkout HEAD -- ABS. Result: 13 failed / 6 passed, vitest exit 1. The 13 red are every crash row, the global:undefined misdiagnosis and both walk-continues controls; the 6 green already reached step 7 on main or are the two controls. Restore proven: blob 057d3ff8b1 == HEAD, git diff HEAD empty, porcelain empty. No build is needed for this leg: spec's vitest imports ./stack.zod from src. @objectstack/spec: build exit 0 (dts 34/34, tree clean); --project local 527 files / 15520 passed (1 todo); --project repo 35 / 602 passed; typecheck exit 0, with the new file inside tsconfig.test.json (listFilesOnly 1). Downstream consumers of spec whose tests call composeStacks (git grep), run after a full turbo build (72/72 tasks): metadata 53/792; plugin-security 120/2289; plugin-dev 8/76; runtime local 272/3800 (1 skipped) and repo 2/69; cli --project unit 222/3141, plus the 11 per-PR composeStacks files across both tiers 11/118; dogfood multi-package-artifact.dogfood.test.ts 1/9. Declared narrowings: the rest of the cli integration tier and of the dogfood suite are left to CI. The 2 cli *.e2e composeStacks files are nightly-tier (OS_TEST_TIERS), outside the per-PR population, NOT MEASURED locally. Gates: dispatch-gates --commands derived 82 at 91bc309 and all 82 ran. 4 exited 3 on the first pass (check:doc-formula-expressions, check:dual-build-cjs-loads, check:lean-entry-closure, check:type-check-debt); after the full build all 4 exited 0. --ran verdict: '82 derived famil(ies) accounted for — 82 run, 0 NOT-MEASURED (a DERIVED zero ...)', exit 0. ESLint narrowing: --no-inline-config --format json over the 2 changed .ts files gives 2 files / 0 errors / 0 warnings, neither ignored. eslint.config.mjs enables no type-aware linting, so untouched files' verdicts cannot move. CI status at report time: in_progress / not read (no CI wait by contract).",
"mcp_calls": "0",
"api_writes": "3 git pushes (the empty-branch probe, the fix commit and the changeset commit), each through write-pace --run. 1 POST /repos/objectstack-ai/objectstack/dispatches (fleet-write relay run 35892771388, op pr_create: POST /repos/objectstack-ai/objectstack/pulls, draft) opened PR #19903. 1 POST /repos//issues/19816/comments (this os-dev-report, via post-stamped). 0 label writes, 0 PATCH. Reads were plain REST GETs (issue, comments, pulls list, PR readback).",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none (no carrier) · noted in PR Acceptance notes only, not filed · step 7's message spells an undefined entry as '#1 (a undefined)' (mergeActionsIntoObjects, a ${typeof entry}); cosmetic, and the region is fenced off by this card",
"carrier: none (no carrier) · noted, not filed · a record action entry with no string name ({}) still keys global:undefined in the collision pass: two stacks each with actions [{}] are refused as STACK_COMPOSE_ACTION_KEY_COLLISION and a single one composes. composeStacks does not schema-validate action records, and skipping name-less records would widen a refusal into an acceptance. Still an envelope, so outside the ADR-0112 ruling's TypeError clause",
"carrier: none (no carrier) · noted, not filed · a Symbol name/objectName still throws a bare TypeError ('Cannot convert a Symbol value to a string') from the pass's key template. JSON, YAML and type-checked TS cannot produce it, so it has no authoring path to reach it"
]
}
Landing prechecks, re-read immediately before the flip: head unchanged; every check-run completed success or skipped by design; check-governed-merges.mjs reads 0 governed paths; the PR is under 5,000 lines; mergeable_state is clean.
Reviewer note carried forward (non-blocking): the changeset sentence "nothing that used to be refused is accepted" is exact for authorable input only; an array with a hole is not authorable from JSON, YAML or defineStack.
domain:spec seat 4 (session_019c3Hi6ZMU1p6m6aA6Bz45d), landing record for the landing done on the maintainer's instruction (provenance in this seat's landing comment above).
The card closed completed through Fixes #19816. The squash 92825780dc has one parent and is an ancestor of origin/main.
Mis-close check: of the cards closed since 2026-09-24T06:05Z, each was closed by its own PR; none by a stray keyword.
pm:dispatched removed. The assignee and the claim belong to the claiming seat and are left untouched.
Activity
objectstack-fleet commentedon Sep 23, 2026
定级
pm:queue·priority:p3·domain:spec·area:devpath—— #19799 同族,崩在更早的一步分诊席(
session_01Tw7jnJinGHvoGSi8aFkhPJ),2026-09-23T09:25Z。立卡门 ①,类 (a)。本席读完了卡面(本卡尚无评论)。本席的读数(
origin/mainc1dfa5241b,已包含 PR #19815)packages/spec/src/stack.zod.ts:4282collectComposedActionKeyCollisions(行号比卡面晚了约 57 行,是 #19815 落地后的位置):actions:只挡了「不是数组」,数组里的null元素照样走到action.objectName || …⇒ 裸TypeError;actions:(obj.actions ?? []).entries()没有任何形状检查 ⇒actions: 5抛… .entries is not a function,[null]读name时抛。与卡面三行复现一致,且落在 ADR-0112 信封之外(裁定
5690859601)。判定
p3:与 [finding]defineStack(config, { strict: false })still raises a bare TypeError for a non-arrayactions(top-level or on an object) —sortActionsByOrdercalls.someunguarded; the sibling of #19785 one key over #19799 / [finding]defineStack(config, { strict: false })raises a bare TypeError frommergeActionsIntoObjectson a non-arrayobjectsor a null entry — outside the ADR-0112 envelope, one door before #18239's fix #19785 同级、同可达面 —— 只有手工拼出的畸形输入才会走到;严格入口(fix(spec): composeStacks refuses a non-arrayobjectswith an ADR-0112 envelope #19783 / fix(spec): composeStacks refuses a non-array concatenated collection instead of dropping its content #19794 / fix(spec): defineStack strict:false refuses a non-array objects with an ADR-0112 envelope #19798 / fix(spec): refuse a malformed actions array in the non-strict bound-action merge #19815)已经用STACK_SCHEMA_INVALID/ 422 拒绝这些形状。actions跳过,交给第 7 步 fix(spec): refuse a malformed actions array in the non-strict bound-action merge #19815 已有的拒绝去报信封错误;或在这里直接给同一个信封。二选一由认领人定,但结果必须是信封错误,⛔ 不是裸TypeError。stack.zod.ts,这个文件本周在飞的 PR 很多 —— 认领前读一遍在飞 PR 的文件清单,有碰这个文件的就排在其后,⛔ 不并入。type字段中继设不了。Generated by Claude Code
os-support-ai commentedon Sep 23, 2026
Claim: PM loop —
composeStacks' action-key collision pass (collectComposedActionKeyCollisions) crashes with a bareTypeErroron a malformedactionselement, outside the ADR-0112 envelope, dispatched at 2026-09-23T14:48ZSession:
session_013RDBh5DqXd2xnLwvHLgLFrBranch:
claude/issue-19816-compose-action-collision-envelopeWorktree:
objectstack-issue-19816Domain:
domain:specSeat:
domain:spec#1File surface:
packages/spec/src/stack.zod.ts, region only:collectComposedActionKeyCollisionsand its tests;.changeset/. ⛔ Every other region ofstack.zod.tsis out, including #19815's step-7mergeActionsIntoObjectsguard, which is read, not edited.Container & model:
S,mode:subagent,model: opus (default judgment tier)Clause-②: no
Thread-read: 5792328149
Serial constraints cleared: census over all 22 open PRs at 2026-09-23T14:48Z (254 file rows).
packages/spec/src/stack.zod.tsis CLEAR: no open PR touches it, which is the serial condition triage set. The function sits atorigin/mainstack.zod.tsby symbol, after PR #19815 landed.Why this qualifies while product P0/P1s are open
A malformed stack gets a bare
TypeErrorwith nocodeand nostatus, instead of the platform's error envelope. The strict doors refuse these shapes withSTACK_SCHEMA_INVALID/ 422;composeStacksshould not answer the same input with an uncoded crash. Triage graded it p3 Bug. It is a product defect in a published API, ⛔ not tooling or contract hygiene.Generated by Claude Code
objectstack-fleet commentedon Sep 23, 2026
os-dev-report
{
"issue": 19816,
"status": "done",
"branch": "claude/issue-19816-compose-action-collision-envelope",
"pr": "#19903",
"session": "session_013RDBh5DqXd2xnLwvHLgLFr",
"premise_still_valid": true,
"summary": "Route (a). collectComposedActionKeyCollisions (composeStacks step 6, packages/spec/src/stack.zod.ts) now skips a non-object top-level
actionsentry, a composed object's non-arrayactions, and a non-object entry inside one. It throws nothing new, and step 7's existing mergeActionsIntoObjects guard gives the one STACK_SCHEMA_INVALID / 422 refusal for every such input. Measured first at origin/main 1f89ba0 with tsx over src. The card's 3 rows reproduced, plus top-level [undefined], [valid, null], and an objectactionsof 'abc', {} or true, all bare TypeErrors with code/status undefined. The pass also mis-coded two stacks that each carried a readable non-object entry (['x']): it keyed them global:undefined and refused them as STACK_COMPOSE_ACTION_KEY_COLLISION. Bypass probe: with step 6 neutralised in-tree (ablation-replace in WRAP mode, restore proven: blob == HEAD, git diff HEAD empty), step 7 alone answered EVERY malformed row with the envelope. So (a) keeps one wording for one condition. The fixed tree matches the bypass probe byte-for-byte on every malformed row; only the real-collision control differs, as intended. Trade-off stated in the PR: a top-level issue path carries the composed-artifact index, not the input stack's local index. Only the collectComposedActionKeyCollisions region, one new test file and a changeset changed; step 7's guard and message were read, not edited. The assignee field was os-support-ai at pickup, set by the PM dispatch; the newest Claim (5797020861) names this branch. origin/main moved 8 commits after the base (to 44ce049), none touching stack.zod.ts or the compose/define-stack tests, so no merge was needed. A size/m label on the PR was set by another actor, not by me. Cleanup: after this comment is posted, the worktree's node_modules is removed and the worktree is removed with a plaingit worktree remove(no --force); the handback says whether that held.","tests": "All at head 91bc309, every heavy run through os-verify-lock (VERDICT command-exit 0 each). New file packages/spec/src/compose-stacks-action-collision-shape.test.ts: 19/19 pass. It asserts not-a-TypeError, code STACK_SCHEMA_INVALID, status 422, exact zod issue paths with invalid_type/expected, and that the message names the location (the wording is not pinned). Controls: a valid pair composes, a real collision is still STACK_COMPOSE_ACTION_KEY_COLLISION, and the walk continues past a skipped entry (stack.actions[1], objects['b_item'].actions[1]). Firing control, fix committed first: BASE's stack.zod.ts was restored into the working tree only via
git restore --source=BASE, with guard-line counts 0/0 on disk, under a trap restoring withgit checkout HEAD -- ABS. Result: 13 failed / 6 passed, vitest exit 1. The 13 red are every crash row, the global:undefined misdiagnosis and both walk-continues controls; the 6 green already reached step 7 on main or are the two controls. Restore proven: blob 057d3ff8b1 == HEAD, git diff HEAD empty, porcelain empty. No build is needed for this leg: spec's vitest imports ./stack.zod from src. @objectstack/spec: build exit 0 (dts 34/34, tree clean); --project local 527 files / 15520 passed (1 todo); --project repo 35 / 602 passed; typecheck exit 0, with the new file inside tsconfig.test.json (listFilesOnly 1). Downstream consumers of spec whose tests call composeStacks (git grep), run after a full turbo build (72/72 tasks): metadata 53/792; plugin-security 120/2289; plugin-dev 8/76; runtime local 272/3800 (1 skipped) and repo 2/69; cli --project unit 222/3141, plus the 11 per-PR composeStacks files across both tiers 11/118; dogfood multi-package-artifact.dogfood.test.ts 1/9. Declared narrowings: the rest of the cli integration tier and of the dogfood suite are left to CI. The 2 cli *.e2e composeStacks files are nightly-tier (OS_TEST_TIERS), outside the per-PR population, NOT MEASURED locally. Gates: dispatch-gates --commands derived 82 at 91bc309 and all 82 ran. 4 exited 3 on the first pass (check:doc-formula-expressions, check:dual-build-cjs-loads, check:lean-entry-closure, check:type-check-debt); after the full build all 4 exited 0. --ran verdict: '82 derived famil(ies) accounted for — 82 run, 0 NOT-MEASURED (a DERIVED zero ...)', exit 0. ESLint narrowing: --no-inline-config --format json over the 2 changed .ts files gives 2 files / 0 errors / 0 warnings, neither ignored. eslint.config.mjs enables no type-aware linting, so untouched files' verdicts cannot move. CI status at report time: in_progress / not read (no CI wait by contract).","mcp_calls": "0",
"api_writes": "3 git pushes (the empty-branch probe, the fix commit and the changeset commit), each through write-pace --run. 1 POST /repos/objectstack-ai/objectstack/dispatches (fleet-write relay run 35892771388, op pr_create: POST /repos/objectstack-ai/objectstack/pulls, draft) opened PR #19903. 1 POST /repos//issues/19816/comments (this os-dev-report, via post-stamped). 0 label writes, 0 PATCH. Reads were plain REST GETs (issue, comments, pulls list, PR readback).",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none (no carrier) · noted in PR Acceptance notes only, not filed · step 7's message spells an undefined entry as '#1 (a undefined)' (mergeActionsIntoObjects,
a ${typeof entry}); cosmetic, and the region is fenced off by this card","carrier: none (no carrier) · noted, not filed · a record action entry with no string name ({}) still keys global:undefined in the collision pass: two stacks each with actions [{}] are refused as STACK_COMPOSE_ACTION_KEY_COLLISION and a single one composes. composeStacks does not schema-validate action records, and skipping name-less records would widen a refusal into an acceptance. Still an envelope, so outside the ADR-0112 ruling's TypeError clause",
"carrier: none (no carrier) · noted, not filed · a Symbol
name/objectNamestill throws a bare TypeError ('Cannot convert a Symbol value to a string') from the pass's key template. JSON, YAML and type-checked TS cannot produce it, so it has no authoring path to reach it"]
}
Generated by Claude Code
objectstack-fleet commentedon Sep 24, 2026
Landing — PR #19903 flipped ready + auto-merge by
domain:specseat 4, 2026-09-24T05:43ZDone on the maintainer's instruction, provenance:
session_019c3Hi6ZMU1p6m6aA6Bz45d(domain:spec#4).This does not take over the claim: the claim, the branch and the card stay with the claiming seat, and this seat only lands the PR.
5808346021on PR fix(spec): composeStacks' action-key collision pass skips malformed actions, so step 7 refuses them with the envelope #19903, on the head the PR carries now.successor skipped by design;check-governed-merges.mjsreads 0 governed paths; the PR is under 5,000 lines;mergeable_stateisclean.defineStack.Generated by Claude Code
objectstack-fleet commentedon Sep 24, 2026
Landed — PR #19903 →
92825780dc, 2026-09-24T06:27Zdomain:specseat 4 (session_019c3Hi6ZMU1p6m6aA6Bz45d), landing record for the landing done on the maintainer's instruction (provenance in this seat's landing comment above).completedthroughFixes #19816. The squash92825780dchas one parent and is an ancestor oforigin/main.pm:dispatchedremoved. The assignee and the claim belong to the claiming seat and are left untouched.Generated by Claude Code
fix(spec): composeStacks' action-key collision pass skips malformed a…
test(spec): the first root-level test file group's titles state each …