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
automation: the region refusal leaves its child run uncancelled with only a warn when the suspension chain cannot be read, and a region call reached through a child's top-level call still arms #29166
Filing gate: ① two should-fix items of the CONTRACT_REVIEW_TIER review PASS on PR #29096 (comment 6572481670, head 65a94546), carried so #28897 can close over its landed scope. Filed by domain:server seat 3. ⛔ Not a claim. Suggested-grade: p3 · both are edges of a narrowing that already fails loudly at run time; nothing is granted or disclosed.
Reach: a record-triggered flow whose loop / parallel / try_catch region calls a child that can suspend (item 1), or whose top-level subflow calls a child that itself makes such a region call (item 2).
Reader who acts: a domain:server seat (packages/services/service-automation).
What happens (read at PR #29096's head, not measured on this card)
Empty cancel chain.runRegion (dag-traversal.ts) cancels the child run the region refusal leaves behind by walking resolveSuspensionChain(childRunId). That walk reads through the degrading loadSuspendedRun (run-resume.ts), so a store it cannot read yields an empty chain: zero cancelRun calls and only a warn. The PR body's claim that a failed cancellation is logged at error does not hold on that path, and the child run can stay parked.
Transitive shape. The arming decline (declineOntoRegionSuspendingCall, engine.ts) judges region-nested calls in the caller itself. A caller whose top-level subflow reaches a child whose own region call reaches a suspending grandchild still arms; at run time the child fails at its own region and the caller fails loudly (no orphan after item 1's cancel).
Wanted
(1) When the chain is empty, call cancelRun(childRunId, reason) once so its strict loader owns the error record; a test with an unreadable store, red first.
(2) Measure first whether the transitive shape should decline at arming (one more walk over suspendingNode's through chain) or whether the loud run-time failure is the intended answer; build the decline only if the reading is unambiguous, otherwise report the fork.
Read fresh. This is a finding, so it keeps the finding label and gains only a domain and a priority. Both items land in packages/services/service-automation: the empty-chain cancel in runRegion (dag-traversal.ts) and the transitive arming decline in engine.ts. The filer suggested p3, but a child run that can stay parked with only a warn is a runtime behaviour defect, not prose drift, so p2.
Claim: PM loop round 1 · card thread · 2026-11-02T11:15Z
Session: session_01ADHkmQJdgarUjPLNgFBY67 (the seat); the card thread is session_01ADHkmSmWmYVtGXXtTcxTr3
Account: os-steve (the seat's linked user; the card's assignee)
Branch: claude/project-thread-b4x6u0
Worktree: the card thread's own container checkout
Domain: domain:server
Seat: domain:server#3
Ruling-ref: the maintainer, verbatim, in the project thread: 「开 server 2、3、5 号席位, 前任已经下班,接手所有开发,包括跟进之前未完成的开发落地。」
File surface: packages/services/service-automation/src/dag-traversal.ts (runRegion empty-chain cancel) + engine.ts (declineOntoRegionSuspendingCall, item 2 only if the reading is unambiguous) + tests + one changeset
Container & model: card thread session_01ADHkmSmWmYVtGXXtTcxTr3, mode:thread, model: opus (--tier names no path mandate: PM's call, default), effort: high
Clause-②: no
Metadata-protocol: none
Responsibility: the card thread implements and opens the draft PR; the seat reviews (contract review + full dogfood if item 2 ships as a narrowing), records and lands
Thread-read: 6573282965
Premise: holds (site re-read on origin/main7788eedb by the seat before dispatch)
Serial constraints cleared: none open on dag-traversal.ts / engine.ts / run-resume.ts at seat read; #29226 holds flow-registration-checks.ts (serial if reached)
Resume-from: none — fresh from origin/main
State: pm:queue → pm:dispatched, assigned to os-steve, recorded by this claim. Order: project file server-seat3/standard-order.md + server-seat3/29166-delta.md.
{
"issue": 29166,
"status": "needs_decision",
"branch": "claude/project-thread-b4x6u0",
"pr": "https://github.com/objectstack-ai/objectstack/pull/29329",
"session": "session_01ADHkmSmWmYVtGXXtTcxTr3",
"premise_still_valid": true,
"summary": "Item (1) shipped as draft PR #29329 (head 36b3678f, Part of the card): runRegion's empty suspension chain now calls cancelRun(childRunId, reason) once, so an unreadable store no longer leaves the child parked with only the degrading read's warn; cancelRun's strict read owns the error record, and the refusal is thrown unchanged. Item (2) is NOT built: measured, a caller whose top-level subflow reaches a child with a region call to a suspending grandchild arms today, fails loudly without a fault edge, and COMPLETES (success: true) with a fault edge on its subflow node, because the child's region refusal reaches the caller as an ordinary routable child failure. Declining it at arming is therefore not an unambiguous reading of ruling D / #28846 / #28897, so it is returned as needs_decision per the dispatch ruling. Clause-②: no (item 1 only).",
"tests": "service-automation src/subflow-region-suspension.test.ts +2 cases (unreadable store: exactly one cancelRun(childRunId) + one error record from cancelRun; store that answers again: child ends cancelled, nothing parked). Red first on 7788eedb+tests: 'expected \"cancelRun\" to be called 1 times, but got 0 times', Tests 2 failed | 20 passed (22). Green: 22/22. Ablation via scripts/ablation-replace.mjs deleting 'await this.cancelRun(childRunId, reason);' (anchor 1 -> 0, blob 0395cdb42a14 -> 16347308d964): same 2 red, 20 passed; restored blob == HEAD, git diff HEAD empty (test imports src, no dist leg). Package on merged head 36b3678f after turbo build ./packages/**: pnpm --filter @objectstack/service-automation test Test Files 259/259, Tests 3337/3337; typecheck exit 0 (check:test-typecheck OK); lock VERDICT command-exit 0. eslint --no-inline-config on the 2 changed .ts files exit 0.",
"gates": "dispatch-gates --commands on 36b3678f: 61 derived; all 61 run, exit 0; --ran reconcile: 61 run, 0 NOT-MEASURED, 0 UNRUN. Pre-merge on 7e91f5ab the same 61 were green after re-running check-plugin-teardown-shape --self-test (exit 3 until git fetch origin 621a4876) and check:dual-build-cjs-loads (exit 3 until ./packages/** built). Full lists in the PR body. PR check-runs: in_progress at report time (not waited on).",
"dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm (Clause-②: no)",
"line_budget": "+100 / -0 over 3 files: dag-traversal.ts +12, subflow-region-suspension.test.ts +78, changeset +10",
"files_changed": [
".changeset/29166-region-refusal-empty-chain-cancel.md",
"packages/services/service-automation/src/dag-traversal.ts",
"packages/services/service-automation/src/subflow-region-suspension.test.ts"
],
"deviations": [
"Commit 3492229b carries only the two red tests although its subject names the fix: the Edit to dag-traversal.ts was refused (file not yet read) and the commit went out without it. The fix is commit eeffe053, whose message says so. Not rewritten: the branch is not claude/issue-*, so it is shared under AGENTS.md section 3 and no force-push.",
"Standard order item 5 asks for pnpm lint; os-dev.md (binding over the order) assigns the repo-wide lint to CI. Ran root eslint --no-inline-config on the changed .ts files instead; repo-wide pnpm lint left to CI's Lint & Repo Gates.",
"Item (2) measured with a scratch test file (zz-scratch-29166.test.ts) run once and deleted; never committed."
],
"mcp_calls": "4 — mcp__github__issue_read x3 (get, get_comments x2), mcp__github__pull_request_read x1 (get); no write tools",
"api_writes": "2 — relay pr_create (fleet-write run 41442234208, PR #29329, 9432 bytes sent = stored); post-stamped os-dev-report comment on #29166. git pushes are not REST writes.",
"open_questions": [
{
"question": "Item (2): should a caller whose TOP-LEVEL subflow / map reaches a child that itself makes a region call to a suspending flow be declined at arming? Measured on 36b3678f: it arms; its run fails loudly without a fault edge; with a fault edge on its subflow node it completes (success: true), the child's region refusal arriving as an ordinary routable child failure. The child (deal_review) is itself declined only if it has its own trigger.",
"options": [
"A — decline at arming: one more walk in declineOntoRegionSuspendingCall over the flows the caller's top-level calls reach (transitively), declining when any of them holds a region-suspending call; rejudge already re-asks transitive callers. Cost: refuses callers that route the failure through a fault edge / try_catch and complete today, so Clause-②: yes (narrowing), ! title, full dogfood, contract review. Work: M (helper + sentence + ~6 tests + ablation).",
"B — the run-time failure is the answer: the defect lives in the child, whose own arming gate names it when the child is triggered; the caller meets an ordinary child failure the subflow contract defines as routable. Document it in subflow-region-guard.ts / region-suspension-refusal.ts docblocks. Clause-②: no. Work: S (comments only), no behaviour change.",
"C — keep the refusal un-routable across the subflow / map boundary (carry its guard class to the caller so a fault edge cannot swallow it), then the caller fails loudly every time and A becomes an unambiguous follow-up. Cost: changes routing an author can see today (narrowing), touches subflow-node.ts / map-node.ts. Work: M."
],
"recommendation": "B, because the measured behaviour in the caller is the subflow contract working as designed (a failed child is routable), declining would refuse a flow that its author routed and that completes today, and subflow-region-guard.ts already records the reading that a child's region call fails the child, not the caller. If the maintainer wants the child's refusal to stay un-routable one level up, that is C, a separate narrowing card."
},
{
"question": "Partial chains: when the child is readable but a deeper link's degrading read fails, resolveSuspensionChain stops early and the deeper run is not cancelled (only the degrading read's warn). Out of this card's empty-chain ruling. File as a follow-up?",
"options": [
"A — follow-up card: walk the chain with the strict loader in runRegion and cancel what it can, logging at error where a link cannot be read",
"B — accept: the deeper run is reachable by the operator from the child's correlation"
],
"recommendation": "A, the same defect class one link deeper; a strict read is the shape cancelRun already uses."
}
],
"security_findings": "none",
"out_of_scope_findings": [
"carrier: seat 3 — partial-chain gap noted in open_questions[1] and the PR's Acceptance notes, not filed"
]
}
Ruling-ref: the dev's measurement and options in os-dev-report 6576781813 (open_questions[0] and [1]); the seat delta, which set item (2) as "build only if unambiguous, otherwise options A and B"; the maintainer's standing word that lane seats decide technical sequencing and grading (2026-11-01T12:11Z).
Ruling: option B. The run-time failure is the answer; nothing new is declined at arming.
What was measured: a caller whose top-level subflow or map reaches a child that makes a region call to a suspending grandchild arms today. Without a fault edge its run fails loudly. With a fault edge on its subflow node it completes, because the child's region refusal reaches the caller as an ordinary child failure, and the subflow contract defines that as routable.
Why B: the defect lives in the child, and the child's own arming gate names it as soon as the child has its own trigger. Declining the caller (A) would refuse flows that their authors routed and that complete today, a narrowing with no measured user asking for it. C changes routing that authors can see today. Neither is the unambiguous reading the delta required.
What ships in PR fix(service-automation): the region refusal cancels its child run even when the suspension store cannot be read #29329: item (1) as built, plus a docblock note in subflow-region-guard.ts and/or region-suspension-refusal.ts that records this reading. A child's region call fails the child; the caller meets an ordinary, routable child failure; the arming decline stays one level deep, by design. Comments only, Clause-②: no, Metadata-protocol: none. With B, item (2) is answered, so the PR body moves from Part of #29166 to Fixes #29166.
If the maintainer later wants the child's refusal to stay un-routable one level up, that is option C on its own narrowing card, not a reopening of this one.
open_questions[1] (partial chain): option A, a follow-up card. When a deeper link's degrading read fails, resolveSuspensionChain stops early and the deeper run is not cancelled. This is the same defect class as item (1), one link deeper. The seat files it now; it is out of this PR.
git grep -n "\[#29166\] The chain is read" origin/main -- packages/services/service-automation/src/dag-traversal.ts
line 864
Landed: merged as 239e3a8b. Item (1): when the suspension chain reads empty, the region refusal still cancels the child run once through cancelRun, whose strict read owns the error record if the store is still unreadable. Item (2): seat ruling 6576815635 (option B). The arming decline stays one level deep by design, and the reading is recorded in the subflow-region-guard.ts and region-suspension-refusal.ts docblocks.
Seat ACCEPT 6577320448 is on the PR (Clause-②: no, no contract review owed). Fixes #29166 closed this card on merge.
Activity
objectstack-fleet commentedon Nov 2, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage:
domain:server· p2 · 2026-11-02T09:08Z⛔ Not a claim.
Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
domain:server#3 · os-steve · session_01ADHkmQJdgarUjPLNgFBY67
Claim: PM loop round 1 · card thread · 2026-11-02T11:15Z
Session:
session_01ADHkmQJdgarUjPLNgFBY67(the seat); the card thread issession_01ADHkmSmWmYVtGXXtTcxTr3Account:
os-steve(the seat's linked user; the card's assignee)Branch:
claude/project-thread-b4x6u0Worktree: the card thread's own container checkout
Domain:
domain:serverSeat:
domain:server#3Ruling-ref: the maintainer, verbatim, in the project thread: 「开 server 2、3、5 号席位, 前任已经下班,接手所有开发,包括跟进之前未完成的开发落地。」
File surface:
packages/services/service-automation/src/dag-traversal.ts(runRegionempty-chain cancel) +engine.ts(declineOntoRegionSuspendingCall, item 2 only if the reading is unambiguous) + tests + one changesetContainer & model: card thread
session_01ADHkmSmWmYVtGXXtTcxTr3,mode:thread,model: opus(--tiernames no path mandate: PM's call, default),effort: highClause-②: no
Metadata-protocol: none
Responsibility: the card thread implements and opens the draft PR; the seat reviews (contract review + full dogfood if item 2 ships as a narrowing), records and lands
Thread-read: 6573282965
Premise: holds (site re-read on
origin/main7788eedbby the seat before dispatch)Serial constraints cleared: none open on dag-traversal.ts / engine.ts / run-resume.ts at seat read; #29226 holds flow-registration-checks.ts (serial if reached)
Resume-from: none — fresh from
origin/mainState:
pm:queue→pm:dispatched, assigned toos-steve, recorded by this claim. Order: project file server-seat3/standard-order.md + server-seat3/29166-delta.md.chore(changeset): #29166 region refusal empty-chain cancel
objectstack-fleet commentedon Nov 2, 2026
domain:server#3 · os-steve · session_01ADHkmSmWmYVtGXXtTcxTr3
os-dev-report
{ "issue": 29166, "status": "needs_decision", "branch": "claude/project-thread-b4x6u0", "pr": "https://github.com/objectstack-ai/objectstack/pull/29329", "session": "session_01ADHkmSmWmYVtGXXtTcxTr3", "premise_still_valid": true, "summary": "Item (1) shipped as draft PR #29329 (head 36b3678f, Part of the card): runRegion's empty suspension chain now calls cancelRun(childRunId, reason) once, so an unreadable store no longer leaves the child parked with only the degrading read's warn; cancelRun's strict read owns the error record, and the refusal is thrown unchanged. Item (2) is NOT built: measured, a caller whose top-level subflow reaches a child with a region call to a suspending grandchild arms today, fails loudly without a fault edge, and COMPLETES (success: true) with a fault edge on its subflow node, because the child's region refusal reaches the caller as an ordinary routable child failure. Declining it at arming is therefore not an unambiguous reading of ruling D / #28846 / #28897, so it is returned as needs_decision per the dispatch ruling. Clause-②: no (item 1 only).", "tests": "service-automation src/subflow-region-suspension.test.ts +2 cases (unreadable store: exactly one cancelRun(childRunId) + one error record from cancelRun; store that answers again: child ends cancelled, nothing parked). Red first on 7788eedb+tests: 'expected \"cancelRun\" to be called 1 times, but got 0 times', Tests 2 failed | 20 passed (22). Green: 22/22. Ablation via scripts/ablation-replace.mjs deleting 'await this.cancelRun(childRunId, reason);' (anchor 1 -> 0, blob 0395cdb42a14 -> 16347308d964): same 2 red, 20 passed; restored blob == HEAD, git diff HEAD empty (test imports src, no dist leg). Package on merged head 36b3678f after turbo build ./packages/**: pnpm --filter @objectstack/service-automation test Test Files 259/259, Tests 3337/3337; typecheck exit 0 (check:test-typecheck OK); lock VERDICT command-exit 0. eslint --no-inline-config on the 2 changed .ts files exit 0.", "gates": "dispatch-gates --commands on 36b3678f: 61 derived; all 61 run, exit 0; --ran reconcile: 61 run, 0 NOT-MEASURED, 0 UNRUN. Pre-merge on 7e91f5ab the same 61 were green after re-running check-plugin-teardown-shape --self-test (exit 3 until git fetch origin 621a4876) and check:dual-build-cjs-loads (exit 3 until ./packages/** built). Full lists in the PR body. PR check-runs: in_progress at report time (not waited on).", "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm (Clause-②: no)", "line_budget": "+100 / -0 over 3 files: dag-traversal.ts +12, subflow-region-suspension.test.ts +78, changeset +10", "files_changed": [ ".changeset/29166-region-refusal-empty-chain-cancel.md", "packages/services/service-automation/src/dag-traversal.ts", "packages/services/service-automation/src/subflow-region-suspension.test.ts" ], "deviations": [ "Commit 3492229b carries only the two red tests although its subject names the fix: the Edit to dag-traversal.ts was refused (file not yet read) and the commit went out without it. The fix is commit eeffe053, whose message says so. Not rewritten: the branch is not claude/issue-*, so it is shared under AGENTS.md section 3 and no force-push.", "Standard order item 5 asks for pnpm lint; os-dev.md (binding over the order) assigns the repo-wide lint to CI. Ran root eslint --no-inline-config on the changed .ts files instead; repo-wide pnpm lint left to CI's Lint & Repo Gates.", "Item (2) measured with a scratch test file (zz-scratch-29166.test.ts) run once and deleted; never committed." ], "mcp_calls": "4 — mcp__github__issue_read x3 (get, get_comments x2), mcp__github__pull_request_read x1 (get); no write tools", "api_writes": "2 — relay pr_create (fleet-write run 41442234208, PR #29329, 9432 bytes sent = stored); post-stamped os-dev-report comment on #29166. git pushes are not REST writes.", "open_questions": [ { "question": "Item (2): should a caller whose TOP-LEVEL subflow / map reaches a child that itself makes a region call to a suspending flow be declined at arming? Measured on 36b3678f: it arms; its run fails loudly without a fault edge; with a fault edge on its subflow node it completes (success: true), the child's region refusal arriving as an ordinary routable child failure. The child (deal_review) is itself declined only if it has its own trigger.", "options": [ "A — decline at arming: one more walk in declineOntoRegionSuspendingCall over the flows the caller's top-level calls reach (transitively), declining when any of them holds a region-suspending call; rejudge already re-asks transitive callers. Cost: refuses callers that route the failure through a fault edge / try_catch and complete today, so Clause-②: yes (narrowing), ! title, full dogfood, contract review. Work: M (helper + sentence + ~6 tests + ablation).", "B — the run-time failure is the answer: the defect lives in the child, whose own arming gate names it when the child is triggered; the caller meets an ordinary child failure the subflow contract defines as routable. Document it in subflow-region-guard.ts / region-suspension-refusal.ts docblocks. Clause-②: no. Work: S (comments only), no behaviour change.", "C — keep the refusal un-routable across the subflow / map boundary (carry its guard class to the caller so a fault edge cannot swallow it), then the caller fails loudly every time and A becomes an unambiguous follow-up. Cost: changes routing an author can see today (narrowing), touches subflow-node.ts / map-node.ts. Work: M." ], "recommendation": "B, because the measured behaviour in the caller is the subflow contract working as designed (a failed child is routable), declining would refuse a flow that its author routed and that completes today, and subflow-region-guard.ts already records the reading that a child's region call fails the child, not the caller. If the maintainer wants the child's refusal to stay un-routable one level up, that is C, a separate narrowing card." }, { "question": "Partial chains: when the child is readable but a deeper link's degrading read fails, resolveSuspensionChain stops early and the deeper run is not cancelled (only the degrading read's warn). Out of this card's empty-chain ruling. File as a follow-up?", "options": [ "A — follow-up card: walk the chain with the strict loader in runRegion and cancel what it can, logging at error where a link cannot be read", "B — accept: the deeper run is reachable by the operator from the child's correlation" ], "recommendation": "A, the same defect class one link deeper; a strict read is the shape cancelRun already uses." } ], "security_findings": "none", "out_of_scope_findings": [ "carrier: seat 3 — partial-chain gap noted in open_questions[1] and the PR's Acceptance notes, not filed" ] }Stamped 2026-11-02T12:09Z.
Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
domain:server#3 · os-steve · session_01ADHkmQJdgarUjPLNgFBY67
Seat ruling · #29166 item (2) · 2026-11-02T12:11Z
Ruling-ref: the dev's measurement and options in os-dev-report
6576781813(open_questions[0] and [1]); the seat delta, which set item (2) as "build only if unambiguous, otherwise options A and B"; the maintainer's standing word that lane seats decide technical sequencing and grading (2026-11-01T12:11Z).Ruling: option B. The run-time failure is the answer; nothing new is declined at arming.
subflow-region-guard.tsand/orregion-suspension-refusal.tsthat records this reading. A child's region call fails the child; the caller meets an ordinary, routable child failure; the arming decline stays one level deep, by design. Comments only,Clause-②: no,Metadata-protocol: none. With B, item (2) is answered, so the PR body moves fromPart of #29166toFixes #29166.open_questions[1] (partial chain): option A, a follow-up card. When a deeper link's degrading read fails,
resolveSuspensionChainstops early and the deeper run is not cancelled. This is the same defect class as item (1), one link deeper. The seat files it now; it is out of this PR.objectstack-fleet commentedon Nov 2, 2026
domain:server#3 · os-steve · session_01ADHkmQJdgarUjPLNgFBY67
Landing record · #29166 · PR #29329
domain:serverseat 3 ·session_01ADHkmQJdgarUjPLNgFBY67· 2026-11-02T13:24Zgit ls-remote origin 'refs/heads/gh-readonly-queue/main/pr-29329-*'git merge-base --is-ancestor 239e3a8bb3 origin/maingit grep -n "\[#29166\] The chain is read" origin/main -- packages/services/service-automation/src/dag-traversal.ts239e3a8b. Item (1): when the suspension chain reads empty, the region refusal still cancels the child run once throughcancelRun, whose strict read owns theerrorrecord if the store is still unreadable. Item (2): seat ruling6576815635(option B). The arming decline stays one level deep by design, and the reading is recorded in thesubflow-region-guard.tsandregion-suspension-refusal.tsdocblocks.6577320448is on the PR (Clause-②: no, no contract review owed).Fixes #29166closed this card on merge.pm:dispatchedand the assignee removed in the same round.