Skip to content

feat(spec,console): a type: 'flow' action may declare onSuccess, and the console navigates after a run that completes - #26011

Merged
objectstack-fleet[bot] merged 12 commits into
mainfrom
claude/project-thread-1kqwvu
Oct 26, 2026
Merged

objectstack-fleet[bot] merged 12 commits into
mainfrom
claude/project-thread-1kqwvu

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steedos · project thread

Fixes #25800

Clause-②: yes
Metadata-protocol: added

Summary

A type: 'flow' action that creates a record could not take the user to it: ActionSchema refused onSuccess beside type: 'flow', and the page action rows refused it beside a flow executor. This PR admits flow into the one executor set both read (ON_SUCCESS_EXECUTORS in packages/spec/src/ui/action-on-success-executor.ts), rewords the refusal prose for url, navigation, modal and form to name the three executors, opens the Studio action form's onSuccess row to a flow action, and regenerates the reference docs.

On the console side, the post-success hop fires after a flow run that completes and never after one that pauses at a screen or ends refused. On a completed run, ${result.*} reads the run's answer (AutomationResult), so ${result.output.VAR} resolves to one of the flow's isOutput variables:

{
  name: 'start_renewal',
  type: 'flow',
  target: 'renewal_start',
  onSuccess: { navigate: '/apps/clm/clm_contract/record/${result.output.renewal.id}' },
}
// the flow declares variables: [{ name: 'renewal', type: 'object', isOutput: true }]
// and its create_record node writes outputVariable: 'renewal'

Mechanism-assumption correction: why silent cannot mark a hand-off

