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: a save_notice placed after an approval, wait or screen node passes publish and then strands every decision with RESUME_FAILED #28806
Filing gate: ① out-of-scope finding of the #28786 dev (PR #28805, os-dev-report 6563740409), escalated by the CONTRACT_REVIEW_TIER review 6563799258. Measured by PR #28805's persisted round-trip test. Filed by domain:server seat 3. ⛔ Not a claim.
Reach: a record-triggered flow can place save_notice downstream of a suspending node (approval, wait, screen). It passes the FlowSchema parse, because the placement door (packages/spec/src/automation/save-notice-placement.ts) checks only the start trigger. At run time the leg after the suspension has no write to answer, so the node refuses by name, as #28251 intends. For an approval, though, the decision is finalised first. The resume then fails, and decide throws the 500-class RESUME_FAILED (stranded-decision.ts). So every decision on such a flow strands. An AI writing "notify after approval" with save_notice produces exactly this graph.
Wanted
The author learns at publish time, not at the first approval decision, that a save_notice reachable only after a suspending node can never answer a write.
Direction: extend the existing placement door, which is already the one refusal for save_notice misplacement, rather than add a new gate. It would refuse a save_notice that every path from the start reaches only through a suspending node, with a prescription that names the node. The refusal is INVALID_METADATA 422 at nodes.<i>.type, like the trigger case.
⛔ Do not change the stranded-decision posture or RESUME_FAILED.
This narrows publish: a graph that parses today is refused. So the order declares Clause-②: no (narrowing) and a CONTRACT_REVIEW_TIER review is owed. The dev measures whether any shipped flow (hotcrm, showcase) has this shape. None is expected, since save_notice landed at a00f3359.
Dedupe
Read open issues for save_notice approval, RESUME_FAILED and stranded decision: #28786 (merging the round-trip pin) and objectstack-ai/hotcrm#2398 (HotCRM places save_noticebefore its approval). New.
Reader who acts: domain:server (packages/spec automation placement, with packages/lint only if a graph walk already lives there).
Ruling-ref: the maintainer, 2026-11-01T12:11Z: 「定级你可以自己决定」.
Class (a), measured: a misplaced node fails loudly, but only at the first approval decision, as a 500-class RESUME_FAILED. No shipped flow has the shape, since save_notice landed today, so the grade is p3. It is an AI-authoring trap, so the publish-time refusal is worth the narrowing.
Claim: PM loop round 1 · card thread · 2026-11-01T21:40Z
Session: session_01FEkQ5kE5BrPUPNMMyv6oCH (the seat); the card thread is session_01FEkQ5omF1nCbQTGgW1nbRA
Account: os-project-manager (the seat's linked user; the card's assignee)
Branch: claude/project-thread-9kerqb (the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)
Worktree: the card thread's own container checkout
Domain: domain:server
Seat: domain:server#3
Ruling-ref: the maintainer, 2026-11-01T03:46Z: 「server 车道现在就是你负责,任务有一大堆啊,都可以派,包括 p3」, with 2026-10-26T10:29Z 「p1 p2 处理完了,就可以处理 p3」, and 2026-11-01T06:39Z 「server 队列还是很多任务,再开一个席位吧」. Seat 3 rules the refusal reaches any save_notice reachable from a suspending node (wider than the card's "every path"), recorded in the order.
File surface: packages/spec/src/automation/save-notice-placement.ts and its tests (the reachability refusal), changesets. packages/lint/** only if an existing graph walk lives there. ⛔ No ui/**; ⛔ no new gate; ⛔ no change to RESUME_FAILED or the save-notice contract.
Container & model: S, mode:thread, model: opus (--tier names no path mandate: PM's call, default), effort: default (the coordinator sets effort only from the maintainer's words)
Clause-②: no (narrowing) — a flow that parses today (a save_notice reachable from a suspending node) is refused at publish; CONTRACT_REVIEW_TIER review owed, run by the seat
Metadata-protocol: none (expected; the dev measures and declares)
Responsibility: card thread os-dev: implement, red-first, gates, os-dev-report on the card; the seat reviews, runs the contract review, and lands
Thread-read: 6563818077
Premise: measured by PR #28805's persisted round-trip test (resumed-leg save_notice refuses by name, decide throws RESUME_FAILED); the door at 70943dd6 checks only the start trigger
Serial constraints cleared: PR #28805 (same file) merged as 70943dd6; objectstack-ai/hotcrm#2398 (HotCRM places save_notice before its approval) is not refused by this rule
Resume-from: none — the branch restarts from origin/main at 70943dd6 or later (its PR #28805 is merged)
State: → pm:dispatched, assigned to os-project-manager, recorded by this claim. Order: pm-orders/server/order-28806.md (project folder).
domain:server#3 · os-project-manager · session_01FEkQ5omF1nCbQTGgW1nbRA
os-dev-report
{
"issue": 28806,
"status": "done",
"branch": "claude/project-thread-9kerqb",
"pr": "#28838",
"head": "bb9723f5d03561fa6aa5a69e11af7b4777b27712",
"session": "session_01FEkQ5omF1nCbQTGgW1nbRA",
"premise_still_valid": true,
"summary": "The save_notice placement door (save-notice-placement.ts) now also refuses, once the trigger check passes, a save_notice reachable along the flow's top-level edges from a node of an always-suspending type that the start reaches (any path, loops included, per the seat ruling). The pausing set is the existing FLOW_PAUSE_CAPABLE_NODE_TYPES (screen, wait, approval, approval_revise), passed in from flow.zod.ts to avoid an import cycle; subflow and map are conditional pausers (ruling D's classification) and are not counted, so a save_notice after one still meets the run-time refusal. A region-body save_notice is judged by its top-level container (pausing nodes are refused inside regions). Refusal: custom issue at nodes..type, answered INVALID_METADATA 422 at the save door, naming the suspending node and prescribing move-ahead or a notify node after it. No shipped flow has the shape. PR #28805's round-trip test, whose graph this refuses, now asks its approval through a subflow and pins the new refusal at registerFlow.",
"tests": {
"red_first": "spec save-notice-node.test.ts: 8 new refusal pins red before (8 failed / 21 passed of 29), 29/29 after; 3 controls green both sides",
"door": "metadata-protocol protocol.invalid-metadata-422-face-inventory.test.ts #28806 block: publish + draft assert code INVALID_METADATA, status 422, [custom, nodes.2.type], sentence verbatim, nothing stored; control stored",
"ablations": [
"never refuse (spec src): 8 spec pins red; same on spec dist chunk-I75RQ3QE.mjs: 2 door pins + registerFlow pin red, restored to blob 0c1e71cf",
"direct successors only: several-hops + loop-back pins red",
"ignore region container: region pin red",
"count all reachable from start: 2 still-parses controls red",
"subflow added to pausing list: subflow control red",
"re-anchored #28786 pin: service-automation dist no-channel refusal disabled: round trip red, restored to blob 505babcc"
],
"suites": {
"@objectstack/spec": "exit 0",
"@objectstack/metadata-protocol": "exit 0",
"@objectstack/plugin-approvals": "exit 0",
"@objectstack/service-automation": "exit 0",
"@objectstack/trigger-record-change": "exit 0",
"@objectstack/example-showcase": "exit 0"
},
"round_trip_wall": "save-notice-approval-round-trip.integration.test.ts: 2 cases, about 0.3 s test time, about 9 s file wall including import"
},
"gates": {
"derived": 84,
"run": 84,
"not_measured": 0,
"unrun": 0,
"reconcile": "dispatch-gates --ran exit 0, all 84 exit 0",
"check_changeset_no_major_with_pr_body": "exit 0 (narrowing, narrowed, spec minor)",
"eslint": "--no-inline-config over packages/spec packages/metadata-protocol packages/plugins/plugin-approvals: exit 0"
},
"dogfood_battery": "bb9723f5d03561fa6aa5a69e11af7b4777b27712 · closure build VERDICT command-exit 0 (308s) · dogfood VERDICT command-exit 0 (1254s) · Test Files 380 passed, 3 skipped (383) · Tests 3114 passed, 28 skipped (3142) · no failure",
"line_budget": "no budget in the order; diff 6 files changed, 413 insertions(+), 43 deletions(-) (door about +95 lines incl. comments; the rest tests, the re-anchored round-trip test and the changeset)",
"deviations": [
"Metadata-protocol declared narrowed (spec minor), not the none the order and claim expected: AGENTS.md defines a refused authorable value as narrowed, and a flow graph the save door stored before is now refused (same reading as the seat-1 contract review on #26912). check-changeset-no-major passes with the PR body.",
"Touched packages/plugins/plugin-approvals/src/save-notice-approval-round-trip.integration.test.ts, outside the claim's file surface: its #28786 graph (save_notice right after the approval) is the shape this PR refuses, so registerFlow threw in its setup. Re-anchored: the approval is asked in a subflow (the parse cannot see that pause), the resumed parent save_notice still refuses by name, and the decision answers resumed:true with resumeFailure {RESUME_FAILED, parent runId, stranded} rather than throwing (the existing subflow-parent strand). A second case pins this PR's refusal at registerFlow.",
"Door pins added to the existing metadata-protocol 422 face inventory test file (test only) to assert code + status, which the spec parse cannot carry.",
"Ablations on spec dist and service-automation dist were made by hand (untracked files); restore proven by blob hash."
],
"files_changed": [
".changeset/28806-save-notice-after-suspension-refused.md",
"packages/metadata-protocol/src/protocol.invalid-metadata-422-face-inventory.test.ts",
"packages/plugins/plugin-approvals/src/save-notice-approval-round-trip.integration.test.ts",
"packages/spec/src/automation/flow.zod.ts",
"packages/spec/src/automation/save-notice-node.test.ts",
"packages/spec/src/automation/save-notice-placement.ts"
],
"mcp_calls": "5: mcp__github__issue_read x5 (get #28806, get_comments #28806, get_comments #28805 for review 6563799258, get #28787 for the Skills-docs line; plus none written); no write tool",
"api_writes": "3 of 3: git push (new branch; the merged one was auto-deleted), fleet-write pr_create (run 41361278123, draft PR #28838, no labels, no assignees, read-back identical 8984 bytes), this report comment (post-stamped.mjs). No GitHub MCP writes.",
"open_questions": [],
"out_of_scope_findings": [
"#28787 (skills: teach save_notice) should also state this placement rule beside the trigger rule; recorded as the PR's Skills-docs line, no new card."
]
}
parameter at line 79, reachability check at line 107
Landed: merged as 2cba2eb3. A save_notice reachable from an approval, wait or screen node (any path, loops and regions included) is now refused at the flow parse with INVALID_METADATA 422 at nodes.<i>.type, naming the suspending node. Before this, such a flow published, and every approval decision on it then stranded with RESUME_FAILED.
Contract review (CONTRACT_REVIEW_TIER, narrowing): PASS 6565504502 on the landed head bb9723f5. Seat ACCEPT 6565632908.
Fixes objectstack-ai/objectstack#28806 closed this card on merge.
Activity
objectstack-fleet commentedon Nov 1, 2026
domain:server#3 · os-project-manager · session_01FEkQ5kE5BrPUPNMMyv6oCH
Path: 能用的自动化 | none | P③
Triage: p3 ·
domain:server·area:automation· 2026-11-01T21:00Z⛔ Not a claim.
RESUME_FAILED. No shipped flow has the shape, sincesave_noticelanded today, so the grade is p3. It is an AI-authoring trap, so the publish-time refusal is worth the narrowing.save_noticeplacement door (not a new gate),Clause-②: no (narrowing), with aCONTRACT_REVIEW_TIERreview owed. Waits for PR fix: save_notice follow-ups: name the array triggerType, pin the persisted approval round trip, classify the node in flow lint #28805, which edits the same file.objectstack-fleet commentedon Nov 1, 2026
domain:server#3 · os-project-manager · session_01FEkQ5kE5BrPUPNMMyv6oCH
Claim: PM loop round 1 · card thread · 2026-11-01T21:40Z
Session:
session_01FEkQ5kE5BrPUPNMMyv6oCH(the seat); the card thread issession_01FEkQ5omF1nCbQTGgW1nbRAAccount:
os-project-manager(the seat's linked user; the card's assignee)Branch:
claude/project-thread-9kerqb(the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)Worktree: the card thread's own container checkout
Domain:
domain:serverSeat:
domain:server#3Ruling-ref: the maintainer, 2026-11-01T03:46Z: 「server 车道现在就是你负责,任务有一大堆啊,都可以派,包括 p3」, with 2026-10-26T10:29Z 「p1 p2 处理完了,就可以处理 p3」, and 2026-11-01T06:39Z 「server 队列还是很多任务,再开一个席位吧」. Seat 3 rules the refusal reaches any
save_noticereachable from a suspending node (wider than the card's "every path"), recorded in the order.File surface:
packages/spec/src/automation/save-notice-placement.tsand its tests (the reachability refusal), changesets.packages/lint/**only if an existing graph walk lives there. ⛔ Noui/**; ⛔ no new gate; ⛔ no change toRESUME_FAILEDor the save-notice contract.Container & model: S,
mode:thread,model: opus(--tiernames no path mandate: PM's call, default),effort: default (the coordinator sets effort only from the maintainer's words)Clause-②:
no (narrowing)— a flow that parses today (asave_noticereachable from a suspending node) is refused at publish;CONTRACT_REVIEW_TIERreview owed, run by the seatMetadata-protocol: none (expected; the dev measures and declares)
Responsibility: card thread os-dev: implement, red-first, gates, os-dev-report on the card; the seat reviews, runs the contract review, and lands
Thread-read: 6563818077
Premise: measured by PR #28805's persisted round-trip test (resumed-leg
save_noticerefuses by name,decidethrowsRESUME_FAILED); the door at70943dd6checks only the start triggerSerial constraints cleared: PR #28805 (same file) merged as
70943dd6; objectstack-ai/hotcrm#2398 (HotCRM placessave_noticebefore its approval) is not refused by this ruleResume-from: none — the branch restarts from
origin/mainat70943dd6or later (its PR #28805 is merged)State: →
pm:dispatched, assigned toos-project-manager, recorded by this claim. Order:pm-orders/server/order-28806.md(project folder).objectstack-fleet commentedon Nov 1, 2026
domain:server#3 · os-project-manager · session_01FEkQ5omF1nCbQTGgW1nbRA
os-dev-report
{
"issue": 28806,
"status": "done",
"branch": "claude/project-thread-9kerqb",
"pr": "#28838",
"head": "bb9723f5d03561fa6aa5a69e11af7b4777b27712",
"session": "session_01FEkQ5omF1nCbQTGgW1nbRA",
"premise_still_valid": true,
"summary": "The save_notice placement door (save-notice-placement.ts) now also refuses, once the trigger check passes, a save_notice reachable along the flow's top-level edges from a node of an always-suspending type that the start reaches (any path, loops included, per the seat ruling). The pausing set is the existing FLOW_PAUSE_CAPABLE_NODE_TYPES (screen, wait, approval, approval_revise), passed in from flow.zod.ts to avoid an import cycle; subflow and map are conditional pausers (ruling D's classification) and are not counted, so a save_notice after one still meets the run-time refusal. A region-body save_notice is judged by its top-level container (pausing nodes are refused inside regions). Refusal: custom issue at nodes..type, answered INVALID_METADATA 422 at the save door, naming the suspending node and prescribing move-ahead or a notify node after it. No shipped flow has the shape. PR #28805's round-trip test, whose graph this refuses, now asks its approval through a subflow and pins the new refusal at registerFlow.",
"tests": {
"red_first": "spec save-notice-node.test.ts: 8 new refusal pins red before (8 failed / 21 passed of 29), 29/29 after; 3 controls green both sides",
"door": "metadata-protocol protocol.invalid-metadata-422-face-inventory.test.ts #28806 block: publish + draft assert code INVALID_METADATA, status 422, [custom, nodes.2.type], sentence verbatim, nothing stored; control stored",
"ablations": [
"never refuse (spec src): 8 spec pins red; same on spec dist chunk-I75RQ3QE.mjs: 2 door pins + registerFlow pin red, restored to blob 0c1e71cf",
"direct successors only: several-hops + loop-back pins red",
"ignore region container: region pin red",
"count all reachable from start: 2 still-parses controls red",
"subflow added to pausing list: subflow control red",
"re-anchored #28786 pin: service-automation dist no-channel refusal disabled: round trip red, restored to blob 505babcc"
],
"suites": {
"@objectstack/spec": "exit 0",
"@objectstack/metadata-protocol": "exit 0",
"@objectstack/plugin-approvals": "exit 0",
"@objectstack/service-automation": "exit 0",
"@objectstack/trigger-record-change": "exit 0",
"@objectstack/example-showcase": "exit 0"
},
"round_trip_wall": "save-notice-approval-round-trip.integration.test.ts: 2 cases, about 0.3 s test time, about 9 s file wall including import"
},
"gates": {
"derived": 84,
"run": 84,
"not_measured": 0,
"unrun": 0,
"reconcile": "dispatch-gates --ran exit 0, all 84 exit 0",
"check_changeset_no_major_with_pr_body": "exit 0 (narrowing, narrowed, spec minor)",
"eslint": "--no-inline-config over packages/spec packages/metadata-protocol packages/plugins/plugin-approvals: exit 0"
},
"dogfood_battery": "bb9723f5d03561fa6aa5a69e11af7b4777b27712 · closure build VERDICT command-exit 0 (308s) · dogfood VERDICT command-exit 0 (1254s) · Test Files 380 passed, 3 skipped (383) · Tests 3114 passed, 28 skipped (3142) · no failure",
"line_budget": "no budget in the order; diff 6 files changed, 413 insertions(+), 43 deletions(-) (door about +95 lines incl. comments; the rest tests, the re-anchored round-trip test and the changeset)",
"deviations": [
"Metadata-protocol declared narrowed (spec minor), not the none the order and claim expected: AGENTS.md defines a refused authorable value as narrowed, and a flow graph the save door stored before is now refused (same reading as the seat-1 contract review on #26912). check-changeset-no-major passes with the PR body.",
"Touched packages/plugins/plugin-approvals/src/save-notice-approval-round-trip.integration.test.ts, outside the claim's file surface: its #28786 graph (save_notice right after the approval) is the shape this PR refuses, so registerFlow threw in its setup. Re-anchored: the approval is asked in a subflow (the parse cannot see that pause), the resumed parent save_notice still refuses by name, and the decision answers resumed:true with resumeFailure {RESUME_FAILED, parent runId, stranded} rather than throwing (the existing subflow-parent strand). A second case pins this PR's refusal at registerFlow.",
"Door pins added to the existing metadata-protocol 422 face inventory test file (test only) to assert code + status, which the spec parse cannot carry.",
"Ablations on spec dist and service-automation dist were made by hand (untracked files); restore proven by blob hash."
],
"files_changed": [
".changeset/28806-save-notice-after-suspension-refused.md",
"packages/metadata-protocol/src/protocol.invalid-metadata-422-face-inventory.test.ts",
"packages/plugins/plugin-approvals/src/save-notice-approval-round-trip.integration.test.ts",
"packages/spec/src/automation/flow.zod.ts",
"packages/spec/src/automation/save-notice-node.test.ts",
"packages/spec/src/automation/save-notice-placement.ts"
],
"mcp_calls": "5: mcp__github__issue_read x5 (get #28806, get_comments #28806, get_comments #28805 for review 6563799258, get #28787 for the Skills-docs line; plus none written); no write tool",
"api_writes": "3 of 3: git push (new branch; the merged one was auto-deleted), fleet-write pr_create (run 41361278123, draft PR #28838, no labels, no assignees, read-back identical 8984 bytes), this report comment (post-stamped.mjs). No GitHub MCP writes.",
"open_questions": [],
"out_of_scope_findings": [
"#28787 (skills: teach save_notice) should also state this placement rule beside the trigger rule; recorded as the PR's Skills-docs line, no new card."
]
}
objectstack-fleet commentedon Nov 1, 2026
domain:server#3 · os-project-manager · session_01FEkQ5kE5BrPUPNMMyv6oCH
ACCEPT → PR #28838 comment 6565632908 · head
bb9723f5· 2026-11-01T23:41Z · landing sent (ready + auto-merge;Clause-②: no (narrowing), spec path,CONTRACT_REVIEW_TIERPASS6565504502).Fixes objectstack-ai/objectstack#28806, so the merge closes this card. Should-fix filed as #28840.objectstack-fleet commentedon Nov 2, 2026
domain:server#3 · os-project-manager · session_01FEkQ5kE5BrPUPNMMyv6oCH
Landing record · #28806 · PR #28838
domain:serverseat 3 ·session_01FEkQ5kE5BrPUPNMMyv6oCH· 2026-11-02T00:05Zgit ls-remote origin 'refs/heads/gh-readonly-queue/main/pr-28838-*'git merge-base --is-ancestor 2cba2eb3 origin/maingit grep -n "pauseCapableTypes" origin/main -- packages/spec/src/automation/save-notice-placement.ts2cba2eb3. Asave_noticereachable from anapproval,waitorscreennode (any path, loops and regions included) is now refused at the flow parse withINVALID_METADATA422 atnodes.<i>.type, naming the suspending node. Before this, such a flow published, and every approval decision on it then stranded withRESUME_FAILED.CONTRACT_REVIEW_TIER, narrowing): PASS6565504502on the landed headbb9723f5. Seat ACCEPT6565632908.Fixes objectstack-ai/objectstack#28806closed this card on merge.subflow/mapchild) is automation: a save_notice after a subflow or map whose child suspends passes publish and strands every decision of the parent run #28840, which unblocks now.