Repository navigation
[Decision] A decision node that declares both a conditions list and mode: refuse it at authoring, give it a meaning, or leave it inert #20168
Description
Ruled: 5856786357 · letter A · 2026-09-27T14:49Z
This card carries the one sub-question left open by #15429's ruling (issuecomment-5793803317, maintainer 「跟主流对齐」). #15429 keeps the engine traversal, the os migrate meta conversion, the lint hint and the pin rewrite. #19867 / PR #20162 carry the spec key. Filed by the domain:services seat (#6021, session_01TEah6PeJGjxJfbHaySJjLQ) on the spec seat's hand-off: #15429 comments 5852506105 (item 2) and 5852576993 (item 1). ⛔ Not a claim.
Background
- The ruling makes an edge-branched decision (no
config.conditions) exclusive by default. Taking every true edge must be declared withmode: 'inclusive'. - A decision that declares a
conditionslist is already first-match by label (logic-nodes.ts, the A decision node has three declared ways to route a branch and two of them do nothing — app-crm's convert-lead guard runs both branches #4414 repair). The ruling does not say whatmodemeans on that shape, and neither does its migration: the migration only touches decisions with noconditions. - PR feat(spec): DecisionConfigSchema declares an optional mode: 'exclusive' | 'inclusive' (contract half of the #15429 ruling) #20162 (head
5fbb7ed86a, open, not merged) declaresmode: z.enum(['exclusive','inclusive']).optional()besideconditionsin onestrictObject, with no refinement between them. So{ conditions: [...], mode: 'inclusive' }parses, and its.describe()says 「A conditions list is first-match on its own」: the key would be accepted and ignored.
Why it cannot wait
The spec seat's contract review (5852563897, relayed in 5852576993): if a release ships PR #20162 as it stands and this combination is refused later, that refusal is a narrowing of a published accept set (Clause-②: no (narrowing) plus an ADR-0087 disposition). Decided before the next Version Packages release, it costs neither. ⛔ This seat does not ask for the release to be held; the release stays the maintainer's.
Governing text
AGENTS.mdPrime Directive chore: version packages #10: 「never advertise or demo a capability the runtime doesn't actually deliver (declared ≠ enforced)」.AGENTS.mdPrime Directive Add comprehensive test suite for Zod schema validation #12: fix the metadata and 「reject it at authoring/publish (validation / lint) so the error surfaces loudly」.- A decision node with no declared
config.conditionstakes EVERY out-edge whose condition holds, in parallel — nothing enforces or warns that intended-exclusive edges partition #15429 ruling5793803317, items 1–2 (edge-branched decisions only). - Prior-ruling search on
origin/main84880f9224:git grep -n -iE 'mode.*inclusive|inclusive gateway' -- AGENTS.md docs/adr packages/spec/src→ 0 hits (the key is not on main yet). Positive control:exclusive gateway→ 1 hit. No ADR decides this combination.
Premises, each with its re-check
- PR feat(spec): DecisionConfigSchema declares an optional mode: 'exclusive' | 'inclusive' (contract half of the #15429 ruling) #20162 accepts
modebesideconditions. Re-check: readDecisionConfigSchemainpackages/spec/src/automation/schemaless-node-config.zod.tsat the PR's head (or onmainonce merged) for a refinement naming both keys. At5fbb7ed86a: none. - Nothing parses
DecisionConfigSchemawhen a flow is registered, so a schema-only refusal is not yet an authoring-time refusal. Re-check:git grep -n "DecisionConfigSchema\|SCHEMALESS_NODE_CONFIG_SCHEMAS" origin/main -- 'packages/**/*.ts' ':!**/*.test.ts' ':!packages/spec/**'→ 3 hits on84880f9224, all inpackages/metadata-protocol/src/reference-sites.ts(a reference walk, not a parse). Control: the schema file itself → 4. Radius: TypeScript underpackages/by these two names. A parse reached through another name is outside it. The spec seat independently reports the config as export-only (5852506105). - Zero authored uses today:
modeis not onmain, so no flow can carry it.
Question
When a decision node declares a conditions list and mode, what does the platform do?
Options
| option | what happens | what an author sees |
|---|---|---|
| A — refuse | The schema refines DecisionConfigSchema: mode together with a non-empty conditions list is refused, with a prescription (drop mode, or move the branches onto edges). The reader #15429 adds applies the same parse at registration. |
A loud error on save / validate that names the fix. |
| B — give it a meaning | mode: 'inclusive' on a conditions list runs every entry that holds (n8n Switch's "send to all matching outputs"). |
A second branching semantics to learn; engine and migration work beyond the ruling. |
| C — leave it inert | Accepted and ignored, as PR #20162 stands. | mode: 'inclusive' looks like it works and silently does nothing. |
四维分析(业务立场)
- ① 项目长远合理性:A 让契约闭合 ——「
mode只属于按连线分支的判断节点」一句话说清。日后真有需求要 B,从 A 放开是放宽(minor、不破坏);从 C 收回是收窄(破坏性、要 ADR-0087 处置)。主流平台里 BPMN 把排他与包容做成两种不同网关,Salesforce Decision 永远第一个命中;n8n Switch 有"全部命中"开关,但那是有人用才加的。 - ② 实际业务拉动:零。
mode还没进main,没有任何流程写过它;hotcrm#1555 那次事故是按连线分支的判断节点,不是条件列表。⇒ 没有拉动就不该有 B。 - ③ 防 AI 犯错:C 最坏 —— AI 写
conditions+mode: 'inclusive'以为会全走,运行时静默只走一条,与 hotcrm#1555 同类的"声明与执行不一致"。A 在保存时响亮拒绝并给出改法;B 多一套语义、多一个出错面。 - ④ 创业阶段不扩散:A 不新增能力;B 新增一套无人要的能力;C 留下一个永久要维护、却不干活的键。
Prior rulings read: mode.*inclusive|inclusive gateway → 0 hits (control exclusive gateway → 1); ADR none; thread: #15429 5793803317, 5852506105, 5852576993.
推荐 A(回退项:B,仅当出现实测拉动)。只看①选 A;②③④ 是否翻转:否。
置信缺口:本席未读 objectui 设计器表单是否会在条件列表节点上展示 mode(协调卡 objectui#10750 已存在);也未测第三方宿主是否绕过注册路径直接执行判断节点。
Execution per answer
- A: the refusal lives in
packages/spec(spec lane). The cheapest landing is inside PR feat(spec): DecisionConfigSchema declares an optional mode: 'exclusive' | 'inclusive' (contract half of the #15429 ruling) #20162 before it merges (the spec seat decides); otherwise a separate spec change ahead of A decision node with no declaredconfig.conditionstakes EVERY out-edge whose condition holds, in parallel — nothing enforces or warns that intended-exclusive edges partition #15429, landed before the next release. The parse at registration comes with the reader A decision node with no declaredconfig.conditionstakes EVERY out-edge whose condition holds, in parallel — nothing enforces or warns that intended-exclusive edges partition #15429 adds (this lane), and the refusal code and prescription join the acceptance list of A decision node with no declaredconfig.conditionstakes EVERY out-edge whose condition holds, in parallel — nothing enforces or warns that intended-exclusive edges partition #15429. - B: the scope of A decision node with no declared
config.conditionstakes EVERY out-edge whose condition holds, in parallel — nothing enforces or warns that intended-exclusive edges partition #15429 grows to "conditions list + inclusive = every match"; the migration rule is unchanged; the spec.describe()is rewritten. - C: no change; the acceptance list of A decision node with no declared
config.conditionstakes EVERY out-edge whose condition holds, in parallel — nothing enforces or warns that intended-exclusive edges partition #15429 gains "the.describe()states thatmodehas no effect on a conditions list". - Under every answer, A decision node with no declared
config.conditionstakes EVERY out-edge whose condition holds, in parallel — nothing enforces or warns that intended-exclusive edges partition #15429 unlocks only once both this card and spec(automation): add an optionalmode: 'inclusive'toDecisionConfigSchema— the contract half of #15429's ruling (edge-branched decisions become exclusive / first-match; "take every true edge" must be declared) #19867 are closed.
Related: #15429 · #19867 / PR #20162 · objectui#10750.
Dedupe: semantic search in this repository (closed included) for decision node mode inclusive declared alongside config.conditions refuse or inert → 6 hits (#19867, #15429, #17493, #17322, #14288, #7545). None is a card on this combination.
Activity
objectstack-fleet commentedon Sep 27, 2026
Premise refresh: PR #20162 has merged, so the accept-both shape is now on
main. It has not been released yet.domain:servicesseat (#6021) ·session_01TEah6PeJGjxJfbHaySJjLQ· 2026-09-27T05:25Z369bcbeda7, at the reviewed head5fbb7ed86a, and spec(automation): add an optionalmode: 'inclusive'toDecisionConfigSchema— the contract half of #15429's ruling (edge-branched decisions become exclusive / first-match; "take every true edge" must be declared) #19867 closedcompleted.origin/maine2c4e125f9:DecisionConfigSchemadeclaresconditionsandmodeside by side, andschemaless-node-config.zod.tscontains 0refine/superRefinecalls. The control ismode: z.enum, with 1 hit. ⇒{ conditions: [...], mode: 'inclusive' }still parses. Premise 1 holds, now onmainrather than on an open PR.packages/specchange (spec lane) that refuses the combination, landed ahead of A decision node with no declaredconfig.conditionstakes EVERY out-edge whose condition holds, in parallel — nothing enforces or warns that intended-exclusive edges partition #15429's engine half and before the next release. The narrowing cost the spec seat's review named (5852563897) applies only once a release has published the key. Until then, the refusal is not a published narrowing.Generated by Claude Code
objectstack-fleet commentedon Sep 27, 2026
Ruling: batch #227 item 4 · letter A · maintainer 「开始总监决裁」 2026-09-27T14:36Z
Director seat, summon #30 续,
session_01AsCNgFBs8HCjwhyHQsFbx3. Batch #227 was presented in the seat chat with this seat's recommendation on each item (thedomain:servicesseat's analysis in the card body and its premise refresh 5852968135 re-read, four axes not flipped); the maintainer answered 「开始总监决裁」, and the batch is ruled as presented. Maintainer verbatim recorded here per the charter.Ruled: A — a decision node that declares a non-empty
conditionslist together withmodeis refused at authoring, with a prescription: dropmode(a conditions list is first-match on its own), or move the branches onto edges and keepmode.modebelongs to the edge-branched decision alone.Readings on
origin/mainab820016b3:packages/spec/src/automation/schemaless-node-config.zod.ts:482declaresmode: z.enum(['exclusive', 'inclusive'])besideconditionsin onestrictObjectwith no refinement between them (PR #20162, merged369bcbeda7), so{ conditions: [...], mode: 'inclusive' }parses and is ignored; the describe at:440-459already teachesmodein BPMN gateway terms. Prior-ruling sweep forinclusive gateway|mode.*inclusiveoverAGENTS.md,docs/adr,packages/spec/src: only the schema and its tests; no ADR decides the combination. #15429's ruling (5793803317, 「跟主流对齐」) covers edge-branched decisions only.Mainstream reading the ruling rests on: BPMN models exclusive and inclusive branching as two different gateways, never one gateway with a list of conditions and a mode; Salesforce Flow's Decision element is always first-match over its outcomes; n8n's Switch gained a 「send to all matching outputs」 option only on measured demand. C is the shape AGENTS.md Prime Directive #10 forbids (declared ≠ enforced), and it is the same silent class as hotcrm#1555; B adds a second branching semantics nobody has asked for. A closes the contract in one sentence, and a later B is a widening (
minor), while a later retreat from C would be a narrowing with an ADR-0087 disposition.Execution parameters, ruled here so the dispatch needs no second decision:
packages/spec: a refinement onDecisionConfigSchemarefusingmodewhenconditionsis a non-empty array, with a located issue atmodeand the prescription above;modeon an empty or absentconditionsis untouched. The registry's dropped-refinements ledger grows by the one route it allows if the closed projection list has no arm for it, as the currency-scale precedent (finding(spec):InlineGridColumnSchema.scalestill offers decimal places on acurrencyinline column, and itsprefixdescribe promises a¥default the renderer no longer has — ruling B / 乙 left the inline grid mirror behind #20045) did.modeis unreleased the refusal is a correction of an unreleased feature: changeset@objectstack/specpatch,Clause-②: no, no ADR-0087 entry. If the release has shippedmodebefore this lands, the dev re-grades tominor+ BREAKING,Clause-②: no (narrowing), with an ADR-0087 disposition — measured at landing time, not assumed.config.conditionstakes EVERY out-edge whose condition holds, in parallel — nothing enforces or warns that intended-exclusive edges partition #15429 adds parsesDecisionConfigSchemaat registration (that lane), and the refusal code and prescription join A decision node with no declaredconfig.conditionstakes EVERY out-edge whose condition holds, in parallel — nothing enforces or warns that intended-exclusive edges partition #15429's acceptance list; A decision node with no declaredconfig.conditionstakes EVERY out-edge whose condition holds, in parallel — nothing enforces or warns that intended-exclusive edges partition #15429 unlocks once this card and spec(automation): add an optionalmode: 'inclusive'toDecisionConfigSchema— the contract half of #15429's ruling (edge-branched decisions become exclusive / first-match; "take every true edge" must be declared) #19867 are closed (spec(automation): add an optionalmode: 'inclusive'toDecisionConfigSchema— the contract half of #15429's ruling (edge-branched decisions become exclusive / first-match; "take every true edge" must be declared) #19867 is closed).domain:spec,pm:queue,priority:p2set in this act (the release timing grades it); it closes byFixeswith the spec PR.State:
needs-user-decision→pm:queuein this act; body first line set to this ruling.Generated by Claude Code
objectstack-fleet commentedon Sep 27, 2026
⏸️ Hold — the ruling above is held for the maintainer's fuller review of batch #227. Director seat (summon #30 续) ·
session_01AsCNgFBs8HCjwhyHQsFbx3· 2026-09-27T14:38Z. The maintainer's word after the batch was posted: 「227 你写的太简单了」. No state transition has been applied: this card keepsneeds-user-decision, its body carries noRuled:line for the comment above, and ⛔ no seat acts on that ruling until a follow-up comment from the director seat confirms it or supersedes it with a different letter.Generated by Claude Code
objectstack-fleet commentedon Sep 27, 2026
✅ Confirmed — the ruling above (5856786357, letter A) is in force. Director seat (summon #30 续) ·
session_01AsCNgFBs8HCjwhyHQsFbx3· 2026-09-27T14:45Z. After the fuller analysis of batch #227 was presented in the seat chat (each item: background, options with real costs, mainstream platforms, four axes, recommendation, confidence gaps, execution), the maintainer answered 「同意」 (verbatim, recorded here per the charter). The hold at 5856799823 is lifted. State transition applied in this act:needs-user-decision→pm:queue, plusdomain:specandpriority:p2(this card is the spec-lane carrier); the body's first line now carriesRuled: 5856786357 · letter A. Execution follows the 裁后执行 section of the ruling.Generated by Claude Code
objectstack-fleet commentedon Sep 27, 2026
Claim: PM loop round 1
Session:
session_01CiCTczDo7tGhafXjf61dUJAccount:
os-sales(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-20168-decision-conditions-mode-refusedWorktree:
objectstack-issue-20168Domain:
domain:specSeat:
domain:spec#4(seat post #18917)File surface: ruling
5856786357item 1:packages/spec/src/automation/schemaless-node-config.zod.ts:DecisionConfigSchema(about :471–:500 onorigin/maine46218674), plus its refusal message helper beside the existingmoderefusal (about :206). The refinement refusesmodewhenconditionsis a non-empty array, located atmode, with the ruled prescription.packages/spec/dropped-refinements.baseline.json, only if the build names the new refinement, with the totals recounted from the body..changeset/.⛔ Not
flow-node-expression-paths.ts(#19961, next in this lane). ⛔ Not the engine traversal (#15429,domain:services). ⛔ Not objectui#10750. (stop on breach; explain in the report)Container & model:
S/M,mode:subagent,model: default judgment tier(no path-derived mandate forpackages/spec/src/automation/). The diff narrows an accept set underpackages/spec/src/**, so the at-tier review is owed before enqueue.Clause-②: no
Thread-read: 5856865124
Serial constraints cleared: read at 2026-09-27T15:54Z. The serial predecessor on this file, PR #20205 (#19938 + #11182), landed as
e46218674at 15:47Z; it also touched the ledger. Open-PR census: 10 open PRs besides the release PR; none touchesschemaless-node-config.zod.ts. PR #20251 (seat 1's #19992) touchesdropped-refinements.baseline.json, the fleet's hot ledger: if both land near each other, the later one re-mergesmainand recounts the two header totals, whichdropped-refinements.test.tspins. In-flight claims: seat 1's #20116 names the ledger 「if the build names it」 forfilter.zod.ts, which is disjoint code.Ruling carried:
5856786357(batch #227 item 4, letter A; maintainer 「开始总监决裁」, confirmed 「同意」 in5856865124): 「a decision node that declares a non-emptyconditionslist together withmodeis refused at authoring, with a prescription: dropmode(a conditions list is first-match on its own), or move the branches onto edges and keepmode」. Its timing item 2 grades the landing. Whilemodeis unreleased (the Version Packages PR #17076 is open and unmerged at this read), this ispatchwithClause-②: noand no ADR-0087 entry. If a release shipsmodefirst, the dev re-grades tominor+**BREAKING**+Clause-②: no (narrowing)+ an ADR-0087 disposition, measured at landing.fix(spec): refuse decision mode beside a non-empty conditions list (#…
test(spec): pin the decision mode-beside-conditions refusal and its c…
chore(spec): declare the DecisionConfig refinement in the dropped-ref…
docs(spec): regenerate the schemaless node config reference for the m…
objectstack-fleet commentedon Sep 27, 2026
os-dev-report
{ "issue": 20168, "status": "done", "branch": "claude/issue-20168-decision-conditions-mode-refused", "pr": "https://github.com/objectstack-ai/objectstack/pull/20279", "session": "session_01CiCTczDo7tGhafXjf61dUJ — subagent of the dispatching PM session (harness-stamped Claude-Session line)", "premise_still_valid": true, "summary": "Ruling 5856786357 (letter A) implemented in packages/spec. DecisionConfigSchema gains a .superRefine: a legal mode beside a NON-EMPTY conditions array adds one custom issue at ['mode'] via the new decisionModeWithConditionsRefusal() helper, beside decisionModePrescription() and in its prose style (the value is echoed; both members are refused). Three shapes parse as before: mode beside an empty list, mode with conditions absent, and a list without mode. An illegal mode value still gets only its value refusal, because a base issue aborts the refinement. The mode describe/docblocks state the refusal and the reference mdx is regenerated. No projection arm fits a value-conditional 'this key forbids that one' rule, so the build's ratchet named exactly one new dropped site: automation/DecisionConfig, root. It was added to dropped-refinements.baseline.json and the header totals recounted from the body (211 to 212 schemas, 604 to 605 sites); the built DecisionConfig.json carries x-dropped-refinements. Timing measured at landing, twice (16:12Z and 18:03Z): npm latest @objectstack/spec is 17.4.0, and its tarball's DecisionConfig.json has conditions only with additionalProperties false; PR #17076 is open and unmerged. So mode is unreleased: patch, Clause-②: no, no ADR-0087 entry, stated in the changeset and PR body. PM mechanism assumption 3, measured: the only door that parses DecisionConfigSchema today is a direct parse, via the export and the SCHEMALESS_NODE_CONFIG_SCHEMAS.decision handle. Registration skips it (validateNodeConfigKeys: if (!schema) continue). FlowSchema parses only an end node's config. os validate's lint reads config ad hoc. The JSON Schema drops the rule and declares it. So no ADR-0112 code+status door exists yet; it arrives with #15429's reader. The refusal code, path, first sentence and both remedies are named in the PR body for that lane's acceptance list.", "tests": "All at head ae8185032. (1) pnpm --filter @objectstack/spec test: exit 0, 549 files, 16159 passed, 2 todo. (2) spec typecheck (tsc + check:scripts-typecheck + check:test-typecheck): exit 0. (3) spec build: exit 0. Before the ledger row, the same build printed '+ automation/DecisionConfig (1 site(s))' and exited 1. (4) spec check:generated: 14/15 current, check:docs stale. After gen:docs, check:docs exits 0 with '226 generated files in sync'. (5) Consumer closure turbo build --only (service-automation/lint/metadata-protocol closures plus client and client-react, excluding spec): exit 0. Consumer suites: service-automation 146 files / 1757 passed, exit 0; lint 110 files / 4258 passed, exit 0; metadata-protocol 189 passed + 3 skipped files, 2715 passed / 19 skipped tests, exit 0. (6) The target file alone: 50/50 passed. (7) Ablation, one-shot from the committed state, via node scripts/ablation-replace.mjs under the lock. It replaced the refinement guard with if (false): anchor 1 to 0, blob 70f2ae5d5b01 to 1de6a6511010. Result 7 failed / 43 passed (50). The six refusal pins went red, plus 'following either remedy parses', whose first assertion is the refusal. The controls and the value-refusal-first test stayed green, the predicted direction. Restore proven: blob 70f2ae5d5b01 == HEAD blob, git diff HEAD empty. No rebuild was needed: the test imports the schema by relative path (src), with no package exports or dist in the resolution path. (8) dispatch-gates --commands --repo objectstack-ai/objectstack, each command run with its exit on disk, then --ran: '107 derived famil(ies) accounted for — 105 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3)', 0 unrun. Five gates first exited 3 on an unbuilt closure and exited 0 on re-run after the closure build: lint check:doc-formula-expressions, lint check:doc-security-posture, spec check:skill-examples, check:docs-transcript-drift, check:lean-entry-closure. NOT MEASURED: check:dual-build-cjs-loads and check:type-check-debt; both need the whole workspace built (86 packages without dist; lint.yml runs a full turbo build first), declared to CI. (9) eslint --no-inline-config --format json on the 2 touched .ts files: exit 0, 2 files, 0 errors, 0 warnings. The population is those 2 files: the json, md and mdx match no config object, the count is read from the JSON output, and it is invariant for untouched files because eslint.config.mjs enables no type-aware linting (no parserOptions.project). (10) check:nul-bytes: exit 0, plus a control-byte grep of the 4 hand-edited files: 0 matches.", "gates": [ "HEAD ae8185032", "pnpm --filter @objectstack/spec build :: exit 0", "pnpm --filter @objectstack/spec test :: exit 0", "pnpm --filter @objectstack/spec typecheck :: exit 0", "pnpm --filter @objectstack/spec gen:docs && pnpm --filter @objectstack/spec check:docs :: exit 0", "turbo run build --only (consumer closure, spec excluded) :: exit 0", "pnpm --filter @objectstack/service-automation exec vitest run --maxWorkers=2 :: exit 0", "pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2 :: exit 0", "pnpm --filter @objectstack/metadata-protocol exec vitest run --maxWorkers=2 :: exit 0", "ablation (scripts/ablation-replace.mjs, guard to if (false)) :: exit 1 as intended (7 failed / 43 passed), restore proven", "node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran ran.list :: exit 0 (107 derived, 105 exit 0, 2 NOT MEASURED exit 3, 0 unrun)", "pnpm check:dual-build-cjs-loads :: exit 3 NOT MEASURED (needs full workspace build)", "pnpm check:type-check-debt :: exit 3 NOT MEASURED (needs full workspace build)", "eslint --no-inline-config (2 touched .ts) :: exit 0", "pnpm check:nul-bytes :: exit 0" ], "line_budget": "235 changed lines (+223 / -12) over 5 files against the 5000 human-merge threshold: under (dispatch-gates reading at ae8185032)", "files_changed": [ "packages/spec/src/automation/schemaless-node-config.zod.ts", "packages/spec/src/automation/schemaless-node-config.test.ts", "packages/spec/dropped-refinements.baseline.json", "content/docs/references/automation/schemaless-node-config.mdx", ".changeset/20168-decision-mode-beside-conditions-refused.md" ], "deviations": [ "PM assumption 3 adapted, not failed silently: no door that answers in the ADR-0112 envelope (code+status) parses DecisionConfigSchema today. The pins cover the direct parse, the SCHEMALESS_NODE_CONFIG_SCHEMAS.decision handle (the by-node-type lookup #15429's reader will use) and the per-parse objectStackErrorMap. The registration door stays #15429's lane, per ruling item 3 and the claim's file surface.", "Heavy locked runs were launched detached (nohup) with the recorded PID and awaited in the foreground with tail --pid. A full lock queue wait (540s) plus a spec build exceeds the ~10 min foreground cap. No run outlived the turn.", "Lock queue timeouts (exit 99) on 5 attempts were recorded NOT MEASURED and re-run with the kept slot. None was read as a result.", "A container restart killed the queued gen:docs/check:docs call. It was re-run from the clean head 72745d8fa and committed as ae8185032.", "The consumer sweep is the direct importers of the schema family found by git grep (spec, service-automation, metadata-protocol) plus @objectstack/lint as the dispatch named it, not all of ...@objectstack/spec. The public types are byte-unchanged (check:api-surface exit 0 with no regeneration), so only this schema's parse accept set narrows.", "Two derived gates are NOT MEASURED and declared to CI: check:dual-build-cjs-loads and check:type-check-debt." ], "mcp_calls": "0 — no MCP GitHub tool called", "api_writes": "3 REST writes, each one repository_dispatch to POST /repos/objectstack-ai/objectstack/dispatches executed by the fleet-write relay as objectstack-fleet[bot]: (1) pr_create, i.e. POST /repos/objectstack-ai/objectstack/pulls with draft forced, giving PR #20279 (request fw-20260927T180426Z-81921c, run 36339286397, success); (2) label-write --assign os-sales, i.e. POST /repos/objectstack-ai/objectstack/issues/20279/assignees (request fw-20260927T180503Z-5cf724, run 36339326273, success, read back as assignee os-sales); (3) this os-dev-report, i.e. POST /repos/objectstack-ai/objectstack/issues/20168/comments via scripts/pm/post-stamped.mjs. git push to the own branch only (not REST).", "open_questions": [], "out_of_scope_findings": [ "carrier: release compiler at the next Version Packages cut (maintainer) · noted, not filed. The pending, unreleased .changeset/19867-decision-config-mode.md bullet 'A conditions list is unaffected' is left as written, since it is another PR's input; this PR's changeset states the refusal in the same release. PR #20279 Acceptance notes name the one-clause amendment if one sentence is wanted.", "carrier: objectui#10750 (designer form on a conditions-list node) · noted, not filed. objectui was not checked out in this container, so whether its designer offers mode on a conditions-list node is NOT MEASURED here." ] }Generated by Claude Code
objectstack-fleet commentedon Sep 27, 2026
ACCEPT — PR #20279 (2026-09-27T19:13Z)
domain:specseat 4 (session_01CiCTczDo7tGhafXjf61dUJ). This is the review of record for the round claimed in5857419814. The dev report is5858409074.Checklist, read on GitHub:
main. The first line isFixes #20168, and there is no other closing keyword. The bare lineClause-②: nomatches the diff. Assigneeos-sales.schemaless-node-config.zod.ts(the refinement and its message helper) and its test;automation/DecisionConfig, root; headers recounted 211 → 212 schemas, 604 → 605 sites);5858910699on the PR, at headae8185032a.customissue at['mode']that echoes the value and names both remedies of ruling5856786357.modevalue still gets only its value refusal.DecisionConfigSchematoday goes through the one refined object. Registration skips the schema, and the JSON projection declares the dropped rule.modebeside a list.modeis unreleased (npmlatest17.4.0 carriesconditionsonly, and chore: version packages #17076 is open), so the grade ispatch,Clause-②: no, and no ADR-0087 entry, per ruling parameter 2. The review re-measured this.ae8185032a: 33 success, 2 skipped, 0 failed; all seven required contexts are success.merge-treeonto today'smain(e6b7d8c86) exits 0.Findings, disposed one line each:
.changeset/19867-decision-config-mode.mdbullet 「Aconditionslist is unaffected」 will sit beside this PR's refusal in the same release → Acceptance notes, for the release compiler: the one-clause amendment is 「— andmodebeside a non-empty list is refused」. The review judged it non-blocking. A parse of that shape answers loudly with the prescription, so no author is misled past a refusal.dropped-refinements.test.tspins the result → Acceptance notes (a hot-file row on seat post [PM seat] domain:spec · seat 4 — ⏳ vacant #18917).config.conditionstakes EVERY out-edge whose condition holds, in parallel — nothing enforces or warns that intended-exclusive edges partition #15429 lane's acceptance list (codecustom, path['mode'], the first sentence, both remedies) is named in the PR body → carrier A decision node with no declaredconfig.conditionstakes EVERY out-edge whose condition holds, in parallel — nothing enforces or warns that intended-exclusive edges partition #15429.zod: "4.4.3"field reads older than the resolved zod. It predates this PR and is untouched → dropped: not this card's, and no reach measured.Next: ready → auto-merge through the relay → merge queue. This card closes on the merge through
Fixes.objectstack-fleet commentedon Sep 27, 2026
Landed — PR #20279 →
733822cf85(2026-09-27T19:36Z)domain:specseat 4 (session_01CiCTczDo7tGhafXjf61dUJ). PR #20279 merged through the merge queue on 2026-09-27T19:35:56Z, and this card closedcompletedthrough itsFixesline. The readings that confirm the landing:733822cf85has one parent (2aa25efb4e), is an ancestor oforigin/main, and the queue branch for the PR is gone.git patch-id --stableequals the reviewed diff's:bbdc806b1902on both (record5858910699atae8185032a, from merge basee46218674b).decisionModeWithConditionsRefusalreads 3 at the merge and 0 on the parent.pm:dispatchedand the assignee come off in the same act.Carried:
main. It re-merges and recounts the header, with the joint totals 211 schemas / 592 sites.mode: 'inclusive'toDecisionConfigSchema— the contract half of #15429's ruling (edge-branched decisions become exclusive / first-match; "take every true edge" must be declared) #19867 pending-changeset clause is noted for the release compiler (ACCEPT5858926859).config.conditionstakes EVERY out-edge whose condition holds, in parallel — nothing enforces or warns that intended-exclusive edges partition #15429 carry the consumer halves.fix(spec): refuse decision `mode` beside a non-empty `conditions` list (
fix(spec)!: refuse a decision branch with no expression — absent or n…