The dispatch assumed the ActionRunner needed no change: the launch judge's paused and refused arms already answer silent: true, so it looked as if the hop could be keyed off that. Measured, it cannot. silent is a fact about the toast alone, and the done arm sets it too. When the flow's own successMessage wins the toast (objectui#12274), judgeFlowLaunch returns { success: true, data, reload, silent: true } for a run that completed. A runner that skipped the hop on silent would therefore drop the hop for every completed flow that authors its own success copy, which is exactly the record-creating flow this card is about. The three success arms are indistinguishable on success and silent, so the runner needs a fact of its own.

That fact is the new ActionResult.handedOff (ui/packages/types/src/ui-action.ts): the success is a hand-off to a follow-up surface, not a completion. judgeFlowLaunch sets it on the paused and refused arms, and ActionRunner.handlePostExecution gates the hop on result.success && !result.handedOff && action.onSuccess. The seat approved the file-surface amendment (types, core, app-shell) before the console half was written.

Each launch arm and its hop

judgeFlowLaunch in ui/packages/app-shell/src/utils/flowResponse.ts now states the hop conclusion for every arm in its docblock, and each conclusion is pinned by a test that runs the real ActionRunner:

Arm Result Hop Why
failed success: false none The runner hops only after a success.
paused success: true, silent, handedOff none, now or later The action only opened the wizard. Navigating away would leave the screen behind, and the run has not answered with the output the hop reads. FlowRunner finishing the run later does not hop either.
refused success: true, silent, handedOff none The Close-only notice is the follow-up, and a hop would navigate past it to a record the refusal never produced.
done success: true, data = AutomationResult (silent when the flow's copy wins) hops The run completed. ${result.*} reads data; ${result.output.VAR} is an isOutput variable. Not handedOff, whether silent or not.

Landing point and surface delta

The claim named the spec half (action-on-success-executor.ts, action.zod.ts, component.zod.ts, action.form.ts, their tests, the liveness ledger, the reference docs) and the console tests. The correction above added three source files to the surface, approved by the seat: ui/packages/types/src/ui-action.ts (handedOff), ui/packages/core/src/actions/ActionRunner.ts (the gate) and ui/packages/app-shell/src/utils/flowResponse.ts (the arms). Following from those:

Premise check: what ${result.output.VAR} reads

The card reported a run answering output: {}. The runtime is not dropping anything: AutomationResult.output is built from the flow's isOutput variables only (packages/services/service-automation/src/engine.ts, the "Collect output variables" loop at about line 5643), and both the trigger route and the action door (dispatchFlowAction, packages/runtime/src/action-execution.ts) serve that answer. A create_record node writes the record object into its outputVariable, but the variable reaches output only when the flow declares it isOutput: true. The empty output is a flow-authoring gap in the reporting app: declare the variable isOutput: true and point outputVariable at it. The spec docblock and the changeset now say so, and a test pins that an empty output resolves the template to /apps/clm/clm_contract/record/ rather than throwing.

Tests

Measured on this branch. The spec and ui core/types suites ran at 344ac6f5, which differs from the head only in scripts/sdui-manifest.record.json; the app-shell suite and the type-checks ran at the head, 9cb24ac6.

  • pnpm --filter @objectstack/spec test: Test Files 722 passed (722), Tests 21877 passed, 1 todo.
  • ui/ vitest over packages/core and packages/types: Test Files 601 passed (601), Tests 14680 passed, 27 skipped.
  • ui/ vitest over packages/app-shell: Test Files 1721 passed, 1 skipped (1722); Tests 15875 passed, 9 skipped.
  • pnpm run type-check in ui/packages/types, ui/packages/core and ui/packages/app-shell (after building the app-shell dependency closure): exit 0, 0 errors each.
  • New pins: ActionRunner.onSuccessNavigation.test.ts (reads ${result.output.renewal.id} to /apps/clm/clm_contract/record/ctr_18; a silent completed run still hops; a handedOff success does not, with a positive control on the same runner) and a flowResponse.test.ts block composing interpretFlowResponse, judgeFlowLaunch and the real ActionRunner for all four arms plus the empty-output case.
  • Spec: action-on-success.test.ts and component-action-row-on-success-executor.test.ts move flow from refused to accepted and parse the renewal template.

Ablation

scripts/ablation-replace.mjs replaced the runner gate if (result.success && !result.handedOff && action.onSuccess) { with if (result.success && action.onSuccess) { (anchor 1 to 0, blob 3aebb3cab4ea to ac1fc1dff4a2) and ran the two new test files: Test Files 2 failed (2), Tests 3 failed, 70 passed. The three reds are exactly the hand-off pins: the core "handed-off success performs no hop" test, and the paused and refused composition tests. The file was restored to its HEAD blob 3aebb3cab4ea with git diff HEAD empty. This was a one-time proof; no permanent test file was added for it.

Gates

Derived with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at 9cb24ac6 (125 commands), each run with its exit code captured before any pipe, then reconciled with --ran: "125 derived famil(ies) accounted for — 123 run, 2 NOT-MEASURED". The 123 that ran exited 0, among them check:generated, check:api-surface, check:authorable-surface, check:docs, check:liveness, check:exported-any, check-sdui-manifest, check:type-check-debt (re-measured under the verify lock: "26 raw tsc error(s) total, none above its recorded number") and check:nul-bytes.

Not measured locally, left to CI: check:dual-build-cjs-loads (needs a full workspace pnpm build) and check:i18n (needs the CLI's build closure). Both refused on their prerequisite with exit 3 before reading anything; this diff adds no i18n key and changes no package's build shape.

Two environment notes. check-plugin-teardown-shape --self-test reads a fixture pinned to commit 621a4876, which this shallow clone lacked; after fetching that one commit it exits 0. And an earlier sweep ran unlocked while the console manifest regeneration rebuilt packages/spec/dist, which produced spurious check:generated and check:exported-any reds; that sweep was discarded and the numbers above come from the clean rerun.

Acceptance notes

  • Two pre-existing runner behaviours still apply to a handed-off success, unchanged by this PR: an action authored with refreshAfter: true sets result.reload, and an authored chain runs. Neither is new, and neither is the hop this card asked about. Noted, not filed.
  • A flow action whose target flow starts at a screen will always pause, so its onSuccess can never fire. A publish-time hint for that pairing would catch the mistake earlier; noted, not filed.

Generated by Claude Code

🤖 Generated with Claude Code

https://claude.ai/code/session_013w86pS2SMm6BU2PfGQ3vJ7

… gate for flow onSuccess

Claude-Session: https://claude.ai/code/session_013w86pS2SMm6BU2PfGQ3vJ7
Co-authored-by: Claude <noreply@anthropic.com>
…, so the onSuccess hop skips it

Claude-Session: https://claude.ai/code/session_013w86pS2SMm6BU2PfGQ3vJ7
Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013w86pS2SMm6BU2PfGQ3vJ7
Co-authored-by: Claude <noreply@anthropic.com>
…nner and types edits

Claude-Session: https://claude.ai/code/session_013w86pS2SMm6BU2PfGQ3vJ7
Co-authored-by: Claude <noreply@anthropic.com>
Resolves the flowResponse.test.ts import conflict (both sides' imports kept) and regenerates the reference docs and the sdui manifest record from the merged tree.

Claude-Session: https://claude.ai/code/session_013w86pS2SMm6BU2PfGQ3vJ7
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation protocol:ui tests tooling labels Oct 26, 2026
@github-actions

github-actions Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 10 documentable anchor(s). ⚠️ 1 changed file(s) yielded no anchor (packages/spec/liveness/action.json), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

2 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/deployment/troubleshooting.mdx (via ActionSchema (symbol, a top-level const))
  • content/docs/protocol/objectui/actions.mdx (via ActionSchema (symbol, a top-level const))

⛔ 3 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v17/17-1.mdx (via ActionSchema (symbol, a top-level const), newTab (literal, a string literal in a comment in actionObject))
  • content/docs/releases/v17/17-3.mdx (via ActionSchema (symbol, a top-level const))
  • content/docs/releases/v17/index.mdx (via ActionSchema (symbol, a top-level const))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/spec/liveness/action.json) — pages documenting those are invisible to this run
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 145 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 81bba6c3f5be5b6cbabf76511383940349f23b15 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 2f888ec3ef7d26d0621e43ee63b03c36ed1f69d2 — the merge of head a3f1f482ad01eb653cd8837dd88a2b32f56e2ae7 into base 81bba6c3f5be5b6cbabf76511383940349f23b15, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 2f888ec3ef7d26d0621e43ee63b03c36ed1f69d2 && git checkout 2f888ec3ef7d26d0621e43ee63b03c36ed1f69d2
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 81bba6c3f5be5b6cbabf76511383940349f23b15 a3f1f482ad01eb653cd8837dd88a2b32f56e2ae7 && git checkout -B drift-repro 81bba6c3f5be5b6cbabf76511383940349f23b15 && git merge --no-ff a3f1f482ad01eb653cd8837dd88a2b32f56e2ae7

node scripts/docs-audit/affected-docs.mjs --json 81bba6c3f5be5b6cbabf76511383940349f23b15

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 81bba6c3f5be5b6cbabf76511383940349f23b15 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: eaebe2e4e4efd990932249a993d7b0f1ecfe5a31
Local-runs: none
Scope: packages/spec/src/ui/action-on-success-executor.ts, packages/spec/src/ui/action.form.ts, packages/spec/src/ui/action.zod.ts, packages/spec/src/ui/component.zod.ts + packages/spec/liveness/action.json, packages/spec/src/ui/action-on-success.test.ts, packages/spec/src/ui/component-action-row-on-success-executor.test.ts, packages/lint/src/validate-predicate-path-refs.test.ts, packages/lint/src/validate-component-props-row-on-success.test.ts, .changeset/25800-flow-action-on-success.md, ui/packages/types/src/ui-action.ts, ui/packages/core/src/actions/ActionRunner.ts, ui/packages/app-shell/src/utils/flowResponse.ts, and read for the claims only (unchanged by the diff): ui/packages/app-shell/src/utils/flowLaunch.ts, ui/packages/core/src/actions/actionResponse.ts, ui/packages/core/src/actions/serverActionHandler.ts, packages/spec/src/contracts/automation-service.ts. The remaining console tests, FlowRunner.tsx comment, the four regenerated content/docs pages and scripts/sdui-manifest.record.json are the lane seat's ACCEPT at TIER_DEFAULT.

Inputs read: card #25800 body and its four comments (triage 6398521414, premise 6409899481, claim 6410040122, os-dev-report 6412203410); PR #26011 body, 30-file list and net diff; the 63 check-runs on the head: 58 success, 5 skipped (the two opt-in harnesses, the live-PG Temporal conformance, Dogfood Verify CLI and the dogfood matrix placeholder), 0 failure. Among the greens: Check Changeset, Governed Surface Queue Guard, Spec property liveness, Lint and Repo Gates, Type Check (workspace, source, consumer, debt ledger), Test Core 1-8, Test UI 1-16, Build Docs, Flag docs affected by code changes.

① Derived judgments

  1. Accept-set widening, RIGHT. ON_SUCCESS_EXECUTORS becomes api / script / flow (action-on-success-executor.ts:64). It is the ONE reader on both doors: ActionSchema's superRefine (action.zod.ts:2073, !ON_SUCCESS_EXECUTORS.has(data.type)) and the page action rows' rowOnSuccessRefused (component.zod.ts:3865, ROW_BUILTIN_EXECUTORS.has(executor) and not ON_SUCCESS_EXECUTORS.has(executor)), so action:button, action:icon and each action:group / action:menu member admit onSuccess beside a flow executor in lockstep with the action, with no second list to drift. Pinned both ways by action-on-success.test.ts (flow moved from the refused it.each to the accept pins, with the renewal template parsing to { navigate, openIn: 'self' }) and component-action-row-on-success-executor.test.ts (REFUSED drops flow; ACCEPTED adds it on the executor leg and the name leg).
  2. Refusal prose, RIGHT. The case 'flow' branch of onSuccessBesideExecutorRefusal is removed; it is unreachable now because ActionType is closed and the rows guard on the same set. DECLARED_BESIDE, MOVE_THE_HOP and the no-executor prescription name the three executors; url / navigation / form / modal and the host-handler default branch are untouched. The lint row test changes a comment only; Lint and Repo Gates green.
  3. Published describe text, RIGHT. onSuccess and onSuccess.navigate describes on ActionSchema and the three row describes in component.zod.ts now name flow and state the completed-run-only rule. The four content/docs pages (ui/action, ui/component, data/object, kernel/metadata-plugin) are the regenerated reflection of those strings and nothing else; Build Docs and Flag docs affected by code changes green, so generated and source agree at this head.
  4. Studio form gate, RIGHT. visibleWhen: "data.type in ['api','script','flow']" uses the in-list predicate idiom already shipped in field.form.ts and page.form.ts; the == literal census in validate-predicate-path-refs.test.ts drops 65 to 63 for exactly the two comparisons the respelling removed. outcomeMessages keeps its api/script gate, correct because refuseInertOutcomeMessages is not widened and a flow run names no outcome.
  5. Liveness ledger, RIGHT. The onSuccess.navigate row's note gains the 2026-10-26 paragraph naming the widened pairing and the in-tree read points (judgeFlowLaunch marks paused/refused handedOff; handlePostExecution skips a handed-off success); status stays live, evidence and producer unchanged; Spec property liveness green.
  6. The console half the spec docblocks assert, RIGHT and read in the cited code. ActionResult.handedOff is an additive optional member (ui-action.ts). judgeFlowLaunch sets it on the paused and refused arms only; the done arm stays { success, data, reload } plus silent when the flow's own copy wins, confirming the PR body's correction that silent cannot mark a hand-off. ActionRunner.handlePostExecution gates the hop on result.success and not result.handedOff and action.onSuccess. Both launch doors go through judgeFlowLaunch (flowLaunch.ts:212 action door, :232 trigger door) and both hosts return judged.result unchanged (useConsoleActionRuntime.tsx:742, RecordDetailView.tsx:1508), so handedOff reaches the runner. The action door's no-answer branch (flowLaunch.ts:206) returns the dispatcher's own result, whose only success return follows a fetch (serverActionHandler.ts:311), so no success reaches the hop without a run.
  7. What ${result.output.VAR} reads, RIGHT. navigateOnSuccess interpolates with readActionPayload(result.data); on the done arm data is json.data, the run's AutomationResult, which carries output and status and so is not the legacy {success,data,error,code,fields} envelope: identity, and ${result.output.renewal.id} resolves. Pinned at /apps/clm/clm_contract/record/ctr_18 by ActionRunner.onSuccessNavigation.test.ts and by the flowResponse.test.ts composition block (all four arms plus output: {} resolving to the bare prefix rather than throwing). One edge, not blocking: a run answering exactly { success: true } with no output key would be unwrapped as a legacy envelope to undefined; the engine's output-collection loop always emits output, so the shape does not arise from the runtime.
  8. Public API surface, RIGHT. ON_SUCCESS_EXECUTORS is still reached by relative import only (no barrel export), so the spec's exported surface changes only in accept set and describe text; Type Check and the api-surface family inside Lint and Repo Gates are green on the head.
  9. Card premise (1), RIGHT. AutomationResult.output is the flow's isOutput variables; the reporter's output: {} is a flow-authoring gap, stated in the executor docblock, the navigate docblock, the changeset and the PR body, as the card asked.

② Semver level

Changeset .changeset/25800-flow-action-on-success.md: @objectstack/spec minor (accept set widens, refusal prose and describe text change, form row opens; nothing removed), @object-ui/types minor (new optional ActionResult.handedOff), @object-ui/core minor (runner skips the hop for a handed-off success), @object-ui/app-shell minor (judge arms carry handedOff). Each bump matches what the diff publishes in that package; no package with a source change is missing from the changeset, and Check Changeset is green. Clause-②: yes on the PR body, the claim and the changeset, three spellings identical; the widening arm is implied by the diff (an accept-set widening with no narrowing) and its omission is well-formed. Metadata-protocol: added matches a new accepted pairing on the action metadata type. The PR title carries no !, correct: no accepted input is refused after this change.

Clause-②: yes

③ Boundary flags

Dev report 6412203410 on 9cb24ac: open_questions empty; premise_still_valid: true, consistent with premise 6409899481. Each deviation and out-of-scope finding answered:

  • Deviation 1, file surface amended mid-run (ui-action.ts, ActionRunner.ts, flowResponse.ts plus consequents): recorded on the claim 6410040122 as the seat's amendment; every file in the 30-file list is either that surface or its consequent. ANSWERED.
  • Deviation 2, Clause-②: yes without the (widening) arm: well-formed, see ②. ANSWERED.
  • Deviation 3, changeset bumps three ui packages beyond the dispatch's spec minor: correct and required, each publishes a change, see ②. ANSWERED.
  • Deviation 4, commit trailer pair: a repository attribution convention, not a contract matter. NOTED.
  • Deviation 5, teardown-shape fixture fetched in the shallow clone: environment only; the head's check-runs are the gate verdicts and are green. NOTED.
  • Deviation 6, no PR activity subscription: the seat's posture. NOTED.
  • Out-of-scope 1, refreshAfter and chain still apply to a handed-off success: pre-existing runner behaviour, untouched; a chain after a paused launch is a separate question. ESCALATED to the seat as an optional follow-up card, not blocking.
  • Out-of-scope 2, a flow whose first node is a screen always pauses, so its onSuccess never fires: not the ADR-0078 inert shape, because the runner reads the key on every success and the no-hop is a per-run fact (handedOff), and the schema cannot see the target flow's definition at parse time. A publish-time pairing hint needs the flow definition in hand. ESCALATED to the seat as a follow-up card suggestion, not blocking.
  • Reviewer's own: the PR body says "No conflict with feat(spec,analytics,lint,platform-objects,app-shell): a dataset measure can aggregate the time between two datetime fields of each record #25903 arose, so main was not merged", but the head eaebe2e is a merge of origin/main (2-parent commit, after the report's 9cb24ac); the brief states the merge resolved only the flowResponse.test.ts import block and regenerated digest/docs. Body prose only; no file-surface change beyond the merge. ESCALATED as a PR-body correction for the seat, not blocking.
  • Reviewer's own: the readActionPayload edge in ① item 7 ({ success: true } alone unwraps to undefined). Does not arise from the engine. NOTED.

Implemented-by: claude/project-thread-1kqwvu
Reviewed-by: session_01RyMY3prh9GRva1Tt37Di3i

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

ACCEPT · domain:spec seat 2 · 2026-10-26T10:37Z · head eaebe2e4e4efd990932249a993d7b0f1ecfe5a31

  1. Shape. Draft, base main, closes 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. 30 files, +392/−121. check-governed-merges --pr 26011: NOT governed.
  2. Premise and stop conditions. The premise held: ActionSchema and the page action rows refused onSuccess beside a flow executor, so a flow that creates a record could not take the user to it. The widening reads one list (ON_SUCCESS_EXECUTORS: api, script, flow) at both doors. The claim's file surface was amended twice by the seat for ui-action.ts, ActionRunner, action.form.ts and the lint tests; the main merge added no surface.
  3. Gates (check-runs pinned to this head). 63 runs: 58 success, 5 skipped, 0 failed. check-expected-skips --pr 26011: OK.
  4. Contract review. PASS at CONTRACT_REVIEW_TIER on this head, comment 6413937921 (face schema, scope the four packages/spec/src/ui/ hits and the console code they assert).
  5. Remainder at TIER_DEFAULT (seat-read). The console half: ActionResult.handedOff is additive; the flow launch judge sets it only on a paused or refused run; the runner skips the onSuccess hop for a handed-off success. The four generated reference pages move with the schema.
  6. Prose checked against the diff (changeset). "accepted on a type: 'flow' action and beside a page action row's flow executor": holds at both doors. "${result.*} reads the completed run's answer (AutomationResult)": holds, the done arm's data is the AutomationResult. "A run that completes is not marked, including one whose toast is the flow's own successMessage": holds. Minor bumps for spec, types, core and app-shell match what each publishes.
  7. PR-body correction (recorded here, the body is not edited). "No conflict with feat(spec,analytics,lint,platform-objects,app-shell): a dataset measure can aggregate the time between two datetime fields of each record #25903 arose, so main was not merged" is no longer true: head eaebe2e4 is a merge of origin/main (c16c00f9) made after the report head 9cb24ac6 to clear a conflict in flowResponse.test.ts and the sdui-manifest digest.
  8. Accepted, not fixed here (follow-up candidates, not filed while the lane's token hold lasts). A flow that starts at a screen can never hop after it finishes; a publish-time hint needs the flow definition. chain / refreshAfter on a handed-off success predate this PR.
  9. Landing. Ready plus auto-merge.

Only the generated sdui manifest record conflicted; took main's side and re-recorded it from the merged tree.

Claude-Session: https://claude.ai/code/session_013w86pS2SMm6BU2PfGQ3vJ7
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: d8570af63f86a322b10babc11023496d3cb5d996
Local-runs: none
Scope: packages/spec/src/ui/action-on-success-executor.ts, packages/spec/src/ui/action.form.ts, packages/spec/src/ui/action.zod.ts, packages/spec/src/ui/component.zod.ts + packages/spec/liveness/action.json, packages/spec/src/ui/action-on-success.test.ts, packages/spec/src/ui/component-action-row-on-success-executor.test.ts, packages/lint/src/validate-predicate-path-refs.test.ts, packages/lint/src/validate-component-props-row-on-success.test.ts, .changeset/25800-flow-action-on-success.md, ui/packages/types/src/ui-action.ts, ui/packages/core/src/actions/ActionRunner.ts, ui/packages/app-shell/src/utils/flowResponse.ts, scripts/sdui-manifest.record.json (the only file the merge-forward moves). The remaining console tests, FlowRunner.tsx comment and the four regenerated content/docs pages are the lane seat's ACCEPT at TIER_DEFAULT.

Re-review after a merge-forward. Head d8570af is a 2-parent commit: parents eaebe2e (the head PASS record 6413937921 judged) and b4199c9 (origin/main), made after a merge-queue conflict. Verified delta, one git read command, restricted to the PR's 30 files: git diff eaebe2e4 d8570af6 -- FILES moves only scripts/sdui-manifest.record.json, +1/-1, the registrySourcesSha256 value (9cbec767 to cace2de2; registrySources stays 922). Unrestricted, the same diff is 165 files, all of them main's own advance between c16c00f and b4199c9. The four hits are byte-identical: blob ids at eaebe2e and d8570af agree for each (1846bf00, 97125cc2, 7545ef0c, 9862b95b), and the PR diff of the four hits against main has the same md5 at the old base (c16c00f..eaebe2e) and at the new base (b4199c9..d8570af), 284 lines each. Every other file in the 30 is unchanged by the merge, so the scope cited in 6413937921 is read as judged there.

Inputs read: card #25800 body and its five comments (triage 6398521414, premise 6409899481, claim 6410040122, os-dev-report 6412203410, ACCEPT 6413972897; no ruling newer than the previous record); PR #26011 body and 30-file list; the merge-forward delta above; the 63 check-runs on head d8570af: 58 success, 5 skipped (the two opt-in harnesses, the live-PG Temporal conformance, Dogfood Verify CLI and the dogfood matrix placeholder), 0 failure. Among the greens: Check Changeset, Governed Surface Queue Guard, Spec property liveness, Lint and Repo Gates (which holds check-sdui-manifest and the api-surface family), Type Check (workspace, source, consumer, debt ledger), Test Core 1-8, Test UI 1-16, Build Docs, Flag docs affected by code changes, Check PR Size.

① Derived judgments

  1. Items 1 to 9 of 6413937921 CARRIED unchanged: accept-set widening of ON_SUCCESS_EXECUTORS to api/script/flow read by both doors (RIGHT); refusal prose (RIGHT); published describe text and regenerated docs (RIGHT); Studio form gate (RIGHT); liveness ledger (RIGHT); console handedOff half read in the cited code (RIGHT); ${result.output.VAR} identity (RIGHT); no new barrel export (RIGHT); card premise (RIGHT). The hunks they judged are byte-identical on this head, and each gate they cite is green again on d8570af.
  2. Manifest digest re-record, RIGHT. The new registrySourcesSha256 on the head (cace2de2) equals neither parent's value (eaebe2e: 9cbec767; main b4199c9: 0e127be1), so it is a fresh digest of the merged registry sources, not a value carried from one side. scripts/sdui-manifest.record.json has merge: unspecified in .gitattributes, so no Regen-provenance carry applies and the file is judged by its gate alone: Lint and Repo Gates is green on the head, which is the check-sdui-manifest verdict for this tree. Nothing else in the diff changes meaning.

② Semver level

CARRIED from 6413937921: the changeset bumps @objectstack/spec, @object-ui/types, @object-ui/core and @object-ui/app-shell minor, each matching what that package publishes; Check Changeset green on d8570af. The merge-forward adds no package change (the manifest record is unpublished tooling state). Clause-②: yes on the PR body, the claim and the changeset, unchanged. Metadata-protocol: added unchanged.

Clause-②: yes

③ Boundary flags

CARRIED from 6413937921: open_questions empty on 6412203410; deviations 1 to 6 ANSWERED or NOTED as there; out-of-scope 1 and 2 ESCALATED to the seat as optional follow-up cards, not blocking; the readActionPayload edge NOTED.

Implemented-by: claude/project-thread-1kqwvu
Reviewed-by: session_01RyMY3prh9GRva1Tt37Di3i

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Seat 2 · re-armed after the merge-forward to d8570af6: contract re-review PASS comment 6415795343 (only scripts/sdui-manifest.record.json moved within the PR's files; the four hits are byte-identical). The ACCEPT (6413960327) stands; its PR-body correction item 7 now also covers this second merge of origin/main. Auto-merge re-armed.

Only the generated sdui manifest record conflicted; took main's side and re-recorded it from the merged tree.

Claude-Session: https://claude.ai/code/session_013w86pS2SMm6BU2PfGQ3vJ7
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 1ea8dd1c1e3b9c7482dc60669e24f3366b5dbc38
Local-runs: none
Scope: packages/spec/src/ui/action-on-success-executor.ts, packages/spec/src/ui/action.form.ts, packages/spec/src/ui/action.zod.ts, packages/spec/src/ui/component.zod.ts + packages/spec/liveness/action.json, packages/spec/src/ui/action-on-success.test.ts, packages/spec/src/ui/component-action-row-on-success-executor.test.ts, packages/lint/src/validate-predicate-path-refs.test.ts, packages/lint/src/validate-component-props-row-on-success.test.ts, .changeset/25800-flow-action-on-success.md, ui/packages/types/src/ui-action.ts, ui/packages/core/src/actions/ActionRunner.ts, ui/packages/app-shell/src/utils/flowResponse.ts, scripts/sdui-manifest.record.json (the only file this merge-forward moves). The remaining console tests, FlowRunner.tsx comment and the four regenerated content/docs pages are the lane seat's ACCEPT at TIER_DEFAULT.

Re-review after a second merge-forward. Head 1ea8dd1 is a 2-parent commit: parents d8570af (the head PASS record 6415795343 judged) and 1bbb7bf (origin/main, #26019 landed), made after a second merge-queue conflict. Verified delta, git read commands only:

  • The PR's net diff against its base, excluding scripts/sdui-manifest.record.json, is byte-identical before and after the merge: git diff b4199c96 d8570af6 (old merge-base to old head) and git diff 1bbb7bf4 1ea8dd1c (new base to new head) both hash sha256 103026e8, 1141 lines, cmp equal.
  • The four hits are byte-identical: blob ids at d8570af and 1ea8dd1 agree for each (1846bf00, 97125cc2, 7545ef0c, 9862b95b), and the hits-only diff against the base hashes sha256 b4b225a8 at both bases. These are the hunks 6415795343 judged.
  • Restricted to the PR's 30 files, git diff d8570af6 1ea8dd1c moves only scripts/sdui-manifest.record.json, +1/-1, the registrySourcesSha256 value; registrySources stays 922 and sdui.manifest.json is untouched by the PR. The head value (15b21b5b) equals neither parent's (d8570af: cace2de2; main 1bbb7bf: 5fd9e0eb), so it is a fresh digest of the merged registry sources. The file has merge: unspecified in .gitattributes (not merge=os-regen), so no Regen-provenance carry applies and the file is judged by its gate alone: Lint and Repo Gates (check-sdui-manifest) is green on the head.
  • Main's advance b4199c9..1bbb7bf (21 commits) touches none of the PR's 30 files other than the record file, and none of the files 6413937921 read for the claims (flowLaunch.ts, actionResponse.ts, serverActionHandler.ts, automation-service.ts, useConsoleActionRuntime.tsx, RecordDetailView.tsx). fix(types): the @object-ui/types/zod mirrors keep an unknown key instead of silently dropping it #26019's ui/packages/types change is confined to src/zod/*.zod.ts mirrors and their tests (41 files); it does not touch ui-action.ts, and no zod mirror of ActionResult exists at the head (git grep for ActionResult and handedOff under ui/packages/types/src/zod is empty), so the PR's additive optional ActionResult.handedOff member and the handlePostExecution gate that reads it are unaffected. The merge's only change to ui-action.ts against main is the PR's own +12 hunk.

Inputs read: card #25800 body and its five comments (triage 6398521414, premise 6409899481, claim 6410040122, os-dev-report 6412203410, ACCEPT 6413972897; no ruling newer than the previous record); PR #26011 body and 30-file list; the merge-forward delta above; the 63 check-runs on head 1ea8dd1, polled until every run completed (last at 13:47Z): 58 success, 5 skipped (the two opt-in harnesses, the live-PG Temporal conformance, Dogfood Verify CLI and the dogfood matrix placeholder), 0 failure. Among the greens: Check Changeset, Governed Surface Queue Guard, Spec property liveness, Lint and Repo Gates, Type Check (workspace, source, consumer, debt ledger), Test Core 1-8 (4/8 and 5/8 finished last), Test UI 1-16, Build Docs, Flag docs affected by code changes, Check PR Size.

① Derived judgments

  1. Items 1 to 9 of 6413937921, carried by 6415795343, CARRIED unchanged again: accept-set widening of ON_SUCCESS_EXECUTORS to api/script/flow read by both doors (RIGHT); refusal prose (RIGHT); published describe text and regenerated docs (RIGHT); Studio form gate (RIGHT); liveness ledger (RIGHT); console handedOff half read in the cited code (RIGHT); ${result.output.VAR} identity (RIGHT); no new barrel export (RIGHT); card premise (RIGHT). The hunks they judged are byte-identical on this head, and each gate they cite is green again on 1ea8dd1.
  2. Item 2 of 6415795343 (manifest digest re-record, RIGHT) CARRIED with the new values named above: a fresh digest, neither parent's, on a file without merge=os-regen, judged by its green gate. Nothing else in the diff changes meaning.
  3. Merge-forward interaction with fix(types): the @object-ui/types/zod mirrors keep an unknown key instead of silently dropping it #26019, RIGHT (new, verified above): no shared file, no ActionResult mirror, nothing the hits claim is altered.

② Semver level

CARRIED from 6413937921 via 6415795343: the changeset bumps @objectstack/spec, @object-ui/types, @object-ui/core and @object-ui/app-shell minor, each matching what that package publishes; Check Changeset green on 1ea8dd1. The merge-forward adds no package change (the manifest record is unpublished tooling state). Clause-②: yes on the PR body, the claim and the changeset, unchanged. Metadata-protocol: added unchanged.

Clause-②: yes

③ Boundary flags

CARRIED from 6413937921 via 6415795343: open_questions empty on 6412203410; deviations 1 to 6 ANSWERED or NOTED as there; out-of-scope 1 and 2 ESCALATED to the seat as optional follow-up cards, not blocking; the readActionPayload edge NOTED.

Implemented-by: claude/project-thread-1kqwvu
Reviewed-by: session_01RyMY3prh9GRva1Tt37Di3i

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Seat 2 · re-armed after the second merge-forward to 1ea8dd1c (origin/main at 1bbb7bf4, #26019 landed): contract re-review PASS comment 6417606265. Within the PR's files only scripts/sdui-manifest.record.json moved (the registry digest), the four hits are byte-identical, and all 63 check-runs on the head are complete with 0 failures. The ACCEPT (6413960327) stands. Auto-merge re-armed.

Takes main's per-package sdui manifest record layout and re-records the two registry packages this branch touches (core, types) from the merged tree.

Claude-Session: https://claude.ai/code/session_013w86pS2SMm6BU2PfGQ3vJ7
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 5dc48816bca331fff3bcf295290cdf88dc17e394
Local-runs: none
Scope: packages/spec/src/ui/action-on-success-executor.ts, packages/spec/src/ui/action.form.ts, packages/spec/src/ui/action.zod.ts, packages/spec/src/ui/component.zod.ts + packages/spec/liveness/action.json, packages/spec/src/ui/action-on-success.test.ts, packages/spec/src/ui/component-action-row-on-success-executor.test.ts, packages/lint/src/validate-predicate-path-refs.test.ts, packages/lint/src/validate-component-props-row-on-success.test.ts, .changeset/25800-flow-action-on-success.md, ui/packages/types/src/ui-action.ts, ui/packages/core/src/actions/ActionRunner.ts, ui/packages/app-shell/src/utils/flowResponse.ts, scripts/sdui-manifest.sources/core.json, scripts/sdui-manifest.sources/types.json (the only two files this merge-forward moves within the PR). The remaining console tests, FlowRunner.tsx comment and the four regenerated content/docs pages are the lane seat's ACCEPT at TIER_DEFAULT.

Re-review after a third merge-forward. Head 5dc4881 is a 2-parent commit: parents 1ea8dd1 (the head PASS record 6417606265 judged) and 601c773 (origin/main, 23 commits past 1bbb7bf, #26120 among them: the single scripts/sdui-manifest.record.json digest became one record per ui registry package under scripts/sdui-manifest.sources/). Verified delta, git read commands only:

  • (a) The PR's net diff against its base, excluding scripts/sdui-manifest.record.json and scripts/sdui-manifest.sources/, is byte-identical before and after the merge: git diff 1bbb7bf4 1ea8dd1c and git diff 601c7738 5dc48816 both hash sha256 103026e8, cmp equal. The PR's file list is 31 files = the same 29 hand-written and generated files plus the two per-package records; scripts/sdui-manifest.record.json has left the diff (head blob b4080228 equals main's).
  • (b) The four hits are byte-identical: blob ids at 1ea8dd1 and 5dc4881 agree for each (1846bf00, 97125cc2, 7545ef0c, 9862b95b), and the base-side blobs at 1bbb7bf and 601c773 agree too (a96432e1, da0842c7, 16d72892, e64cd0cd), so the hunks are the ones 6417606265 and its predecessors judged.
  • (c) Main's advance 1bbb7bf..601c773 touches none of the PR's 29 files and none of the files the earlier records read for claims (flowLaunch.ts, actionResponse.ts, serverActionHandler.ts, automation-service.ts, useConsoleActionRuntime.tsx, RecordDetailView.tsx) or the Scope + files above. Its only overlap with the PR's file list is the two per-package records themselves, which devx(sdui): one manifest record per ui registry package, so ui PRs on different packages stop conflicting #26120 created on main. Under ui/packages/types and ui/packages/core it changes one unrelated test (list-bind-only-11405.test.ts) and nothing in src proper; no ActionResult mirror appears.
  • (d) The two per-package records are the generator's output: core.json and types.json each move only their sha256 value, with sources (110 and 81) and manifestSha256 unchanged; sdui.manifest.json is untouched (blob 60e80870 on both main and head, 106 components). ui/packages/core and ui/packages/types are exactly the two registry packages the PR's ui source edits land in (app-shell, the third, has no registry record). Neither file carries merge=os-regen (git check-attr merge reads unspecified for both and for record.json, .gitattributes blob identical on main and head), so no Regen-provenance carry applies and the files are judged by their gate alone: Lint · repo & tooling gates (the check-sdui-manifest step) is green on the head.

Inputs read: card #25800 body and its five comments (triage 6398521414, premise 6409899481, claim 6410040122, os-dev-report 6412203410, ACCEPT 6413972897; no ruling newer than the previous record); PR #26011 body and 31-file list; the merge-forward delta above; the 63 check-runs on head 5dc4881, polled every 60s until every run completed (last at 14:59Z): 58 success, 5 skipped (the two opt-in harnesses, the live-PG Temporal conformance, Dogfood Verify CLI and the dogfood matrix placeholder), 0 failure. Among the greens: Check Changeset, Governed Surface Queue Guard, Spec property liveness, Lint & Repo Gates, Lint · repo & tooling gates, TypeScript Type Check with its four lanes (workspace, source, consumer, debt ledger), Test Core 1-8, Test UI 1-16, Build Core, Build Docs, Lint UI, Type Check UI, Build & E2E UI, Flag docs affected by code changes, Check PR Size. The whole diff was not re-read; the carried judgments rest on (a) and (b).

① Derived judgments

  1. Items 1 to 9 of 6413937921, carried by 6415795343 and 6417606265, CARRIED unchanged again: accept-set widening of ON_SUCCESS_EXECUTORS to api/script/flow read by both doors (RIGHT); refusal prose (RIGHT); published describe text and regenerated docs (RIGHT); Studio form gate (RIGHT); liveness ledger (RIGHT); console handedOff half read in the cited code (RIGHT); ${result.output.VAR} identity (RIGHT); no new barrel export (RIGHT); card premise (RIGHT). The hunks they judged are byte-identical on this head (verified (a) and (b)), and each gate they cite is green again on 5dc4881.
  2. Item 2 of 6415795343 and 6417606265 (manifest digest re-record, RIGHT) CARRIED in its new per-package form: the single record.json digest has returned to main's bytes, and the two registry packages the PR touches carry fresh per-package sha256 values with their source counts unchanged, verified (d), judged by the green gate.
  3. Item 3 of 6417606265 (merge-forward interaction with fix(types): the @object-ui/types/zod mirrors keep an unknown key instead of silently dropping it #26019, RIGHT) CARRIED: main's further advance adds no shared file and no ActionResult mirror, verified (c).
  4. Merge-forward interaction with devx(sdui): one manifest record per ui registry package, so ui PRs on different packages stop conflicting #26120, RIGHT (new, verified (d)): the record-layout change on main is absorbed exactly as its design intends, with the PR re-recording only the packages it moves and leaving every other package's record at main's value.

② Semver level

CARRIED from 6413937921 via 6415795343 and 6417606265: the changeset bumps @objectstack/spec, @object-ui/types, @object-ui/core and @object-ui/app-shell minor, each matching what that package publishes; Check Changeset green on 5dc4881. The merge-forward adds no package change (the per-package manifest records are unpublished tooling state). Clause-②: yes on the PR body, the claim and the changeset, unchanged. Metadata-protocol: added unchanged.

Clause-②: yes

③ Boundary flags

CARRIED from 6413937921 via 6415795343 and 6417606265: open_questions empty on 6412203410; deviations 1 to 6 ANSWERED or NOTED as there; out-of-scope 1 and 2 ESCALATED to the seat as optional follow-up cards, not blocking; the readActionPayload edge NOTED.

Implemented-by: claude/project-thread-1kqwvu
Reviewed-by: session_01RyMY3prh9GRva1Tt37Di3i

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Seat 2 · re-armed after the third merge-forward to 5dc48816 (origin/main at 601c7738, with #26120's per-package sdui records): contract re-review PASS comment 6419294223. Within the PR only the two per-package records scripts/sdui-manifest.sources/core.json and types.json moved (sha only), the four hits are byte-identical, and all 63 check-runs on the head are complete with 0 failures. The ACCEPT (6413960327) stands. Auto-merge re-armed.

Resolves packages/spec/liveness/action.json by hand: main's in-repo evidence, evidenceScope and producer for onSuccess.navigate, with this branch's #25800 note sentence (already pointing at in-repo ui/ paths) kept on main's note.

Claude-Session: https://claude.ai/code/session_013w86pS2SMm6BU2PfGQ3vJ7
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 26, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 7913854e1ffffdf5085494b5f9cb46a50a6179fa
Local-runs: none
Scope: packages/spec/src/ui/action-on-success-executor.ts, packages/spec/src/ui/action.form.ts, packages/spec/src/ui/action.zod.ts, packages/spec/src/ui/component.zod.ts + packages/spec/liveness/action.json (the one file this merge-forward moves within the PR), ui/packages/app-shell/src/utils/flowResponse.ts, ui/packages/core/src/actions/ActionRunner.ts, ui/packages/types/src/ui-action.ts (read at the head for the ledger note's claims), ui/packages/app-shell/src/views/FlowRunner.tsx and packages/spec/src/contracts/automation-service.ts (read because main's advance moved them). The remaining files are the lane seat's ACCEPT at TIER_DEFAULT, unchanged since 6419294223.

Re-review after a fourth merge-forward with one hand-resolved conflict. Head 7913854 is a 2-parent commit: parents 5dc4881 (the head PASS record 6419294223 judged) and 7111e14 (origin/main, 16 commits past 601c773, f79b9d5 among them: the renderer's liveness evidence moved in-repo under ui/). Verified delta, git read commands only (PR head read as refs/heads/pr26011 = 7913854):

  • (a) The PR's net diff against its base, excluding packages/spec/liveness/action.json, is the same before and after the merge: git diff 601c7738 5dc48816 and git diff 7111e14a 7913854e (1142 lines each) differ in exactly one line, the index header of ui/packages/app-shell/src/views/FlowRunner.tsx (d0b2f4b2..febd2a8a became e98d7c63..f40de657), because main moved that file under it; every hunk body is byte-identical. The file list is the same 31 files on both sides and equals the API's list; this merge re-records none of the 27 sdui records (scripts/sdui-manifest.sources/core.json and types.json still carry exactly the hunks 6419294223 judged).
  • (b) The four hits are byte-identical: blob ids at 1ea8dd1, 5dc4881 and 7913854 agree for each (1846bf00, 97125cc2, 7545ef0c, 9862b95b), and the base-side blobs at 601c773 and 7111e14 agree too (a96432e1, da0842c7, 16d72892, e64cd0cd), so the hunks are the ones 6413937921 and its successors judged.
  • (c) The action.json hunk against main is one note line on row props.onSuccess.children.navigate (@@ -217,7 +217,7 @@, 1 minus, 1 plus). On that row the head's evidenceScope (in-repo), evidence, producer, status (live) and verifiedAt equal main's byte for byte, and the head's note equals main's note (2778 bytes, unchanged by main) followed by the 649-byte sentence the PR appended at 5dc4881 (suffix sha256 ea7f6fe1, identical on both sides). The sentence's three claims hold at the head: ON_SUCCESS_EXECUTORS is new Set(['api', 'script', 'flow']) (action-on-success-executor.ts:71); judgeFlowLaunch returns { success: true, silent: true, handedOff: true } on the paused and refused arms only, the done arm stays { success, data, reload } plus silent when the flow's copy wins (flowResponse.ts:654-677); handlePostExecution gates the hop on result.success and not result.handedOff and action.onSuccess (ActionRunner.ts:1984), with handedOff an optional member of ActionResult (ui-action.ts:1125).
  • (d) Main's advance 601c773..7111e14 (130 files) touches none of ui-action.ts, ActionRunner.ts, flowResponse.ts, the spec tests, the lint tests, the changeset or the sdui records, and none of flowLaunch.ts, actionResponse.ts, serverActionHandler.ts, useConsoleActionRuntime.tsx or RecordDetailView.tsx. Two overlaps the brief did not name, both read: FlowRunner.tsx (feat(spec,automation,app-shell)!: a flow screen field typed file collects an upload, and the run resumes with the uploaded file's id #26134 adds the upload scope at lines 205-740; the PR's hunk is the docblock at line 75, comment only, merged without conflict and byte-identical against both bases) and automation-service.ts (feat(spec,automation,app-shell)!: a flow screen field typed file collects an upload, and the run resumes with the uploaded file's id #26134 extends the screen field type-hint docblock only; AutomationResult.output and status are unchanged, so item 7 of 6413937921 still reads true).

Inputs read: card #25800 body and its five comments (triage 6398521414, premise 6409899481, claim 6410040122, os-dev-report 6412203410, ACCEPT 6413972897; no ruling newer than the previous record); PR #26011 body and 31-file list; the merge-forward delta above; the previous records 6413937921, 6415795343, 6417606265 and 6419294223 and the seat notes beside them; the 63 check-runs on head 7913854, polled every 60s until every run completed (last at 16:07Z): 58 success, 5 skipped (the two opt-in harnesses, the live-PG Temporal conformance, Dogfood Verify CLI and the dogfood matrix placeholder), 0 failure. Among the greens: Check Changeset, Governed Surface Queue Guard, Spec property liveness, Lint & Repo Gates, Lint · repo & tooling gates, TypeScript Type Check with its four lanes, Test Core 1-8, Build Core, Lint UI, Type Check UI, Build & E2E UI, Flag docs affected by code changes, Check PR Size. The whole diff was not re-read; the carried judgments rest on (a) and (b).

① Derived judgments

  1. Items 1 to 9 of 6413937921, carried by 6415795343, 6417606265 and 6419294223, CARRIED unchanged again: accept-set widening of ON_SUCCESS_EXECUTORS to api/script/flow read by both doors (RIGHT); refusal prose (RIGHT); published describe text and regenerated docs (RIGHT); Studio form gate (RIGHT); liveness ledger (RIGHT, re-verified at (c) in its merged form); console handedOff half read in the cited code (RIGHT, re-read at the head in (c)); ${result.output.VAR} identity (RIGHT, automation-service.ts re-read in (d)); no new barrel export (RIGHT); card premise (RIGHT). The hunks they judged are byte-identical on this head (verified (a) and (b)), and each gate they cite is green again on 7913854.
  2. Items 2 to 4 of 6419294223 (per-package sdui records, RIGHT; interaction with fix(types): the @object-ui/types/zod mirrors keep an unknown key instead of silently dropping it #26019, RIGHT; interaction with devx(sdui): one manifest record per ui registry package, so ui PRs on different packages stop conflicting #26120, RIGHT) CARRIED: the two per-package records carry the same hunks (a), and main's further advance adds no sdui record change and no ActionResult mirror (d).
  3. Hand-resolved merge of packages/spec/liveness/action.json, RIGHT (new, verified (c)): the dev took main's three re-pointed fields verbatim (f79b9d5 moved the row's evidence from cross-repo objectui paths to in-repo ui/ paths) and kept exactly the PR's appended sentence, whose in-tree read points were already spelled as ui/ paths and now agree with the row's evidence realm; status stays live; Spec property liveness green on the head.
  4. Merge-forward interaction with feat(spec,automation,app-shell)!: a flow screen field typed file collects an upload, and the run resumes with the uploaded file's id #26134 on FlowRunner.tsx, RIGHT (new, verified (d)): the two changes are disjoint (docblock line 75 versus the upload scope from line 205), the textual merge carried both, and the PR's hunk is prose only.

② Semver level

CARRIED from 6413937921 via 6415795343, 6417606265 and 6419294223: the changeset bumps @objectstack/spec, @object-ui/types, @object-ui/core and @object-ui/app-shell minor, each matching what that package publishes; Check Changeset green on 7913854. The merge-forward adds no package change (the liveness ledger is unpublished tooling state). Clause-②: yes on the PR body, the claim and the changeset, unchanged. Metadata-protocol: added unchanged.

Clause-②: yes

③ Boundary flags

CARRIED from 6413937921 via 6415795343, 6417606265 and 6419294223: open_questions empty on 6412203410; deviations 1 to 6 ANSWERED or NOTED as there; out-of-scope 1 and 2 ESCALATED to the seat as optional follow-up cards, not blocking; the readActionPayload edge NOTED.

  • Reviewer's own, carried: the PR body still says "No conflict with feat(spec,analytics,lint,platform-objects,app-shell): a dataset measure can aggregate the time between two datetime fields of each record #25903 arose, so main was not merged" and names scripts/sdui-manifest.record.json as re-recorded; the head is now the fourth merge of origin/main. Body prose only; the seat's ACCEPT 6413960327 item 7 and notes 6415808473, 6417618498 and 6419322365 already cover it. ESCALATED as a PR-body correction for the seat, not blocking.
  • Reviewer's own, new: the brief's premise that main's advance touches none of the other PR files is off by one file, FlowRunner.tsx; read in (d), the overlap is a clean textual merge of disjoint hunks and introduces no flag. NOTED.

Implemented-by: claude/project-thread-1kqwvu
Reviewed-by: session_01RyMY3prh9GRva1Tt37Di3i

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Seat 2 · fourth merge-forward to 7913854e (origin/main at 7111e14a, with the hand-resolved packages/spec/liveness/action.json conflict against f79b9d5): contract re-review PASS comment 6420886973. The resolved hunk is main's row plus exactly the PR's note sentence, the four hits are byte-identical, and all 63 check-runs on the head are complete with 0 failures. The ACCEPT (6413960327) stands. Auto-merge stayed armed through the push and GitHub queued this head at 16:07Z.

Takes main's scripts/sdui-manifest.sources/core.json and re-records it from the merged tree; regenerates content/docs/references/data/object.mdx, which the merge deferred.

Claude-Session: https://claude.ai/code/session_013w86pS2SMm6BU2PfGQ3vJ7
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

objectstack-fleet Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor Author

Seat note (domain:spec seat 2, session_01RyMY3prh9GRva1Tt37Di3i): the Test UI (12/16) failure on head 0b9b772 is not this PR's.

Plan: once #26231 lands, this branch takes main forward and CI re-runs; auto-merge is re-armed after the re-review record on that head and green CI. No change is pushed here for this failure.

Brings in #26231 (the ObjectSchema corpus guards re-pointed to 48); merged clean, nothing deferred for regeneration.

Claude-Session: https://claude.ai/code/session_013w86pS2SMm6BU2PfGQ3vJ7
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: a3f1f482ad01eb653cd8837dd88a2b32f56e2ae7
Local-runs: none
Scope: packages/spec/src/ui/action-on-success-executor.ts, packages/spec/src/ui/action.form.ts, packages/spec/src/ui/action.zod.ts, packages/spec/src/ui/component.zod.ts + scripts/sdui-manifest.sources/core.json and content/docs/references/data/object.mdx (the two files the fifth merge-forward moved within the PR), ui/packages/app-shell/src/views/RecordDetailView.tsx (read because main's advance moved it; a claim-read file of 6413937921), ui/packages/app-shell/src/services/MetadataService.retiredObjectEnabled.test.ts and MetadataService.specKeyObjectPayload.test.ts (read because they were the two reds on the intermediate head). The remaining files are the lane seat's ACCEPT at TIER_DEFAULT, unchanged since 6419294223.

Re-review after the fifth and sixth merge-forwards, kept short. This head is reached in two hops from 7913854 (the PASS record 6420886973 judged): 0b9b772 merged origin/main at df8aac4 (18 commits past 7111e14, #26033 among them), and a3f1f48 merged origin/main at a1477de (9 commits past df8aac4, #26231 among them). Verified delta, git read commands only (PR head read as refs/pull/26011/head = a3f1f48; the brief named 0b9b772, which was superseded while its check-runs were still being polled, so this record names the head that stands and judges both hops):

  • (a) The PR's net diff against its base, excluding scripts/sdui-manifest.sources/, is the same before and after both hops: git diff 7111e14a 7913854e, git diff df8aac41 0b9b7722 and git diff a1477dec a3f1f482 (1141 lines each) differ in exactly two lines, both on content/docs/references/data/object.mdx: its index header (c98d0986..fadb40a3 became e26e6057..cfa78d28) and its hunk header (@@ -515,7 +515,7 @@ became @@ -522,7 +522,7 @@), because feat(spec,plugin-security,lint): sharing.controlledByParent.ownerMayEdit — a detail row's owner updates it with read on the master #26005 added seven generated lines above the PR's row; every plus and minus line is byte-identical. The file list is the same 31 files on all three sides and equals the API's list (31 files, +393/-122); no source, test, ledger or changeset file moved.
  • (b) The four hits are byte-identical: blob ids at 7913854, 0b9b772 and a3f1f48 agree for each (1846bf00, 97125cc2, 7545ef0c, 9862b95b), and the base-side blobs at 7111e14, df8aac4 and a1477de agree too (a96432e1, da0842c7, 16d72892, e64cd0cd), so the hunks are the ones 6413937921 and its successors judged.
  • (c) First hop, main's advance 7111e14..df8aac4 (137 files): it touches object.mdx (feat(spec,plugin-security,lint): sharing.controlledByParent.ownerMayEdit — a detail row's owner updates it with read on the master #26005, read in (a)), core.json (feat(ui): an edit form's state-machine select offers only the moves the server accepts #26033 added a validator source, sources 110 to 111) and RecordDetailView.tsx (fix(app-shell,react,plugin-detail): fold an override-only admin's approval levers into one menu and draw Recall once #26053, the approval bar's Recall folding at lines 383-3850, nothing in the flow-action door or the result it reads; the PR does not change this file and its four RecordDetailView.*.test.tsx pins are on the runtime result, not on this code), and none of ui-action.ts, ActionRunner.ts, flowResponse.ts, FlowRunner.tsx, automation-service.ts, the liveness ledger, the spec or lint tests, the changeset, ui/packages/types or packages/spec/src/ui. A git merge-tree replay of 7913854 onto df8aac4 reports exactly one content conflict, core.json, plus the os-regen deferral on object.mdx (merge=os-regen; none of the sdui record files carries it), which matches the dev's report. Second hop, main's advance df8aac4..a1477de (285 files): it touches no file of the PR and no claim-read file; it re-records three other sdui records (components.json, i18n.json, plugin-map.json) and, in test(app-shell): re-point the ObjectSchema corpus guards to 48 after sharing was adopted #26231, re-points the two ObjectSchema corpus guards from 47 to 48. The head's tree (2c52c851) is byte-identical to git merge-tree 0b9b7722 a1477dec, so the second hop is the automatic textual merge and carries only main's own changes.
  • (d) The two records move sha only: core.json on the head reads sources 111 and manifestSha256 1f6e1ccb exactly as main does, with sha256 a5c4bc9e (main) to 05fe4580 (PR); types.json carries the identical hunk 6420886973 judged (sources 81, 30832d40 to d7928a68). scripts/sdui-manifest.record.json and the manifest itself are unchanged against main. The regenerated object.mdx differs from main's only by the PR's existing onSuccess row. On both heads the Lint · repo & tooling gates job, which runs the check-sdui-manifest step (SDUI manifest is present, intact and fresh at the objectui pin), is green, the step included (jobs 121712161108 on 0b9b772 and 121725219888 on a3f1f48, both success).

The intermediate head 0b9b772 completed 62 check-runs with two reds, Test UI (12/16) (MetadataService.retiredObjectEnabled.test.ts:161, expected 48 to be 47) and Test UI (7/16) (MetadataService.specKeyObjectPayload.test.ts:175, the same assertion). Read as base-red, not attributable to this diff: the two guards pin the top-level key count of ObjectSchema, #26005 (5e113b3, in that hop's main advance) added sharing to it, main itself fails Test UI (12/16) with the identical assertion at 6862ef6 (job 121711671210, 16:57Z) and at the PR's then-base df8aac4, the PR changes neither guard file (blobs 99e40b91 and e4058be1 equal main's) and no file under packages/spec/src/data. Tracked by #26218, fixed by #26231 (a1477de), which this head carries; the seat's note 6422403296 says the same.

Inputs read: card #25800 body and its five comments (triage 6398521414, premise 6409899481, claim 6410040122, os-dev-report 6412203410, ACCEPT 6413972897; no ruling newer than the previous record); PR #26011 body (unchanged since 6420886973) and 31-file list; the two merge-forward deltas above; the previous records 6413937921, 6415795343, 6417606265, 6419294223 and 6420886973 and the seat notes beside them (the newest, 6422403296); the check-runs on 0b9b772 (62, all completed: 55 success, 5 skipped, the 2 base-reds above) and the 63 check-runs on head a3f1f48, polled every 60s until every run completed (last at 17:42Z): 58 success, 5 skipped (the two opt-in harnesses, the live-PG Temporal conformance, Dogfood Verify CLI and the dogfood matrix placeholder), 0 failure. Among the greens: Check Changeset, Governed Surface Queue Guard, Spec property liveness, Lint & Repo Gates, Lint · repo & tooling gates, Lint · docs & code guards, TypeScript Type Check with its four lanes, Test Core 1-8, Build Core, Build Docs, Lint UI, Type Check UI, Build & E2E UI, Test UI 1-16, Flag docs affected by code changes, Check PR Size. The whole diff was not re-read; the carried judgments rest on (a) and (b).

① Derived judgments

  1. Items 1 to 9 of 6413937921 and items 2 to 4 of 6419294223, carried by 6420886973 as its items 1 and 2, CARRIED unchanged again: accept-set widening of ON_SUCCESS_EXECUTORS to api/script/flow read by both doors (RIGHT); refusal prose (RIGHT); published describe text and regenerated docs (RIGHT); Studio form gate (RIGHT); liveness ledger (RIGHT); console handedOff half (RIGHT); ${result.output.VAR} identity (RIGHT); no new barrel export (RIGHT); card premise (RIGHT); per-package sdui records and the interactions with fix(types): the @object-ui/types/zod mirrors keep an unknown key instead of silently dropping it #26019 and devx(sdui): one manifest record per ui registry package, so ui PRs on different packages stop conflicting #26120 (RIGHT). The hunks they judged are byte-identical on this head (verified (a) and (b)), and each gate they cite is green again on a3f1f48.
  2. Items 3 and 4 of 6420886973 (the hand-resolved action.json merge, RIGHT; the FlowRunner.tsx interaction with feat(spec,automation,app-shell)!: a flow screen field typed file collects an upload, and the run resumes with the uploaded file's id #26134, RIGHT) CARRIED: neither hop's main advance touches either file (c).
  3. Hand-resolved merge of scripts/sdui-manifest.sources/core.json in the first hop, RIGHT (new, verified (c) and (d)): main's side was taken and the record re-recorded over the merged tree, so the row names main's 111 sources and the PR's sha, as the lockstep gate requires; the second hop left it untouched.
  4. Regeneration of content/docs/references/data/object.mdx after the os-regen deferral, RIGHT (new, verified (a) and (d)): the head's file is main's plus the one onSuccess row the PR has carried since eaebe2e, and the docs gates are green on the head.
  5. The two Test UI reds on 0b9b772, base-red and not this diff's (new, read above): the second hop carries test(app-shell): re-point the ObjectSchema corpus guards to 48 after sharing was adopted #26231 and the two shards are green on a3f1f48 (Test UI 12/16 job 121729302068 at 17:28Z, Test UI 7/16 job 121729301926 at 17:31Z).

② Semver level

CARRIED from 6413937921 via 6415795343, 6417606265, 6419294223 and 6420886973: the changeset bumps @objectstack/spec, @object-ui/types, @object-ui/core and @object-ui/app-shell minor, each matching what that package publishes; Check Changeset green on a3f1f48. The two merge-forwards add no package change (a sdui record and a generated doc are unpublished tooling state). Clause-②: yes on the PR body, the claim and the changeset, unchanged. Metadata-protocol: added unchanged.

Clause-②: yes

③ Boundary flags

CARRIED from 6413937921 via 6415795343, 6417606265, 6419294223 and 6420886973: open_questions empty on 6412203410; deviations 1 to 6 ANSWERED or NOTED as there; out-of-scope 1 and 2 ESCALATED to the seat as optional follow-up cards, not blocking; the readActionPayload edge NOTED.

  • Reviewer's own, carried: the PR body still says "No conflict with feat(spec,analytics,lint,platform-objects,app-shell): a dataset measure can aggregate the time between two datetime fields of each record #25903 arose, so main was not merged" and names scripts/sdui-manifest.record.json as re-recorded; the head is now the sixth merge of origin/main, and the per-package records core.json and types.json are what moves. Body prose only; the seat's ACCEPT 6413960327 item 7 and the notes through 6422403296 already cover it. ESCALATED as a PR-body correction for the seat, not blocking.
  • Reviewer's own, new: the brief's premise that the first hop's main advance touches none of the claim-read files is off by one file, RecordDetailView.tsx; read in (c), the overlap is the approval bar's Recall folding, disjoint from the flow-action door, and introduces no flag. NOTED.
  • Reviewer's own, new: the brief named head 0b9b772; the dev pushed a3f1f48 while that head's runs were completing, so this record names a3f1f48 and judges 0b9b772's reds in passing. NOTED.

Implemented-by: claude/project-thread-1kqwvu
Reviewed-by: session_01RyMY3prh9GRva1Tt37Di3i

VERDICT: PASS

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:ui size/l tests tooling

Projects

None yet

2 participants