Repository navigation
Flow actions: a type: 'flow' action cannot navigate to the record its run created — onSuccess is refused on flow actions and the run's response carries output: {} #25800
Description
Premise checked before dispatch (domain:spec seat 2, 2026-10-26): readings and corrections in comment 6409899481 (#25800 (comment)). The goal below is unchanged.
Filing class: ③ capability gap, filed at destination by the hotclm director seat under the maintainer's ruling on objectstack-ai/hotclm#99 item 3 (batch 5, letter B, 2026-10-25). Unlabelled, no assignee; the objectstack seat triages.
Symptom (hotclm, @objectstack/* 18.1.0 and 17.7.0)
clm_contract has a type: 'flow' header action start_renewal (target flow renewal_start, F12). Pressing it creates the renewal draft (renewed_from set) and toasts the successMessage, but the user stays on the old contract's record page with nothing to click: no link in the toast, no navigation. A second press is refused by the flow ("already has a renewal draft MSA-2026-0018"), so the only way to the draft is the list view.
Why the app cannot declare it
onSuccess.navigateexists and can read${result.id}, butON_SUCCESS_EXECUTORS = new Set(['api', 'script'])(packages/spec/src/ui/action-on-success-executor.ts:64):ActionSchemarefusesonSuccesson aflowaction. 17.7.0 refused at parse ("if that capability is needed, it is a spec proposal"); 18.1.0 refuses with the flow-specific message ("would follow the flow's start, not a result … move the hop onto anapiorscriptaction").- Even the response has nothing to navigate to: both
POST /api/v1/automation/renewal_start/trigger(what the console sends) andPOST /api/v1/actions/clm_contract/start_renewalanswer{ success: true, output: {}, successMessage }. Thecreate_recordnode'soutputVariableis not anisOutputflow variable, so the new record's id never reaches the caller. - The console (
ActionRunner.handlePostExecution) would performonSuccessafter any executor, but no validated flow action can carry the key;judgeFlowLaunch's done arm returns{ success, data, reload }only. - Re-authoring the flow as a
script/apiaction would reshape app metadata to dodge the gap, which the hotclm maintainer's standing rule forbids (「平台的问题就等平台」).
Measured by the hotclm dev on origin/main e798190 (17.7.0) and read in the 18.1.0 spec dist; report: objectstack-ai/hotclm#99 (comment), seat analysis objectstack-ai/hotclm#99 (comment).
Expected capability
An autolaunched flow started from a record action should be able to hand a record back to the console, and the action should be able to navigate to it. One shape that fits the existing pieces:
- The flow run's response carries the flow's
isOutputvariables underoutput(today it is{}even when the flow declares outputs; if that is a flow-authoring gap rather than a runtime one, say which). ON_SUCCESS_EXECUTORSadmitsflowwhen the flow completes synchronously (no screen, no pause), with${result.output.<var>}interpolation, soonSuccess: { navigate: '/apps/clm/clm_contract/record/${result.output.renewal_id}' }is declarable.
Either half alone is enough for a related-list fallback to become a link; both are needed for the "press, land on the draft" experience.
Dedupe
Searched objectstack issues for flow action post-success navigation / onSuccess on flow / flow output in the action response: #25458 is about the navigate path form on api/script actions, #20296 is the liveness grading of onSuccess.navigate, #14244 is recordLoadDenied on flow actions. None asks for a flow action to navigate to a record the run produced.
Activity
objectstack-fleet commentedon Oct 25, 2026
Triage: first grade —
enhancement·priority:p1·domain:spec·area:workflow·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-25T13:54Z⛔ Not a claim, ⛔ not a dispatch.
onSuccessontype: 'flow'actions, plus a run output the action can navigate with, widens the action contract. Spec first, then the runtime and the console runner.Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Premise check before dispatch · domain:spec seat 2 · 2026-10-26T06:39Z
Read-only premise reading on origin/main
d5f4c2a2a(dispatch-runbook 派发前置). The goal of this card is unchanged; only the readings below move the landing point or the method, and the dispatch order carries them.PARTLY CORRECTED. (1) The run response already carries
isOutputvariables (service-automation engine.ts:~5646, dag-traversal.ts:~1204, run-resume.ts:~1447, run-history.ts:~735; action door runtime action-execution.ts:~1347 returns it as is). The hotclmoutput: {}is a flow-AUTHORING gap (create_record outputVariable is notisOutput: true); say so on the card, as its ask (1) requests. (2) holds: ON_SUCCESS_EXECUTORS = ['api','script'] at packages/spec/src/ui/action-on-success-executor.ts:64; refusal text :110-114; enforced action.zod.ts:~2073 and component.zod.ts:~3865.Each count above is a re-measurable reading, not a ruling; the dev re-measures before relying on it.
objectstack-fleet commentedon Oct 26, 2026
Claim: PM loop round 1 · 2026-10-26T06:49Z
Session:
session_01RyMY3prh9GRva1Tt37Di3iAccount:
zhuangjianguoBranch:
claude/project-thread-1kqwvu(card thread; the platform assigns the thread's branch and it can push only that one, written as measured instead ofclaude/issue-25800-<slug>)Worktree:
objectstack-issue-25800Domain:
domain:specSeat:
domain:spec#2File surface:
packages/spec/src/ui/action-on-success-executor.ts, action.zod.ts, component.zod.ts, packages/spec/liveness/action.json + generated ui manifests, ui/packages/app-shell/src/utils/flowResponse.ts (+ the app-shell tests that pin the paused/refused result), ui/packages/types/src/ui-action.ts (one ActionResult member), ui/packages/core/src/actions/ActionRunner.ts (source + tests), packages/lint/src/validate-component-props-row-on-success.test.ts (one comment), packages/spec/src/ui/action.form.ts (onSuccess row gate admits flow), packages/lint/src/validate-predicate-path-refs.test.ts (corpus census 65 → 63), one changeset(stop on breach; explain in the report) — amended by the seat after the thread measured that ActionRunner cannot tell a hand-off from a completed runContainer & model:
M,mode:thread,model: TIER_DEFAULT — --tier: no path-derived mandate,effort: xhighClause-②: yes
Metadata-protocol: added
Responsibility: n/a — not a defect card
Thread-read: 6409899481
Premise: corrected 6409899481
Serial constraints cleared:
open PR #25903 regenerates the same ui generated manifests (no claim overlap; merge main only on conflict)objectstack-fleet commentedon Oct 26, 2026
os-dev-report
{ "issue": 25800, "status": "done", "branch": "claude/project-thread-1kqwvu", "pr": "https://github.com/objectstack-ai/objectstack/pull/26011", "session": "session_013w86pS2SMm6BU2PfGQ3vJ7", "premise_still_valid": true, "summary": "`flow` joins `api` and `script` in `ON_SUCCESS_EXECUTORS`, the one set `ActionSchema.onSuccess` and the page action rows read; the refusal prose for `url`, `navigation`, `modal` and `form` names the three executors, the Studio action form shows the `onSuccess` row for a flow action, and the reference docs are regenerated. Console: the hop fires only after a flow run that completes (`judgeFlowLaunch` done arm), never after one that pauses at a screen or ends refused, and `${result.output.VAR}` resolves against the completed run's `AutomationResult`. Mechanism-assumption correction (recorded in the PR body): `silent` cannot separate a hand-off from a completed run, because the done arm is also `silent` when the flow's own `successMessage` wins the toast (objectui#12274), so a runner keyed on `silent` would drop the hop for exactly the record-creating flow this card is about; the fix adds `ActionResult.handedOff`, set on the paused and refused arms and read by `ActionRunner.handlePostExecution`. Every judgeFlowLaunch arm now states its hop conclusion in the docblock: failed no hop (not a success), paused handedOff and no hop now or when FlowRunner finishes the run, refused handedOff and no hop past the notice, done not handedOff (silent or not) and hops reading `data`. Card ask (1): the runtime is not dropping anything; `AutomationResult.output` holds only the flow's `isOutput` variables (packages/services/service-automation/src/engine.ts, the output-collection loop near line 5643), and both the trigger route and the action door (`dispatchFlowAction`) serve it, so the reporter's `output: {}` is a flow-authoring gap: declare the variable `isOutput: true` and point the create_record node's `outputVariable` at it.", "tests": "At head 9cb24ac6 (spec and ui core/types suites at 344ac6f5, which differs only in scripts/sdui-manifest.record.json): `pnpm --filter @objectstack/spec test` Test Files 722 passed (722), Tests 21877 passed | 1 todo; ui vitest packages/core + packages/types Test Files 601 passed (601), Tests 14680 passed | 27 skipped; ui vitest packages/app-shell Test Files 1721 passed | 1 skipped (1722), Tests 15875 passed | 9 skipped (lock VERDICT command-exit 0); `pnpm run type-check` in ui/packages/types, core and app-shell exit 0 each, 0 errors (app-shell after `pnpm --filter '@object-ui/app-shell^...' build`; before that build it reported 192 errors, 139 of them TS2307 for unbuilt @object-ui/auth, layout and siblings, none in a touched file). Ablation (one-time, scripts/ablation-replace.mjs): runner gate `if (result.success && !result.handedOff && action.onSuccess) {` replaced by `if (result.success && action.onSuccess) {`, anchor 1 -> 0, blob 3aebb3cab4ea -> ac1fc1dff4a2; the two new test files then gave Test Files 2 failed (2), Tests 3 failed | 70 passed, the three reds being exactly the hand-off pins (core 'a handed-off success performs no hop', app-shell 'paused at a screen' and 'ended refused' composition tests); restored to HEAD blob 3aebb3cab4ea, `git diff HEAD` empty.", "gates": "Local subset: `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` at 9cb24ac6 derived 125 commands; each run with exit captured before any pipe; `--ran` verdict: '125 derived famil(ies) accounted for — 123 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3)'. 123 exited 0, including check:generated, check:api-surface, check:authorable-surface, check:docs, check:liveness, check:exported-any, check-sdui-manifest, check:nul-bytes, check:type-check-debt (re-run under the verify lock: '26 raw tsc error(s) total, none above its recorded number') and check:skill-examples (re-run after building @objectstack/client-react). NOT MEASURED locally (exit 3, prerequisite): check:dual-build-cjs-loads (needs a full workspace build) and check:i18n (needs the CLI build closure). An earlier sweep ran unlocked while the manifest regeneration rebuilt packages/spec/dist and gave spurious check:generated / check:exported-any reds; it was discarded. CI: PR #26011 check-runs read right after creation: total_count 0 — in_progress, no conclusion yet.", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "1 — mcp__github__pull_request_read (get_check_runs on #26011), read-only; no write tool", "api_writes": "2 — (1) fleet-write relay pr_create: POST /repos/objectstack-ai/objectstack/dispatches (HTTP 204), run 40296483458 conclusion success, executing POST /repos/objectstack-ai/objectstack/pulls (draft) and POST /repos/objectstack-ai/objectstack/issues/26011/assignees; read-back 9760 bytes sent = 9760 stored, assignees zhuangjianguo. (2) this report: scripts/pm/post-stamped.mjs --comment=25800, through the same relay (POST /repos/objectstack-ai/objectstack/dispatches) executing POST /repos/objectstack-ai/objectstack/issues/25800/comments. Plus git push of branch claude/project-thread-1kqwvu.", "open_questions": [], "out_of_scope_findings": [ "carrier: PR body Acceptance notes · noted, not filed — two pre-existing ActionRunner behaviours still apply to a handedOff success: an authored `refreshAfter: true` sets `result.reload`, and an authored `chain` runs (ui/packages/core/src/actions/ActionRunner.ts handlePostExecution); neither is the hop this card asked about", "carrier: PR body Acceptance notes · noted, not filed — a flow action whose target flow starts at a screen always pauses, so its `onSuccess` can never fire; a publish-time hint for that pairing would catch the authoring mistake earlier" ], "deviations": [ "File surface amended mid-run with the seat's approval: ui/packages/types/src/ui-action.ts, ui/packages/core/src/actions/ActionRunner.ts and ui/packages/app-shell/src/utils/flowResponse.ts joined the claim's surface after the measured mechanism correction; consequent edits: 8 console view tests pinning the paused or refused result with toEqual, FlowRunner.tsx and FlowRunner.refusedOutcome-7707.test.tsx comments, scripts/sdui-manifest.record.json (registry digest only; sdui.manifest.json byte-identical), packages/lint validate-predicate-path-refs census 63.", "Clause-② line copied verbatim from the claim (`Clause-②: yes`, no arm) per the copy-don't-invent rule; the dispatch note read `yes (widening)`. Both are well-formed; the seat may add the arm in the PR body if wanted.", "Changeset also bumps @object-ui/types, @object-ui/core and @object-ui/app-shell minor (added `handedOff` and the runner and judge behaviour), beyond the dispatch's @objectstack/spec minor.", "The harness attribution reminder asked for a model-named Co-Authored-By trailer; commits carry the AGENTS.md model-free pair instead.", "Gate check-plugin-teardown-shape --self-test needed its pinned fixture commit 621a4876 fetched (`git fetch --depth=1 origin 621a4876…`) in this shallow clone; it exited 3 (prerequisite) before that and 0 after.", "PR activity subscription not taken: the dispatch contract overrides the platform's auto-subscribe posture, and CI convergence belongs to the seat." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
ACCEPT → PR #26011 comment 6413960327 (domain:spec seat 2); contract review PASS comment 6413937921 on head eaebe2e. Ready with auto-merge.
Merge origin/main into claude/project-thread-1kqwvu
objectstack-fleet commentedon Oct 26, 2026
Landed · domain:spec seat 2
dc718454146364b8809740687da1be799baaf0d7(contract review PASS 6423205064 on heada3f1f482)origin/main586a7ed7d, contains the merge commit (git merge-base --is-ancestor)State:
Fixes #25800closed this card as completed;pm:dispatchedremoved in this stroke.