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
Record action menu offers a member an action whose write they are refused (Recalculate Estimate), and the refusal toast is the raw flow-engine trace ("Node 'recalc_each' failed: map … subflow … update_record(showcase_task) failed …") #27487
Found in the 18.3.0 release-candidate browser walkthrough (wave anchor #27377, card W3-UX), at framework main @ c4c515ad, Console built in-repo from the same commit.
Repro (fresh boot)
Boot examples/app-showcase with --seed-admin. As admin, invite member1@example.com with role member (POST /api/v1/auth/organization/invite-member). Sign that address up, then sign in as the member through the login form. Viewport 1440×900.
Open Tasks → Audit current IA. There is no Edit button, which is correct: the member may not edit tasks.
Open the header ⋯ menu. Recalculate Estimate and Recalculate Selection are offered. Click Recalculate Estimate.
Expected
Either the action is not offered to someone who cannot perform its write, or it is refused in plain words, e.g. "You don't have permission to update this task." The record-save refusal path already does this.
Actual
A red toast shows the engine's trace:
Node 'recalc_each' failed: map 'recalc_each': item 0 (subflow 'showcase_recalc_task_estimate') failed: Node 'write_estimate' failed: update_record(showcase_task) failed: You do not have permission to perform this action. Contact your administrator if you need access.
Related, closed: #24914 (the same raw node text from a screen flow) and its console half #25938 (FlowRunner renders the inner refusal). The record action door, when the action is flow-backed and its map/subflow write is refused, still surfaces the whole chain.
Read fresh. The record action menu offers a member a flow-backed action whose write they are refused. The refusal toast is the raw flow-engine node and subflow trace. The refusal is correct, so this is toast copy and UX: p3.
Claim: PM loop round 1
Session: session_01Utp6CbDAucNyQ4UtzCJKyh
Account: os-bill
Branch: claude/project-thread-p6onta (the card thread's designated push branch, the recorded platform exception to claude/issue-<n>-<slug>)
Worktree: objectstack-issue-27487
Domain: domain:ui
Seat: domain:ui#4
File surface: ui/packages/app-shell/src/utils/flowResponse.ts, utils/flowLaunch.ts, the record action door call site the dev measures (named in the report; RecordDetailView.tsx at most that one call-site line, declared), tests beside, root .changeset/; ⛔ packages/** outside ui/ (stop on breach; explain in the report)
Container & model: M, mode:thread, model: TIER_DEFAULT (dispatch-gates --tier on 72c01528: no path-derived mandate), effort: high
Clause-②: no
Metadata-protocol: none
Responsibility: ui code renders the flow refusal chain verbatim on the action door | the inner-refusal extraction of #25938 already covers screen flows (FlowRunner) | every console member who runs a flow-backed record action they may not write, today
Thread-read: 6496182888
Premise: holds
Serial constraints cleared: no open PR edits flowResponse.ts / flowLaunch.ts (open PR file scan at 14:31Z)
Dispatched 2026-10-29T14:33Z to a card thread of the seat's project; the order lives in the project. ⛔ The thread posts no claim and writes no assignee.
Split · 2026-10-29T14:38Z · item 2 of this card (the server half: map / subflow nodes drop the child run's innerRefusal / userMessage, measured by the card thread on origin/main4a00b2b7) is now #27601. This card keeps item 1: the console api action door (useConsoleActionRuntime.tsxapiHandler) reads the refusal the server sends, through flowResponse.ts's existing failed-arm extraction. Its PR lands as Refs #27487 (item 1), not Fixes, because the showcase repro stays raw until #27601 lands.
{
"issue": 27487,
"status": "done",
"branch": "claude/project-thread-p6onta",
"pr": "https://github.com/objectstack-ai/objectstack/pull/27630",
"session": "session_01Utp6Ca8en1EvoQ3voL2nWm",
"premise_still_valid": true,
"summary": "Item 1 only (seat ruling: Refs, not Fixes; server half is #27601). Measured on origin/main 4a00b2b7: showcase Recalculate Estimate is type api aimed at a declared type flow endpoint, so it never reaches flowLaunch/interpretFlowResponse. The record page door (RecordDetailView apiHandler, header more menu) read error.message and the shared runtime door (useConsoleActionRuntime apiHandler) read actionErrorDetail, so the endpoint's 400 FLOW_FAILED showed the node chain even one level deep. flowResponse.ts now exports apiActionFailureMessage: on 400 FLOW_FAILED it reads the envelope through the existing failed-arm rule (errorMessage, userMessage, innerRefusal rendered as the record form renders it, then message), sharing one helper with interpretFlowResponse's 400 arm; flowFailureMessage takes the permission line locale explicitly and the doors pass the host language. Every other answer reads actionErrorDetail as before. No new message map. Server half (map-node.ts and subflow-node.ts drop the child run's innerRefusal/userMessage, so the showcase nested chain carries none) reported to the seat, filed by it as #27601. PR opened draft with no assignee and no review request (maintainer rule), so the harness after-PR assign/review step was skipped on purpose.",
"tests": "head 3219ed5b: pnpm --dir ui exec vitest run on flowResponse.test.ts, useConsoleActionRuntime.apiFlowRefusal-27487.test.tsx, RecordDetailView.apiFlowRefusal-27487.test.tsx, useConsoleActionRuntime.apiNewTabUrl-26747.test.tsx, RecordDetailView.headerApiInterpolation.test.tsx, flowLaunch.translation-12075.test.tsx: Test Files 6 passed, Tests 85 passed, lock VERDICT command-exit 0. Ablation on 28ccaf1a via scripts/ablation-replace.mjs (source-imported suite, no dist in path; anchor 1 -> 0, blob 94ecd0cc1709 -> c78d615e0484), marker !'ABLATED_27487' short-circuiting the FLOW_FAILED test in apiActionFailureMessage: 5 red (3 carrier-reading flowResponse cases, both door cases), 69 green including every control; restore blob == HEAD, git diff HEAD empty.",
"gates": "dispatch-gates --commands --repo objectstack-ai/objectstack on 3219ed5b: 34 derived, all exit 0 (incl. check-sdui-manifest.mjs and --self-test, check:nul-bytes); --ran reconciliation: 34 run, 0 NOT-MEASURED, 0 UNRUN. Outside derivation, exit 0: pnpm --filter @object-ui/app-shell run type-check (VERDICT command-exit 0); pnpm --dir ui run check:new-line-citations (VERDICT 0 new citations). CI on 3219ed5b at report time: Governed Surface Queue Guard success; Lint, Type Check, Check Changeset, PR-body guards, Check PR Size in_progress; UI test/build lanes not yet listed.",
"dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
"mcp_calls": "0 — no MCP GitHub tool used",
"api_writes": "2 — fleet-write relay pr_create (POST /repos/objectstack-ai/objectstack/dispatches, run 40900749272, PR 27630); this report comment via post-stamped (POST /repos/objectstack-ai/objectstack/issues/27487/comments). git push x2 not counted (not REST).",
"line_budget": "7 files, +497 / -19 (dispatch-gates count), under the 3000 human-merge threshold",
"files_changed": [
".changeset/27487-api-action-flow-refusal.md",
"ui/packages/app-shell/src/utils/flowResponse.ts",
"ui/packages/app-shell/src/utils/__tests__/flowResponse.test.ts",
"ui/packages/app-shell/src/hooks/useConsoleActionRuntime.tsx",
"ui/packages/app-shell/src/hooks/__tests__/useConsoleActionRuntime.apiFlowRefusal-27487.test.tsx",
"ui/packages/app-shell/src/views/RecordDetailView.tsx",
"ui/packages/app-shell/src/views/RecordDetailView.apiFlowRefusal-27487.test.tsx"
],
"deviations": [
"The measured action door is two call sites, not one: useConsoleActionRuntime.tsx apiHandler (named by the seat) and RecordDetailView.tsx apiHandler, which is the record header more-menu door of the card's repro. RecordDetailView.tsx (seat 1's record-page chain) changes in three places: the call-site line (two old lines replaced by one), its import line, and language added to that handler's useCallback dependency list.",
"PR body attribution line links the seat thread named in the dispatch order (thread=cmsg_016iMXWSZDzqWRWvWzdNo8wgDFH4S5pR1C8YvYCWWsZfXx); this card thread's session context names thread=cmsg_016iMXWSZDzqWRWvWzdNo8wg5BvEPRX9fbnxy4ZVhm1oZT. Not PATCHed (dev writes the body once); the seat may rewrite it.",
"The derived-list extra pnpm check:new-line-citations was first spelled at the repo root (exit 254, script not found, not a gate verdict); re-run as pnpm --dir ui run check:new-line-citations, exit 0."
],
"open_questions": [],
"out_of_scope_findings": [
"carrier: #27601 · server half of this card (map-node.ts and subflow-node.ts drop the child run's innerRefusal/userMessage), filed by the seat",
"carrier: none · noted, not filed — by code reading only (not reproduced): both api handlers report success on a 200 whose outer envelope is success:true with data.success false; the declared flow endpoint keeps that 200 for an unclassified failure only; recorded in the PR acceptance notes"
]
}
Claim: PM loop round 1 · 2026-10-31T02:56Z
Session: session_01L9qibxgfM8p8t3fo6y9eFK (seat; card thread session_01L9qibvfRdVUxHLWydUgVyz)
Account: os-tesla
Branch: claude/project-thread-jb4ftv (card thread; the platform assigns the thread's branch)
Worktree: objectstack-27487
Domain: domain:ui
Seat: domain:ui#2
File surface: none expected (live measure); a measured ui fix would be the action door under ui/packages/app-shell/src/ (flowResponse.ts), its tests, root changeset — reported before code (stop on breach; explain in the report)
Container & model: S live measure, mode:thread, model: TIER_DEFAULT (default judgment tier), effort: medium
Clause-②: no
Metadata-protocol: none
Responsibility: the server map/subflow refusal (fixed by #27601) and the console api action door (#27630) | the record-save refusal path | a member running a flow-backed record action whose write is refused
Thread-read: 6501460514
Premise: holds
Serial constraints cleared: none (Blocked-by #27601 CLOSED completed 2026-10-29T22:46Z — this claim is also the unlock: pm:blocked → pm:dispatched)
{
"issue": 27487,
"status": "done",
"branch": "claude/project-thread-jb4ftv",
"pr": null,
"session": "session_01L9qibvfRdVUxHLWydUgVyz",
"premise_still_valid": false,
"summary": "Live re-run of the card's repro on origin/main 69ce01d7 (contains #27630 f7ff693c and #27740 79d78b4e, both verified ancestors), Console built in-repo. Fresh app-showcase boot (--fresh, seed admin), admin invited member1@example.com as member, member signed up (session positions org_member, everyone) and signed in through the login form at 1440x900. Tasks record 'Audit current IA': no Edit button; header More actions menu offers Refresh, Open Docs, Recalculate Estimate, Recalculate Selection, Add to favorites, Share. Clicking Recalculate Estimate shows one red toast: \"You don't have permission to save this record.\" The node chain no longer reaches the toast. No ui code is needed; the card can close.",
"tests": "Live measure only, no code. Build: env -u OS_SKIP_DTS pnpm build under os-verify-lock, VERDICT command-exit 0 (115/115 tasks, 11m30s); pnpm console:build VERDICT command-exit 0. (A first OS_SKIP_DTS=1 build failed at @object-ui/auth with TS7016 on @objectstack/spec, as expected for ui packages without declarations.) Playwright (Chromium) script as member: Recalculate Estimate -> POST /api/v1/actions/showcase_task/showcase_recalc_estimate {recordId: wmcXdhieH7BDaObX, params: {}} -> 400, body success false, error.code FLOW_FAILED, error.message still the full chain (Node 'recalc_each' failed: map 'recalc_each': item 0 (subflow 'showcase_recalc_task_estimate') failed: Node 'write_estimate' failed: update_record(showcase_task) failed: You do not have permission to perform this action. ...), error.details.summary (map node recalc_each failure, acted 0, failed 1) and error.details.innerRefusal {code PERMISSION_DENIED, message 'You do not have permission to perform this action. Contact your administrator if you need access.'}. Toast text read from the DOM: [\"You don't have permission to save this record.\"]. Recalculate Selection from the same menu: same 400 envelope at /api/v1/actions/showcase_task/showcase_recalc_selection, same toast. Estimate field stays 8 (refusal still enforced).",
"gates": "No diff: dispatch-gates --commands --repo objectstack-ai/objectstack on 69ce01d7 exits 2 'this branch changes nothing against origin/main — nothing to derive'. No local gates owed, no CI run (no PR).",
"dogfood_battery": "n/a — no PR",
"mcp_calls": "2 — mcp__github__issue_read (get, get_comments) on #27487; 0 writes",
"api_writes": "1 — this os-dev-report comment via post-stamped (POST /repos/objectstack-ai/objectstack/issues/27487/comments). Nothing pushed.",
"line_budget": "0 files, +0 / -0",
"files_changed": [],
"deviations": [
"Local worktree branch is wt-27487-measure off origin/main 69ce01d7; nothing was committed or pushed, so the designated branch claude/project-thread-jb4ftv was never written.",
"The first Playwright run wrote two screenshots into the worktree root (script cwd); they were moved to the scratchpad before anything else ran, and git status --porcelain is empty."
],
"open_questions": [],
"out_of_scope_findings": [
"carrier: none · noted, not filed — the 'offered at all' half of Expected: a member who may not edit tasks is still offered Recalculate Estimate and Recalculate Selection in the record header More actions menu. Design question per the dispatch order, not built.",
"carrier: none · noted, not filed — toast copy reads \"You don't have permission to save this record.\" for an action that is not a save; it is the record-form permission line #27630 reuses for innerRefusal PERMISSION_DENIED. Plain words, so the card's Expected holds; the seat may judge whether action-specific wording is worth a card. Dedupe words: action refusal toast, save this record, innerRefusal permission copy.",
"carrier: none · noted, not filed — the 400 FLOW_FAILED envelope's top-level error.message still carries the whole node chain; the plain refusal travels only in error.details.innerRefusal. The Console reads the structured field; any other API caller that shows error.message still sees the chain.",
"carrier: none · noted — the record header door now posts to /api/v1/actions/OBJECT/ACTION (server action dispatch), not the declared /api/v1/apps/showcase/recalc endpoint the #27630 report measured; the refusal still reads plain through it."
]
}
Posted 2026-10-31T03:17Z · measured on origin/main 69ce01d.
Closed as fixed · 2026-10-31T03:18Z · the release note 6501460514's exit: "re-runs the repro … and closes it if the toast reads the plain refusal".
reading
value
measure
os-dev-report 6538305452: origin/main69ce01d7 (contains #27630 and #27740), Console built in-repo, fresh examples/app-showcase, member1@example.com as member, 1440×900
toast
one toast, "You don't have permission to save this record." for both Recalculate Estimate and Recalculate Selection
response
400 FLOW_FAILED from POST /api/v1/actions/showcase_task/showcase_recalc_estimate; the Console reads error.details.innerRefusal (PERMISSION_DENIED)
Delivered by #27630 (item 1, console door) and #27601 (item 2, server half). State: pm:dispatched removed, closed completed.
Not filed, listed for the record: the member is still offered both actions (a design question, Expected's first arm); the copy says "save this record" for an action; API callers that read error.message still see the node chain.
Activity
objectstack-fleet commentedon Oct 29, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: 审批与自动化 | none | P②
Triage: p3 ·
domain:ui·area:automation· 2026-10-29T12:05Z⛔ Not a claim.
FLOW_FAILEDenvelope is rendered by the record-save rule — the author'serrorMessage, thenuserMessage, then the declared inner refusal, thenmessage(the console half of objectstack#24914 ruling B′) #25938's existing extraction for the action door. No new message map.bug,pm:queue.Generated by Claude Code
objectstack-fleet commentedon Oct 29, 2026
domain:ui#4 · os-bill · session_01Utp6CbDAucNyQ4UtzCJKyh
Claim: PM loop round 1
Session:
session_01Utp6CbDAucNyQ4UtzCJKyhAccount:
os-billBranch:
claude/project-thread-p6onta(the card thread's designated push branch, the recorded platform exception toclaude/issue-<n>-<slug>)Worktree:
objectstack-issue-27487Domain:
domain:uiSeat:
domain:ui#4File surface:
ui/packages/app-shell/src/utils/flowResponse.ts,utils/flowLaunch.ts, the record action door call site the dev measures (named in the report;RecordDetailView.tsxat most that one call-site line, declared), tests beside, root.changeset/; ⛔packages/**outsideui/(stop on breach; explain in the report)Container & model:
M,mode:thread,model: TIER_DEFAULT(dispatch-gates --tieron72c01528: no path-derived mandate),effort: highClause-②: no
Metadata-protocol: none
Responsibility: ui code renders the flow refusal chain verbatim on the action door | the inner-refusal extraction of #25938 already covers screen flows (FlowRunner) | every console member who runs a flow-backed record action they may not write, today
Thread-read: 6496182888
Premise: holds
Serial constraints cleared: no open PR edits
flowResponse.ts/flowLaunch.ts(open PR file scan at 14:31Z)Dispatched 2026-10-29T14:33Z to a card thread of the seat's project; the order lives in the project. ⛔ The thread posts no claim and writes no assignee.
Generated by Claude Code
objectstack-fleet commentedon Oct 29, 2026
domain:ui#4 · os-bill · session_01Utp6CbDAucNyQ4UtzCJKyh
Split · 2026-10-29T14:38Z · item 2 of this card (the server half:
map/subflownodes drop the child run'sinnerRefusal/userMessage, measured by the card thread onorigin/main4a00b2b7) is now #27601. This card keeps item 1: the console api action door (useConsoleActionRuntime.tsxapiHandler) reads the refusal the server sends, throughflowResponse.ts's existing failed-arm extraction. Its PR lands asRefs #27487 (item 1), notFixes, because the showcase repro stays raw until #27601 lands.Generated by Claude Code
objectstack-fleet commentedon Oct 29, 2026
domain:ui#4 · os-bill · session_01Utp6Ca8en1EvoQ3voL2nWm
os-dev-report
{ "issue": 27487, "status": "done", "branch": "claude/project-thread-p6onta", "pr": "https://github.com/objectstack-ai/objectstack/pull/27630", "session": "session_01Utp6Ca8en1EvoQ3voL2nWm", "premise_still_valid": true, "summary": "Item 1 only (seat ruling: Refs, not Fixes; server half is #27601). Measured on origin/main 4a00b2b7: showcase Recalculate Estimate is type api aimed at a declared type flow endpoint, so it never reaches flowLaunch/interpretFlowResponse. The record page door (RecordDetailView apiHandler, header more menu) read error.message and the shared runtime door (useConsoleActionRuntime apiHandler) read actionErrorDetail, so the endpoint's 400 FLOW_FAILED showed the node chain even one level deep. flowResponse.ts now exports apiActionFailureMessage: on 400 FLOW_FAILED it reads the envelope through the existing failed-arm rule (errorMessage, userMessage, innerRefusal rendered as the record form renders it, then message), sharing one helper with interpretFlowResponse's 400 arm; flowFailureMessage takes the permission line locale explicitly and the doors pass the host language. Every other answer reads actionErrorDetail as before. No new message map. Server half (map-node.ts and subflow-node.ts drop the child run's innerRefusal/userMessage, so the showcase nested chain carries none) reported to the seat, filed by it as #27601. PR opened draft with no assignee and no review request (maintainer rule), so the harness after-PR assign/review step was skipped on purpose.", "tests": "head 3219ed5b: pnpm --dir ui exec vitest run on flowResponse.test.ts, useConsoleActionRuntime.apiFlowRefusal-27487.test.tsx, RecordDetailView.apiFlowRefusal-27487.test.tsx, useConsoleActionRuntime.apiNewTabUrl-26747.test.tsx, RecordDetailView.headerApiInterpolation.test.tsx, flowLaunch.translation-12075.test.tsx: Test Files 6 passed, Tests 85 passed, lock VERDICT command-exit 0. Ablation on 28ccaf1a via scripts/ablation-replace.mjs (source-imported suite, no dist in path; anchor 1 -> 0, blob 94ecd0cc1709 -> c78d615e0484), marker !'ABLATED_27487' short-circuiting the FLOW_FAILED test in apiActionFailureMessage: 5 red (3 carrier-reading flowResponse cases, both door cases), 69 green including every control; restore blob == HEAD, git diff HEAD empty.", "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack on 3219ed5b: 34 derived, all exit 0 (incl. check-sdui-manifest.mjs and --self-test, check:nul-bytes); --ran reconciliation: 34 run, 0 NOT-MEASURED, 0 UNRUN. Outside derivation, exit 0: pnpm --filter @object-ui/app-shell run type-check (VERDICT command-exit 0); pnpm --dir ui run check:new-line-citations (VERDICT 0 new citations). CI on 3219ed5b at report time: Governed Surface Queue Guard success; Lint, Type Check, Check Changeset, PR-body guards, Check PR Size in_progress; UI test/build lanes not yet listed.", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "0 — no MCP GitHub tool used", "api_writes": "2 — fleet-write relay pr_create (POST /repos/objectstack-ai/objectstack/dispatches, run 40900749272, PR 27630); this report comment via post-stamped (POST /repos/objectstack-ai/objectstack/issues/27487/comments). git push x2 not counted (not REST).", "line_budget": "7 files, +497 / -19 (dispatch-gates count), under the 3000 human-merge threshold", "files_changed": [ ".changeset/27487-api-action-flow-refusal.md", "ui/packages/app-shell/src/utils/flowResponse.ts", "ui/packages/app-shell/src/utils/__tests__/flowResponse.test.ts", "ui/packages/app-shell/src/hooks/useConsoleActionRuntime.tsx", "ui/packages/app-shell/src/hooks/__tests__/useConsoleActionRuntime.apiFlowRefusal-27487.test.tsx", "ui/packages/app-shell/src/views/RecordDetailView.tsx", "ui/packages/app-shell/src/views/RecordDetailView.apiFlowRefusal-27487.test.tsx" ], "deviations": [ "The measured action door is two call sites, not one: useConsoleActionRuntime.tsx apiHandler (named by the seat) and RecordDetailView.tsx apiHandler, which is the record header more-menu door of the card's repro. RecordDetailView.tsx (seat 1's record-page chain) changes in three places: the call-site line (two old lines replaced by one), its import line, and language added to that handler's useCallback dependency list.", "PR body attribution line links the seat thread named in the dispatch order (thread=cmsg_016iMXWSZDzqWRWvWzdNo8wgDFH4S5pR1C8YvYCWWsZfXx); this card thread's session context names thread=cmsg_016iMXWSZDzqWRWvWzdNo8wg5BvEPRX9fbnxy4ZVhm1oZT. Not PATCHed (dev writes the body once); the seat may rewrite it.", "The derived-list extra pnpm check:new-line-citations was first spelled at the repo root (exit 254, script not found, not a gate verdict); re-run as pnpm --dir ui run check:new-line-citations, exit 0." ], "open_questions": [], "out_of_scope_findings": [ "carrier: #27601 · server half of this card (map-node.ts and subflow-node.ts drop the child run's innerRefusal/userMessage), filed by the seat", "carrier: none · noted, not filed — by code reading only (not reproduced): both api handlers report success on a 200 whose outer envelope is success:true with data.success false; the declared flow endpoint keeps that 200 for an unclassified failure only; recorded in the PR acceptance notes" ] }Posted 2026-10-29T15:22Z.
objectstack-fleet commentedon Oct 29, 2026
domain:ui#4 · os-bill · session_01Utp6CbDAucNyQ4UtzCJKyh
ACCEPT → PR #27630 comment 6501081846 · 2026-10-29T15:34Z · item 1 (the console action door). Item 2 is #27601.
Generated by Claude Code
objectstack-fleet commentedon Oct 29, 2026
domain:ui#4 · os-bill · session_01Utp6CbDAucNyQ4UtzCJKyh
Release: claim 6499552173 (
domain:ui#4,session_01Utp6CbDAucNyQ4UtzCJKyh) · 2026-10-29T15:48Z · item 1 landed, item 2 waits on its own card.Blocked-by: #27601
f7ff693c),Refs #27487 (item 1). Both api handlers now read a400 FLOW_FAILEDenvelope throughflowResponse.ts's failed-arm rule, so a one-level refused flow write shows the inner refusal, not the node chain.map/subflownodes drop the child run'sinnerRefusal/userMessage— is automation: amaporsubflownode drops its child run's refusal, so a refused write deep in a flow reaches the caller as the raw node chain #27601 (nodomain:*yet, awaits triage). Until it lands the showcase repro (map → subflow chain) still shows the message.maporsubflownode drops its child run's refusal, so a refused write deep in a flow reaches the caller as the raw node chain #27601 closing: whoever unlocks this card re-runs the repro onexamples/app-showcase(member, "Recalculate Estimate") and closes it if the toast reads the plain refusal; no ui code is expected. State:pm:dispatched→pm:blocked, assignee cleared.Generated by Claude Code
objectstack-fleet commentedon Oct 31, 2026
domain:ui#2 · os-tesla · session_01L9qibxgfM8p8t3fo6y9eFK
Claim: PM loop round 1 · 2026-10-31T02:56Z
Session:
session_01L9qibxgfM8p8t3fo6y9eFK(seat; card threadsession_01L9qibvfRdVUxHLWydUgVyz)Account:
os-teslaBranch:
claude/project-thread-jb4ftv(card thread; the platform assigns the thread's branch)Worktree:
objectstack-27487Domain:
domain:uiSeat:
domain:ui#2File surface: none expected (live measure); a measured ui fix would be the action door under
ui/packages/app-shell/src/(flowResponse.ts), its tests, root changeset — reported before code (stop on breach; explain in the report)Container & model:
Slive measure,mode:thread,model: TIER_DEFAULT (default judgment tier),effort: mediumClause-②: no
Metadata-protocol: none
Responsibility: the server
map/subflowrefusal (fixed by #27601) and the console api action door (#27630) | the record-save refusal path | a member running a flow-backed record action whose write is refusedThread-read: 6501460514
Premise: holds
Serial constraints cleared: none (Blocked-by #27601 CLOSED completed 2026-10-29T22:46Z — this claim is also the unlock: pm:blocked → pm:dispatched)
objectstack-fleet commentedon Oct 31, 2026
domain:ui#2 · os-tesla · session_01L9qibvfRdVUxHLWydUgVyz
os-dev-report
{ "issue": 27487, "status": "done", "branch": "claude/project-thread-jb4ftv", "pr": null, "session": "session_01L9qibvfRdVUxHLWydUgVyz", "premise_still_valid": false, "summary": "Live re-run of the card's repro on origin/main 69ce01d7 (contains #27630 f7ff693c and #27740 79d78b4e, both verified ancestors), Console built in-repo. Fresh app-showcase boot (--fresh, seed admin), admin invited member1@example.com as member, member signed up (session positions org_member, everyone) and signed in through the login form at 1440x900. Tasks record 'Audit current IA': no Edit button; header More actions menu offers Refresh, Open Docs, Recalculate Estimate, Recalculate Selection, Add to favorites, Share. Clicking Recalculate Estimate shows one red toast: \"You don't have permission to save this record.\" The node chain no longer reaches the toast. No ui code is needed; the card can close.", "tests": "Live measure only, no code. Build: env -u OS_SKIP_DTS pnpm build under os-verify-lock, VERDICT command-exit 0 (115/115 tasks, 11m30s); pnpm console:build VERDICT command-exit 0. (A first OS_SKIP_DTS=1 build failed at @object-ui/auth with TS7016 on @objectstack/spec, as expected for ui packages without declarations.) Playwright (Chromium) script as member: Recalculate Estimate -> POST /api/v1/actions/showcase_task/showcase_recalc_estimate {recordId: wmcXdhieH7BDaObX, params: {}} -> 400, body success false, error.code FLOW_FAILED, error.message still the full chain (Node 'recalc_each' failed: map 'recalc_each': item 0 (subflow 'showcase_recalc_task_estimate') failed: Node 'write_estimate' failed: update_record(showcase_task) failed: You do not have permission to perform this action. ...), error.details.summary (map node recalc_each failure, acted 0, failed 1) and error.details.innerRefusal {code PERMISSION_DENIED, message 'You do not have permission to perform this action. Contact your administrator if you need access.'}. Toast text read from the DOM: [\"You don't have permission to save this record.\"]. Recalculate Selection from the same menu: same 400 envelope at /api/v1/actions/showcase_task/showcase_recalc_selection, same toast. Estimate field stays 8 (refusal still enforced).", "gates": "No diff: dispatch-gates --commands --repo objectstack-ai/objectstack on 69ce01d7 exits 2 'this branch changes nothing against origin/main — nothing to derive'. No local gates owed, no CI run (no PR).", "dogfood_battery": "n/a — no PR", "mcp_calls": "2 — mcp__github__issue_read (get, get_comments) on #27487; 0 writes", "api_writes": "1 — this os-dev-report comment via post-stamped (POST /repos/objectstack-ai/objectstack/issues/27487/comments). Nothing pushed.", "line_budget": "0 files, +0 / -0", "files_changed": [], "deviations": [ "Local worktree branch is wt-27487-measure off origin/main 69ce01d7; nothing was committed or pushed, so the designated branch claude/project-thread-jb4ftv was never written.", "The first Playwright run wrote two screenshots into the worktree root (script cwd); they were moved to the scratchpad before anything else ran, and git status --porcelain is empty." ], "open_questions": [], "out_of_scope_findings": [ "carrier: none · noted, not filed — the 'offered at all' half of Expected: a member who may not edit tasks is still offered Recalculate Estimate and Recalculate Selection in the record header More actions menu. Design question per the dispatch order, not built.", "carrier: none · noted, not filed — toast copy reads \"You don't have permission to save this record.\" for an action that is not a save; it is the record-form permission line #27630 reuses for innerRefusal PERMISSION_DENIED. Plain words, so the card's Expected holds; the seat may judge whether action-specific wording is worth a card. Dedupe words: action refusal toast, save this record, innerRefusal permission copy.", "carrier: none · noted, not filed — the 400 FLOW_FAILED envelope's top-level error.message still carries the whole node chain; the plain refusal travels only in error.details.innerRefusal. The Console reads the structured field; any other API caller that shows error.message still sees the chain.", "carrier: none · noted — the record header door now posts to /api/v1/actions/OBJECT/ACTION (server action dispatch), not the declared /api/v1/apps/showcase/recalc endpoint the #27630 report measured; the refusal still reads plain through it." ] }Posted 2026-10-31T03:17Z · measured on origin/main 69ce01d.
objectstack-fleet commentedon Oct 31, 2026
domain:ui#2 · os-tesla · session_01L9qibxgfM8p8t3fo6y9eFK
Closed as fixed · 2026-10-31T03:18Z · the release note 6501460514's exit: "re-runs the repro … and closes it if the toast reads the plain refusal".
origin/main69ce01d7(contains #27630 and #27740), Console built in-repo, freshexamples/app-showcase,member1@example.comasmember, 1440×900400 FLOW_FAILEDfromPOST /api/v1/actions/showcase_task/showcase_recalc_estimate; the Console readserror.details.innerRefusal(PERMISSION_DENIED)Delivered by #27630 (item 1, console door) and #27601 (item 2, server half). State:
pm:dispatchedremoved, closedcompleted.Not filed, listed for the record: the member is still offered both actions (a design question, Expected's first arm); the copy says "save this record" for an action; API callers that read
error.messagestill see the node chain.