Repository navigation
[finding] defineStack(config, { strict: false }) raises a bare TypeError from mergeActionsIntoObjects on a non-array objects or a null entry — outside the ADR-0112 envelope, one door before #18239's fix #19785
Description
Activity
objectstack-fleet commented
on Sep 23, 2026 ContributorAuthorMore actions定级
pm:queue·priority:p3·domain:spec·area:devpath—— #18239 同族、早一道门;与 #19784 不并分诊席(
session_01Tw7jnJinGHvoGSi8aFkhPJ),2026-09-23T04:21Z。立卡门 ①,类 (a)。本席的读数(
origin/main,读代码,⛔ 未重跑卡面的复现)packages/spec/src/stack.zod.ts:2942mergeActionsIntoObjects:守卫只有if (!config.objects || config.objects.length === 0),随后直接config.objects.map(…)(:2965)。⇒objects: 5时(5).length === 0为假、.map不存在 ⇒TypeError;objects: 'abc'同样;[null, obj]在 map 里读null.actions⇒TypeError。三条复现都能从代码直接读出来,且都落在 ADR-0112 的拒绝信封之外(没有code/status)。判定
- 同族异缺陷,⛔ 不并:[finding] composeStacks step 2 (
mergeObjects) iteratesstack.objectsunguarded — an unparsed input with a non-arrayobjectsraises a bare TypeError outside the ADR-0112 envelope; same defect family #18212 just closed forpermissions/data, one step earlier, PRE-EXISTING on main #18239 修的是composeStacks第 2 步(mergeObjects),本卡是defineStack非严格路径更早撞上的mergeActionsIntoObjects;[finding] composeStacks drops a stack's grants and seed rows when itspermissions/datais not an array — the composed artifact is silently incomplete, which ruling 5690859601 forbids (follow-up C of #18239) #19784 是第 3 步的静默丢内容。同一个文件、三种机制、三个函数。 p3:与 [finding] composeStacks step 2 (mergeObjects) iteratesstack.objectsunguarded — an unparsed input with a non-arrayobjectsraises a bare TypeError outside the ADR-0112 envelope; same defect family #18212 just closed forpermissions/data, one step earlier, PRE-EXISTING on main #18239 同级、同可达面(只有strict: false)。- 契约依据:[finding] composeStacks step 2 (
mergeObjects) iteratesstack.objectsunguarded — an unparsed input with a non-arrayobjectsraises a bare TypeError outside the ADR-0112 envelope; same defect family #18212 just closed forpermissions/data, one step earlier, PRE-EXISTING on main #18239 裁决5690859601已把「ADR-0112 信封错误,⛔ 不是裸TypeError」适用到组合路径;本卡把同一句话用到早一道门。⚠️ 卡面留给认领人的第一问 ——「strict: false该拒绝还是跳过」—— 裁决只答了组合路径,本卡的认领人先答它,若答案是「跳过」,那是契约方向,停下回报。
取卡的人要知道
⚠️ 与 [finding] composeStacks step 2 (mergeObjects) iteratesstack.objectsunguarded — an unparsed input with a non-arrayobjectsraises a bare TypeError outside the ADR-0112 envelope; same defect family #18212 just closed forpermissions/data, one step earlier, PRE-EXISTING on main #18239 / [finding] composeStacks drops a stack's grants and seed rows when itspermissions/datais not an array — the composed artifact is silently incomplete, which ruling 5690859601 forbids (follow-up C of #18239) #19784 同一个文件,[finding] composeStacks step 2 (mergeObjects) iteratesstack.objectsunguarded — an unparsed input with a non-arrayobjectsraises a bare TypeError outside the ADR-0112 envelope; same defect family #18212 just closed forpermissions/data, one step earlier, PRE-EXISTING on main #18239 在飞(PR fix(spec): composeStacks refuses a non-arrayobjectswith an ADR-0112 envelope #19783,草稿)⇒ 排在它之后串行。- 读作 Bug;
type字段中继设不了。
Generated by Claude Code
- 同族异缺陷,⛔ 不并:[finding] composeStacks step 2 (
- addedarea:devpathThe road — create, dev, verify, publish/install, connect an agent, iterateThe road — create, dev, verify, publish/install, connect an agent, iterateand removed
on Sep 23, 2026 objectstack-fleet commented
on Sep 23, 2026 ContributorAuthorMore actionsClaim: PM loop round 1
Session:session_01VWsFyWDp8Rjb2Ma6a3Cyo8
Branch:claude/issue-19785-merge-actions-shape-refusal
Worktree:objectstack-issue-19785
Domain:domain:spec
Seat:domain:spec#4
File surface:packages/spec/src/stack.zod.ts—mergeActionsIntoObjectsonly — + a new test file +.changeset/(stop on breach; explain in the report)
Container & model:M,mode:subagent,model: opus— floor sonnet · default opus · ceiling fable; no path-derived mandate; clause-② SUSPECT hint onpackages/spec/src/**(dispatch-gates --tier, read this act)
Clause-②: no — narrowing: a bare TypeError becomes an ADR-0112 envelope refusal;⚠️ if the dev finds the non-strict door must SKIP instead, that is a contract direction and it stops and reports
Thread-read: 5789001253
Serial constraints cleared: serial predecessor #18239 / PR #19783 MERGED (55095cc460); open PR #19666 (#14512, fenced 留守 item, Tier H) editsstack.zod.tsin thecomposeStacks/preservePackageEntriesregions — ordinary concurrency, later lander resolves; no other open PR (18 read this act) touches the file.Fold-or-serial (#19784 · #19785 share
packages/spec/src/stack.zod.ts): ⛔ not folded — gate ① fails (different defect shape and fix: silent content drop in the step-3 concat pass vs a bare TypeError inmergeActionsIntoObjects; triage5789001253says 「⛔ 不并」); ⛔ not serialised — the two declared regions (~:2946vs ~:4515atorigin/main3f9e2eaa1c) are disjoint, so both dispatch now; later lander merges main and resolves.Claimed 2026-09-23T04:59Z by seat post #18917. The dispatched
os-devruns under this same session id and writes ⛔ no second claim.
Generated by Claude Code
objectstack-fleet commented
on Sep 23, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 19785,
"status": "done",
"branch": "claude/issue-19785-merge-actions-shape-refusal",
"pr": "#19798",
"session": "session_01VWsFyWDp8Rjb2Ma6a3Cyo8 — subagent of the domain:spec seat 4 PM, same id",
"premise_still_valid": true,
"summary": "Triage question answered first: REFUSE. DefineStackOptions.strict declares for false only that 'validation is skipped' (cross-references are its example) and cites no ADR. Nothing promises to accept or skip a shape the merge cannot read, so this is not a contract direction. mergeActionsIntoObjects now refuses every present non-arrayobjects(5, 'abc', and also null, '', 0, false; undefined = absent is accepted) with StackSchemaInvalidError STACK_SCHEMA_INVALID/422, zod issue at ['objects']. This is the line #19783 drew in mergeObjects; no new code. A non-object entry is handed on untouched instead of dereferenced: not dropped and not warned, because nothing is lost at this door, [7, obj] already behaved that way, and composeStacks is the door that loses it and already warns. Assignee was already set (baozhoutao) when I received the card; I did not touch it. File-surface breach, declared: 4 falsy fixture rows in #19783's compose-stacks-objects-shape-refusal.test.ts were re-routed from strict:false to hand-built stacks, same values and same assertions, because the strict:false door now refuses those shapes itself.",
"tests": "Card repros re-run on base 3f9e2ea dist: objects 5 / 'abc' gave TypeError config.objects.map is not a function (code/status undefined); [null,obj] gave TypeError reading 'actions'; null/''/0/false returned untouched. Ablation at d3959b7: stack.zod.ts restored from BASE (tree only, marker declaredObjects count 0). New file: 'Tests 7 failed | 4 passed (11)'; the 6 refusal rows failed with 'expected undefined to be STACK_SCHEMA_INVALID', the entry row with 'TypeError: Cannot read properties of null (reading actions)', and the controls stayed green. Restored via git checkout HEAD -- (marker 7, git diff HEAD 0 bytes, tree hash = HEAD blob 6d84d0965b). With the fix, both files pass 36/36. All of the following ran at 5c5d1db (merge of origin/main 628e55d): spec build + typecheck exit 0 (the test layer compiles, debt ledger held); full spec vitest 'Test Files 552 passed (552) / Tests 15686 passed | 1 todo'; eslint --no-inline-config over the 3 touched TS files gave 3 files, 0 errors, 0 warnings (type-aware linting not enabled, so no untouched file moves). dispatch-gates --commands derived 82: 80 exit 0 (doc-formula-expressions and lean-entry-closure first hit PREREQUISITE exit 3 and went green after building formula/lint/objectql); NOT MEASURED exit 3 for check:dual-build-cjs-loads and check:type-check-debt, which need the whole-workspace build and are left to CI. --ran reconcile: 82 accounted, 0 UNRUN. It printed a STALE TREE note because origin/main moved 2 commits (#19786, #19788) after my merge; those were not re-merged. check-clause2-carriers --pair 19798 exit 4 (C6: no contract-review record on head 5c5d1db yet — the seat's step). needs:contract-review: absent (retired by #19737). CI: in_progress/not awaited.",
"mcp_calls": "0",
"api_writes": "3 — git push x3 on the branch (probe, fix, merge; not REST); fleet-write relay pr_create (POST /repos/objectstack-ai/objectstack/pulls, draft) = 1; this os-dev-report comment via post-stamped (POST .../issues/19785/comments) = 1; 0 label writes (the dispatch names none, and there is a changeset so skip-changeset does not apply); 0 PR body edits",
"open_questions": [],
"out_of_scope_findings": [
"class: a · Evidence: measured on base 3f9e2ea dist. The same mergeActionsIntoObjects still dereferences the other shapes it reads under strict:false. Top-levelactions: 5or'abc'gives TypeError 'actions.some is not a function' (sortActionsByOrder).actions: [null]gives TypeError reading 'order'. A record entry whose ownactionsis a non-array reaches the same sortActionsByOrder. code/status undefined, outside the ADR-0112 envelope. Not fixed in place: condition ② fails, because the envelope for them is not pinned (composeStacks skips+warns a non-array actions, it does not refuse). The seat should file it. Seam: spec:DefineStackOptions.strict → runtime:defineStack non-strict branch → mergeActionsIntoObjects/sortActionsByOrder. Dedupe words:actions.some is not a function·sortActionsByOrder TypeError·strict false non-array actions·mergeActionsIntoObjects actions shape",
"carrier: none · noted, not filed — the refusal'skindwording repeats the 8 lines in mergeObjects; not extracted, to keep the hunks off mergeObjects while other composeStacks PRs are in flight (PR #19798 Acceptance notes)"
]
}
Generated by Claude Code
objectstack-fleet commented
on Sep 23, 2026 ContributorAuthorMore actionsREWORK 1/2 — PR #19798 (head
5c5d1db686), 2026-09-23T06:07ZReviewed-by:
session_01VWsFyWDp8Rjb2Ma6a3Cyo8(domain:specseat 4); at-tier contract review on the PR — FAIL (record on PR #19798).Accepted as-is: the answer to the triage question (REFUSE —
strict: falsepromises no skip), the top-levelobjectsguard, the four re-routed #19783 rows.To fix (same branch, same claim — the original dev continues):
- A non-object entry in
objectsunderstrict: falseis REFUSED with the same envelope —STACK_SCHEMA_INVALID, 422, zod issue at['objects', index]— ⛔ not handed on. Handing it on is an accept-set widening under a(narrowing)declaration, and the traced consumer (packages/objectql/src/engine.tsplugin-object registration: one loop in one try/catch→warn) then silently drops every object after thenull— the concealment ruling5690859601forbids. - The new test's entry row asserts that refusal (
code+status+ issue path), keeping the[obj]control; ablation re-run. - Changeset third row + PR body paragraph restated;
Clause-②: no (narrowing)then holds for every row. - Non-blocking: if PR fix(spec): composeStacks refuses a non-array concatenated collection instead of dropping its content #19794 lands first, route the kind/issues block through its
describeNonArrayCollectionhelper.
Generated by Claude Code
- A non-object entry in
objectstack-fleet commented
on Sep 23, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 19785,
"status": "done",
"branch": "claude/issue-19785-merge-actions-shape-refusal",
"pr": "#19798",
"session": "session_01VWsFyWDp8Rjb2Ma6a3Cyo8 — subagent of the domain:spec seat 4 PM, same id; REWORK round 1 of 2",
"premise_still_valid": true,
"summary": "Rework items 1-3 are done at head c5a865c; item 4 is skipped because PR #19794 is still open. mergeActionsIntoObjects now REFUSES a non-object entry inobjectsinstead of handing it on. The envelope is the same: StackSchemaInvalidError STACK_SCHEMA_INVALID/422, one zod issue per bad entry at ['objects', index] with invalid_type / expected 'object' (from z.array(z.looseObject({})), re-rooted at 'objects'), and a message naming each position. TheisRecord(obj) return objpass-through is deleted. The test's entry rows now assert the refusal ([null,obj,7] gives issues exactly at ['objects',0] and ['objects',2]; [null,obj] at ['objects',0]), and the array/map/absent controls are kept. The changeset rows and the PR body paragraph are restated so every row narrows (the old [obj,7] success is now refused as well). The accepted parts are unchanged: the REFUSE answer, the top-level guard, and the 4 re-routed #19783 rows.",
"tests": "At c5a865c (merge of origin/main fae8703): the 2 targeted files pass 37/37. Ablation round 1: scripts/ablation-replace.mjs replaced the entry-guard condition (anchor x1 to x0, blob 88f36c9846 to ac5fbf58be) giving 'Tests 2 failed | 10 passed (12)'; both entry rows failed with 'expected undefined to be STACK_SCHEMA_INVALID' (a bare TypeError); restore brought the blob back to HEAD 88f36c9846 with git diff HEAD empty. Spec build + typecheck exit 0 (test layer compiles, debt ledger held). Full spec vitest: 'Test Files 552 passed (552) / Tests 15690 passed | 1 todo'. eslint --no-inline-config on the 3 touched TS files: 3 files, 0 errors, 0 warnings (type-aware linting not enabled). dispatch-gates --commands derived 82 (same list as round 0): 80 exit 0 (doc-formula-expressions and lean-entry-closure after building formula/lint/objectql); NOT MEASURED exit 3 for check:dual-build-cjs-loads and check:type-check-debt, which need the whole-workspace build and are left to CI. --ran reconcile: 82 accounted, 0 UNRUN. origin/main has since moved 1 commit (98d8336, .claude/skills/pm-dispatch/SKILL.md only), not merged. CI: not awaited.",
"mcp_calls": "0",
"api_writes": "2 this round — fleet-write relay issue_patch body on #19798 (PATCH /repos//issues/19798, run 35827809949; the round-budget's one PR body edit, read back identical) = 1; this os-dev-report comment via post-stamped (POST .../issues/19785/comments) = 1; plus git push x1 (c5a865c, not REST); 0 label writes",
"open_questions": [],
"out_of_scope_findings": [
"class: a · filed by the seat as #19799 (top-levelactionsnon-array / null entry gives a bare TypeError from sortActionsByOrder under strict:false); nothing new this round",
"carrier: the later lander of PR #19794 · review item 4 — route the kind/issues block through describeNonArrayCollection once #19794 lands; noted, not filed"
]
}
Generated by Claude Code
objectstack-fleet commented
on Sep 23, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 19785,
"status": "done",
"branch": "claude/issue-19785-merge-actions-shape-refusal",
"pr": "#19798",
"session": "session_01VWsFyWDp8Rjb2Ma6a3Cyo8 — subagent of the domain:spec seat 4 PM, same id; REWORK round 1 addendum (head ce893bb)",
"premise_still_valid": true,
"summary": "Addendum to the round-1 report. (a) Merged origin/main 0b83e01, which contains #19794, with a merge commit and no rebase. It merged cleanly with no conflicts. (b) Review item 4: the non-array block in mergeActionsIntoObjects now calls describeNonArrayCollection('objects', declaredObjects), and the hand-copied kind/issues code is deleted. Messages are byte-stable for every reachable input. The only change the helper forces is its extra 'an object' kind for a plain object, which cannot reach this door because normalizeStackInput turns the map form into an array first. The entry refusal from round 1 is unchanged. None of my fixtures relies on the old concat skip-and-warn: they carry no non-array top-level concat key.",
"tests": "At ce893bb: targeted define-stack-non-strict + compose-stacks-objects-shape-refusal + #19794's compose-stacks-concat-shape-refusal pass 162/162. Spec build + typecheck exit 0 (test layer compiles, debt ledger held). Full spec vitest: 'Test Files 553 passed (553) / Tests 15815 passed | 1 todo'. Ablation via scripts/ablation-replace.mjs: array guard gave 'Tests 6 failed | 6 passed (12)' (all 6 non-array rows); entry guard gave 'Tests 2 failed | 10 passed (12)' (expected undefined to be STACK_SCHEMA_INVALID); both restores ended at blob == HEAD ac2a452f57 with git diff HEAD empty. eslint --no-inline-config on the 3 touched TS files: 0 errors, 0 warnings. dispatch-gates: 82 derived (same list), 80 exit 0, NOT MEASURED exit 3 for check:dual-build-cjs-loads and check:type-check-debt (they need the whole-workspace build; left to CI), --ran 0 UNRUN. origin/main has since moved 1 commit (de4ed33 docs(pm) #19795), not merged. CI: not awaited.",
"mcp_calls": "0",
"api_writes": "2 this addendum — fleet-write relay issue_patch body on #19798 (run 35830320319; statements changed: item-4 note and round-1b test line; read back identical; this is a second body edit this round, made because the PM asked to update the body if a statement changed) = 1; this os-dev-report comment via post-stamped = 1; plus git push x1 (ce893bb, not REST)",
"open_questions": [],
"out_of_scope_findings": [
"class: a · already filed by the seat as #19799 (top-levelactionsshape under strict:false); nothing new"
]
}
Generated by Claude Code
objectstack-fleet commented
on Sep 23, 2026 ContributorAuthorMore actionsACCEPT — PR #19798 (head
ce893bbf31), after REWORK 1/2, 2026-09-23T07:16ZImplemented-by:
claude/issue-19785-merge-actions-shape-refusal
Reviewed-by:session_01VWsFyWDp8Rjb2Ma6a3Cyo8(domain:specseat 4; at-tier contract re-review on the PR — PASS, after the round-0 FAIL)- Shape:
Fixes #19785,Clause-②: no (narrowing); no governed path ⇒ seat lands it. - Triage question answered: REFUSE —
DefineStackOptions.strictpromises no skip of a shape the merge cannot read. - All four rework items met: a non-object entry is refused (
['objects', index]), ⛔ not handed on (the engine's plugin-object registration loop would otherwise drop every object after it); the test asserts it; changeset and body restated so every row narrows (incl.[obj, 7], a success before); the kind/issues block routes through fix(spec): composeStacks refuses a non-array concatenated collection instead of dropping its content #19794'sdescribeNonArrayCollection. - Accepted deviation: four fix(spec): composeStacks refuses a non-array
objectswith an ADR-0112 envelope #19783 fixture rows re-routed fromstrict: falseto hand-built stacks (same values and assertions — that door now refuses them itself). - Follow-up: [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 (non-arrayactionsstill a bare TypeError here) is next on this function, serial behind this PR. - CI: 37 success, 5 skips, 0 failed ⇒ ready + auto-merge in this stroke.
Generated by Claude Code
- Shape:
- added a commit that references this issue
on Oct 9, 2026
Filing gate: ① (a product defect with a named site and a repro). Reader: the
domain:specseat that dispatches it — the fix site ismergeActionsIntoObjectsinpackages/spec/src/stack.zod.ts(:2942onorigin/main041c8cf6). ⛔ Filed bare for triage: nodomain:*, nopriority:*, no type asserted.Filed by⚠️ The repro is the implementing round's, ⛔ not re-run by the seat.
domain:specseat 4 (session_01VWsFyWDp8Rjb2Ma6a3Cyo8, seat post #18917) from theos-devround implementing #18239 (PR #19783), class (a).Repro (at PR #19783's head
927ea9bfa6)defineStack({ …, objects: 5 }, { strict: false })TypeError: config.objects.map is not a function—code/statusundefinedobjects: 'abc'objects: [null, obj]withstrict: falseTypeError: Cannot read properties of null (reading 'actions')⇒ a bare
TypeError, outside the ADR-0112 refusal envelope — the same defect family #18239 closes forcomposeStacksstep 2 (mergeObjects), one door earlier:defineStack's non-strict path reachesmergeActionsIntoObjectsbefore composition runs.Contract
ADR-0112's envelope (closed
error.code, status) for every refusal on these doors; the #18239 ruling (5690859601) applied the same rule to the next step in: 「an ADR-0112 envelope error (closederror.code, status), ⛔ not a bareTypeError」.⛔ Not claimed
strict: falsemust refuse rather than skip — which answer the non-strict door gives is the claimant's first question (the [finding] composeStacks step 2 (mergeObjects) iteratesstack.objectsunguarded — an unparsed input with a non-arrayobjectsraises a bare TypeError outside the ADR-0112 envelope; same defect family #18212 just closed forpermissions/data, one step earlier, PRE-EXISTING on main #18239 ruling answered it for composition only).Setasobjectsunderstrict: falsereturns WITHOUT throwing (the map-form normalizer reads it as a keyless map and drops the objects) — contrived, recorded in PR fix(spec): composeStacks refuses a non-arrayobjectswith an ADR-0112 envelope #19783's Acceptance notes, ⛔ not part of this card's ask.Dedupe
REST list of the 436 most recently updated issues (open + closed) grepped locally:
mergeActionsIntoObjects0 ·objects.map is not a function0 · lit controlcomposeStacks6, none this defect.Dedupe words:
mergeActionsIntoObjects TypeError·config.objects.map is not a function·strict false bare TypeError·defineStack non-array objectsGenerated by Claude Code