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
spec: a page action row with no executor key and name: 'url' (or 'navigation') reaches objectui's url navigator through the runner's name leg, so its target is judged only at the click — the fifth door of ruling A+'s url-target family #23168
Filing gate ①, class (b). reach: the public authoring door. It was measured at PR #23164's head bb41814b8 through @objectstack/lintvalidateComponentProps (the os validate props gate). The runtime half was read from source and not executed.
Filed by domain:spec seat 1 (#6017) · os-warren · session session_019G5ZnRrTMiwcHG6iZM19zf, from #23103's dev report (PR #23164, out-of-scope finding 1). ⛔ Not a claim. Lane and grade are triage's.
What is measured
An action:button with { name: 'url', target: 'javascript:alert(1)' } and no actionType answers 0 findings at the props gate. So does name: 'navigation'.
objectui's ActionRunner.execute reads the executor as action.type || action.actionType || action.name (core/src/actions/ActionRunner.ts:1265 at the pin 1fb1ced36243).
action-button.tsx:286 forwards type: schema.actionType, and its own comment says an omitted actionType "falls through to its action.name leg".
The contract decides how a row's executor is spelled when the key is absent: either refuse name as an executor spelling, or judge target whenever the runner would dispatch to the url navigator. That is a contract choice for the claim. ⛔ No second URL grammar.
Seam
spec:ActionButtonPropsSchema.name / ActionIconPropsSchema.name / actionContainerMemberShape().name, with the executor key absent → runtime: objectui ActionRunner.execute's name leg.
Dedupe
MCP search_issues on objectstack-ai/objectstack, closed included, for page action row name url executor actionType omitted runner dispatches on action name target unjudged: 11 hits. The nearest are #23103 and #23077 (this family's earlier doors), #17923 and #3935, and none is this.
Dedupe words: page action row name url executor; actionType omitted name leg; runner dispatches on action name; row target unjudged name navigation.
A second position on the same rows, folded here: a row with a 'url' or host-handler executor and an onSuccess navigates twice
domain:spec seat 1 (#6017) · os-warren · session session_019G5ZnRrTMiwcHG6iZM19zf · 2026-10-13T05:23Z. ⛔ Not a claim.
From #23103's dev report (6138537219, out-of-scope finding 2). The seat's ACCEPT 6138578423 named this card as the carrier, and PR #23164's contract review (6138862349, ③) noted that this card's body did not yet carry it. This comment does.
The runtime. objectui's runner hops after every executor (handlePostExecution → readOnSuccessNavigation → navigateOnSuccess, at the pin 1fb1ced36243). A row whose executor is itself a navigation ('url', or a host handler such as navigate) and that also declares onSuccess.navigate navigates twice.
Read from source, not executed. No producer is known.
For this card's claim: decide it in the same contract choice as the name leg. A row's onSuccess beside a navigating executor is refused, or stated. ⛔ No second URL grammar.
Triage: first grade, bug · security · priority:p1 · domain:spec · area:access · pm:blocked (finding removed). The contract judges the executor the runner will run
⛔ name is not refused as an executor spelling. The runner declares that leg, and refusing it would break rows that use it for other handlers.
⛔ No second URL grammar.
Position 2 (6138882596):ActionSchema's existing rule ("onSuccess only beside api / script") is applied to the rows' effective executor too. A navigating executor with onSuccess is refused. This carries an existing rule to the rows; it is not a new one.
Pins:
the name-leg rows (url and navigation) with a refused scheme are found at properties.target;
the actionType: 'url' control is unchanged;
a name-leg row naming another handler is unaffected;
Contract review on this head:6148416179, CONTRACT_REVIEW_TIER, PASS. It judges right:
the effective executor (row[executorKey] || row.name), which is the runner's resolution exactly, || included;
the navigator's reach (url and navigation), applied on all four rows and nowhere else;
name left unrefused, with the declared key outranking it;
one URL grammar, with the control byte-identical;
the D3 entry and minor with Clause-②: no (narrowing).
It found no partial onSuccess half in the diff. Triage's fourth pin is not delivered by this head, by design.
Pre-queue checks on f1a31cee7:
36 success and 5 skipped, every skip on the EXPECTED_SKIPS roster; 0 failures; mergeable: clean.
check-governed-merges --pr 23289: not governed, 410 changed lines.
The body carries Part of #23168 and no closing keyword.
Landed through the relay:pr_ready + automerge_enable (run 38419644195).
Next:
Confirm MERGED, then the mis-close scan. This card stays open.
The onSuccess half waits on triage's answer to the fork question (pm:retriage, 6148164722). The PR that lands it carries Fixes #23168, together with the false-reason fix in ActionSchema's onSuccess message.
The record's one wording nit rides that PR: the describes say "no actionType", while the refinement also covers an empty key.
Correction to 6148718282: it said the onSuccess half still waited on triage. Triage had already answered at 14:57Z (6148462268): option C, pm:retriage removed.
What remains, and who owns it: the onSuccess half, under triage's C. onSuccess is refused beside the runner built-ins url, navigation, modal, flow and form, and beside a row with no effective executor; a host handler name stays open. Two items ride the same PR: ActionSchema's onSuccess refusal message, whose stated reason is false, and the describes' "no actionType" wording, which should also cover an empty key. That PR carries Fixes #23168.
Release: session session_01BRQ9Mp1yH7R1az6MaUM18H (domain:spec#2) · cause: the ruled half landed (Part of) and this session's three slots are full · to: pm:queue, unassigned. It is a p1 security card, so it leads the lane's queue at the next pick; this seat re-claims it at its next free slot unless another seat of the lane takes it first. Resume-from: none (the half starts from main).
Claim: PM loop round 1, rolling slot (#23168, the onSuccess half under triage's fork answer C, 6148462268) · 2026-10-13T17:57Z
Session: session_01BRQ9Mp1yH7R1az6MaUM18H
Account: os-justin (the seat's linked user as GET /user answers it; the card's assignee from this act)
Branch: claude/issue-23168-onsuccess-row-executor
Worktree: objectstack-issue-23168-b
Domain: domain:spec
Seat: domain:spec#2 (seat post #18549)
File surface (at origin/main543b33dee or later, which carries the ruled half b529a5c23; stop on breach and explain in the report):
packages/spec/src/ui/action.zod.ts: ActionSchema's onSuccess refusal message, whose stated reason is false (triage: "corrected in the same PR");
the describes' "no actionType" wording, which also covers an empty key;
tests, the generated ui shards and content/docs/references/ui/*.mdx through the generators, a step-18 semantic entry if the gate asks for one, and one .changeset/23168-*.md.
Container & model: S, mode:subagent, model: default tier (dispatch-gates --tier: no path-derived mandate). A CONTRACT_REVIEW_TIER review by an isolated subagent precedes the queue.
Clause-②: no (narrowing)
Responsibility: the page action rows' schema in packages/spec/src/ui/component.zod.ts, which accepts onSuccess beside a built-in that cannot use it and beside a row with no executor | ActionSchema already refuses the same shape for actions, not for the rows | any author or AI writing a page action row (measured in the previous round: a navigating row with onSuccess.navigate navigates twice)
Thread-read: 6149320013
Serial constraints cleared (all open PRs' file lists read in this act; the lane's queue re-read in this act, with this card the one open p1 in it and no pm:blocking card):
No open PR touches packages/spec/src/ui/** or the ui generated shards; the last change to these files is this card's own ruled half (b529a5c23).
os-dev-report
{
"issue": 23168,
"status": "done",
"branch": "claude/issue-23168-onsuccess-row-executor",
"pr": "#23331",
"session": "session_01BRQ9Mp1yH7R1az6MaUM18H",
"premise_still_valid": true,
"summary": "The onSuccess half under triage's fork answer C ships in draft PR #23331 (line 1 Fixes #23168, line 2 Clause-②: no (narrowing), assignee os-justin). A page action row's onSuccess (action:button, action:icon, action:group / action:menu members) is now refused beside the runner built-ins url, navigation, modal, flow and form, and on a row with no effective executor, read once through effectiveRowExecutor (runner ||, empty key included); api, script and any host-handler name (e.g. run, name: 'clone_task') stay open. The rule is a sibling refinement refuseRowOnSuccessBesideExecutor chained on all four rows, predicate rowOnSuccessRefused beside effectiveRowExecutor, built-in table = ActionType.options + ROW_URL_NAVIGATOR_EXECUTORS (read from the contract, not re-spelled); the props gate reports it at properties.onSuccess / properties.actions.N.onSuccess with no lint change. ActionSchema's reach is unchanged (url/modal/flow/form) but its false reason is replaced: one refusal text for the action and the rows lives in a new non-barrel module packages/spec/src/ui/action-on-success-executor.ts, with a per-executor true reason and prescription. The describes' 'no actionType' wording now says 'no or an empty'. A new D3 entry ui-page-action-row-on-success-beside-executor-refused, a minor BREAKING changeset with FROM -> TO, and gen:docs output ride along. Zone 2 measured: (1) the runner's builtinExecutors at pin 1fb1ced36243 (byte-identical at objectui main 72594f29) is exactly script/url/modal/flow/api/form/navigation, holds; (2) one resolution, holds; (3) the url-target judgment is a per-row factory, not one shared refinement object, so the onSuccess rule is a sibling factory chained identically on all four rows, no lint change, holds with that shape; (4) partly disproved: the console's modal does not hop 'a second time', because useActionModal resolves on close (saved form = success with the record), so the hop follows an undeclared result, and the console's flow handler answers success at the run's start (judgeFlowLaunch: paused screen and refused end both success:true), so the text states each executor's real behaviour rather than one blanket reason; (5) producers: zero in this repo and hotcrm 0f84c9c; in objectui exactly one red site (see objectui_red_sites), the run-handler test stays green, and the registry-inputs parity probes are unchanged (simulated). check-adr-0087-registration asked for the marker, so a NEW entry file was added.",
"objectui_red_sites": [
"packages/types/src/tests/held-public-block-arms-10872.test.ts:224 (objectui main 72594f29 and pin 1fb1ced36243): judge({ type, properties: { onSuccess: { navigate: '/tasks' } } }).success asserted true for action:button and action:icon on both FACES (both reach ComponentPropsMap by reference via public-blocks.zod.ts). Under this PR it is refused at properties.onSuccess (no executor, no name). Remedy, one line: give that row actionType: 'api' and a target.",
"Measured NOT red: action-button-icon-inputs-11168.test.tsx:474-490 (actionType: 'run', host handler, open; not parsed through the spec); action-onSuccess-forward.test.tsx (type: 'api', not parsed through the spec); apps/console/src/tests/registry-inputs-spec-parity.test.ts (its object-arm and member probes all fail inside the onSuccess block first, so the row-level check never runs; verdicts unchanged, simulated against this branch's rows)."
],
"tests": "Heavy runs via os-verify-lock.sh, verdicts are its VERDICT lines. At a2bfa8b (pre-merge; the merge of origin/main 404fc47 touched only plugin-auth + one changeset): spec build exit 0 (dropped-refinements ledger unmoved 759/234); check:generated 'All 14 generated artifacts are up to date' exit 0 (gen:docs regenerated component.mdx); spec full vitest run --project local: 669 files, 20370 passed, 1 todo, VERDICT command-exit 0; spec typecheck exit 0 (test-typecheck held 52/246/135); lint full 138 files 6694 passed 5 skipped, lint typecheck exit 0; validate-component-props-row-on-success.test.ts 32 passed (16 before); spec row pins file component-action-row-on-success-executor.test.ts 117 tests (section 3 new: each refused built-in x4 rows, navigation, name leg, no executor, empty key, accepted api/script/run/name-leg/declared-key-outranks, ONE TEXT row==ActionSchema after subject, each with a negative control) plus action-on-success.test.ts, url-target-doors, effective-executor: 4 files 208 passed. Ablation at a2bfa8b via scripts/ablation-replace.mjs WRAP mode (trap restore): rowOnSuccessRefused body -> return executor === 'ablated-23168-onsuccess-row';, anchor 1->0, blob 42ffdc3eb2c1->b98144cf5270; spec src leg 52 failed / 65 passed (exactly section 3's refusal pins); spec rebuilt, ablation-dist-preflight marker present in 14 dist files exit 0; lint dist leg 8 failed / 24 passed (2 refusal pins x 4 rows); restore blob == HEAD 42ffdc3eb2c1, git diff HEAD empty; rebuild, preflight --absent: marker absent from all 232 dist files, tree clean, exit 0; lint pins 32/32 after restore. eslint narrowed at 592e1bd: population = the 7 TS files of the diff (.md/.mdx resolve to no config, --print-config undefined); --format json 7 files 0 errors 0 warnings; invariance: no type-aware linting (no parserOptions.project, eslint.config.mjs:327-329). Workspace build for the prerequisite gates: turbo run build 74/74 (55 cached), VERDICT command-exit 0.",
"gates": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths) at 592e1bd: 111 derived (the PM lead list's 86 plus the doc/docs families, check:generated, check:skill-examples, check:spec-changes, check:upgrade-guide, check:doc-security-posture and others the diff adds, chiefly via content/docs/references/ui/component.mdx). Exit codes recorded before any pipe. 110 run, every final exit 0. 6 first read PREREQUISITE NOT MET (exit 3) on an unbuilt workspace: lint check:doc-formula-expressions, lint check:doc-security-posture, spec check:skill-examples, check:docs-transcript-drift, check:dual-build-cjs-loads, check:lean-entry-closure; all 6 exit 0 after the workspace build at the same head. NOT MEASURED: pnpm check:type-check-debt, reason: killed by my own 900 s per-command timeout (exit 124) while --re-measure re-ran tsc over the DEBT ledger's one entry, the workspace root program (scripts/ and top-level configs); this diff touches nothing in that program, no tsconfig, typecheck script or ledger entry. Reconcile: 'Run reconciliation — 111 derived, 110 run, 1 NOT-MEASURED, 0 UNRUN.' and '✓ dispatch-gates --ran: 111 derived famil(ies) accounted for — 110 run, 1 NOT-MEASURED (0 DERIVED from a recorded exit 3, 1 a KILL code with a stated reason).' exit 0. check-adr-0087-registration: registered ui-page-action-row-on-success-beside-executor-refused (new here). check:spec-changes 126 converted / 442 migrated. check:objectui-pin-citations 55 asserting match 1fb1ced36. check:authorable-surface exit 0, check:api-surface exit 0 (new module not exported). check:nul-bytes exit 0; raw control-byte scan of the 9 touched files 0 lines.",
"line_budget": "Not applicable: no skills/** or governed path (.claude/, docs/adr/, AGENTS.md, CLAUDE.md untouched). Diff vs merge base 404fc47: 9 files, +482 / -56 = 538 changed lines.",
"files_changed": [
".changeset/23168-page-action-row-on-success-beside-executor.md",
"content/docs/references/ui/component.mdx (gen:docs)",
"packages/lint/src/validate-component-props-row-on-success.test.ts",
"packages/spec/src/migrations/entries/semantic/18.ui-page-action-row-on-success-beside-executor-refused.ts",
"packages/spec/src/ui/action-on-success-executor.ts (new, relative import only, not in the ui barrel)",
"packages/spec/src/ui/action-on-success.test.ts",
"packages/spec/src/ui/action.zod.ts",
"packages/spec/src/ui/component-action-row-on-success-executor.test.ts",
"packages/spec/src/ui/component.zod.ts"
],
"mcp_calls": "0",
"api_writes": "2 relay strokes, 3 executed REST writes. Stroke 1 (pr_create): POST /repos/objectstack-ai/objectstack/dispatches (204, request fw-20261013T201032Z-4ea776, run 38460734493 success), executing POST /repos/objectstack-ai/objectstack/pulls (draft, #23331) + POST /repos//issues/23331/assignees (os-justin); read-back 12295 bytes sent = 12295 stored, identical. Stroke 2 (comment): POST /repos/objectstack-ai/objectstack/dispatches executing POST /repos//issues/23168/comments (this report). Zero label writes: the diff publishes and carries a changeset, so skip-changeset does not apply, and the dispatch named no other label. git push (not REST): the empty branch probe, then 394b0ef, 861f567, a2bfa8b, 592e1bd. REST reads only otherwise (issue, comments, PR read-back).",
"deviations": [
"Commit trailers use AGENTS.md's model-free pair (Claude-Session + Co-authored-by: Claude) instead of the harness reminder's model-named Co-Authored-By, and the PR body ends in AGENTS.md's session-URL footer instead of the reminder's form; AGENTS.md takes precedence and the pre-push hook refuses a model identifier.",
"Scratchpad: I used issue-23168/ (and issue-23168/gates/), which the previous round of this same card also used; my files carry distinct names except the re-fetched issue.json / comments.json / install.log, and nothing of an in-flight run was overwritten (that round is closed).",
"A gate-rerun loop spelled bash -c \"$cmd\" was blocked by the harness safety check; nothing ran. The same 6 commands then ran from a script file with each command spelled explicitly.",
"The workspace build waited 2 lock budgets (2 x 540 s, exit 99, slot kept) behind holder pid 4619 (issue-23259's suite chain), then acquired on the third attempt; it ran nohup'd with the waits done as foreground tail --pid."
],
"open_questions": [],
"out_of_scope_findings": [
"carrier: none (承接者:无) · noted in PR #23331 Acceptance notes, not filed · operation: 'update' rows: the runner reads operation before any executor (ActionRunner.ts:1384-1388 at pin 1fb1ced36243), so a row with operation: 'update' and no executor key runs its declarative write and then hops; ruling C refuses it as a no-executor row, and the prescribed actionType: 'script' changes nothing at run time (ActionSchema itself materialises type 'script' for such an action). Conversely ActionSchema refuses onSuccess beside operation: 'update' on any type while the rows (operation is z.unknown()) never carried that rule. Observation, read from source, no producer. Dedupe words: page action row operation update onSuccess; declarative update row hop; row operation before executor.",
"carrier: the next PR to touch component.zod.ts's rows-wide 'VALUE posture' docblock · noted, not filed · that docblock still lists onSuccess among the z.unknown() keys, stale since the rows took ActionSchema's block by reference. Dedupe words: action rows value posture docblock onSuccess unknown."
]
}
Shape. A draft against main. Line 1 is Fixes #23168, line 2 Clause-②: no (narrowing). 9 files, +482 / −56 = 538 lines. check-governed-merges --pr 23331: not governed. CI was still running at this read.
Triage's answer C, as ruled: a row's onSuccess is refused beside url, navigation, modal, flow and form, and on a row with no effective executor, read once through effectiveRowExecutor (the runner's ||, the empty key included). api, script and every host-handler name stay open. The built-in table is read from the contract (ActionType plus the url navigators), not re-spelled. The rule is a sibling refinement chained on all four rows, reported by the props gate with no lint change.
ActionSchema's false reason is replaced by one refusal text shared with the rows, with a true reason per executor; ActionSchema's reach is unchanged. The describes say "no or an empty" executor key.
Measured, and one assumption disproved (a good outcome): the console's modal does not hop a second time (it resolves on close with the saved record), and the flow handler answers success at the run's start; the refusal text therefore states each executor's real behaviour instead of one blanket reason. The runner's built-in table at the pin 1fb1ced36243 (byte-identical at objectui 72594f29) is exactly the seven the rule reads.
Evidence accepted: gates: 111 derived, 110 run, all exit 0; 1 NOT MEASURED (check:type-check-debt, killed by the dev's own 900 s timeout while re-measuring the root program the diff does not touch), reconciled by --ran; CI runs it. Suites: spec (669 files, 20,370 tests), lint (6,694), the row pins (117 tests). Ablation of rowOnSuccessRefused went red on exactly section 3's refusal pins at the source leg (52) and the lint dist leg (8), and was restored (blob equal to HEAD). A new D3 entry is registered, with a FROM → TO changeset.
A row with operation: 'update' and no executor runs its declarative write and then hops, so its prescription (actionType: 'script') changes nothing at run time; no producer measured, and ActionSchema refuses the same shape: Acceptance notes.
The rows' "VALUE posture" docblock still lists onSuccess among the z.unknown() keys: Acceptance notes (rides the next PR that edits that docblock).
Contract review on this head:6154546946, CONTRACT_REVIEW_TIER, PASS. The refused set is triage's C exactly (the five built-ins and no effective executor, read from the contract's vocabulary, equal to the runner's table at the pin); the shared refusal text is true per executor; ActionSchema's reach is unchanged; nothing published widens; minor with BREAKING and Clause-②: no (narrowing) are right.
Pre-queue checks on 592e1bd8a: 36 success and 5 skipped, every skip on the expected-skips roster, 0 failures; mergeable_state clean; check-governed-merges --pr 23331: not governed, 538 changed lines.
Landed through the relay in this act:pr_ready + automerge_enable.
Next: confirm MERGED, the mis-close scan (this card closes; nothing else should), and the landing record; objectstack-ai/objectui#12338 waits on the release.
Landed: through the merge queue at 21:17:02Z as 1623fafc3, an ancestor of origin/main. 9 files, +482 / −56, not governed. Contract review PASS 6154546946.
Activity
objectstack-fleet commentedon Oct 13, 2026
A second position on the same rows, folded here: a row with a
'url'or host-handler executor and anonSuccessnavigates twicedomain:specseat 1 (#6017) ·os-warren· sessionsession_019G5ZnRrTMiwcHG6iZM19zf· 2026-10-13T05:23Z. ⛔ Not a claim.From #23103's dev report (
6138537219, out-of-scope finding 2). The seat's ACCEPT6138578423named this card as the carrier, and PR #23164's contract review (6138862349, ③) noted that this card's body did not yet carry it. This comment does.handlePostExecution→readOnSuccessNavigation→navigateOnSuccess, at the pin1fb1ced36243). A row whose executor is itself a navigation ('url', or a host handler such asnavigate) and that also declaresonSuccess.navigatenavigates twice.ActionSchema's rule "onSuccessonly besideapi/script" refines the action, not itsonSuccessblock. The rows now take the block by reference (PR feat(spec)!: a page action row's onSuccess is ActionSchema's own block, and its executor key refuses the 'navigation' alias #23164), so the rule does not carry over: the rows' executor key is open, and the action'stypeis closed.For this card's claim: decide it in the same contract choice as the
nameleg. A row'sonSuccessbeside a navigating executor is refused, or stated. ⛔ No second URL grammar.objectstack-fleet commentedon Oct 13, 2026
Triage: first grade,
bug·security·priority:p1·domain:spec·area:access·pm:blocked(findingremoved). The contract judges the executor the runner will runBlocked-by: #23103
Triage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-13T05:59Z. ⛔ Not a claim, ⛔ not a dispatch.security: it is the url-target family of ruling A+ ([finding] url-target doors ruling A+ did not name (page action rows, dashboard header actionUrl, onSuccess.navigate) still admit a target the console's runner refuses at the click #23077 and spec: a page action row'sonSuccessisz.unknown(), soonSuccess.navigate: 'javascript:…'on anaction:button/action:icon/action:grouporaction:menumember parses and validates clean — the fourth door of ruling A+'s url-target family #23103, both p1security). This is its fifth door, where the contract admits what the click refuses.type ?? actionType ?? name). When that resolves to the url navigator ('url'or'navigation'),targetis judged by the one url-target refusal (PR feat(spec)!: the page action rows' url target, a dashboard header action's actionUrl and onSuccess.navigate apply the shared url-target refusal #23101), whichever key spelled it.nameis not refused as an executor spelling. The runner declares that leg, and refusing it would break rows that use it for other handlers.6138882596):ActionSchema's existing rule ("onSuccessonly besideapi/script") is applied to the rows' effective executor too. A navigating executor withonSuccessis refused. This carries an existing rule to the rows; it is not a new one.urlandnavigation) with a refused scheme are found atproperties.target;actionType: 'url'control is unchanged;onSuccessis refused.onSuccessisz.unknown(), soonSuccess.navigate: 'javascript:…'on anaction:button/action:icon/action:grouporaction:menumember parses and validates clean — the fourth door of ruling A+'s url-target family #23103, in flight) is reworking the same rows' schema (onSuccessby reference). The unlock scan fires when spec: a page action row'sonSuccessisz.unknown(), soonSuccess.navigate: 'javascript:…'on anaction:button/action:icon/action:grouporaction:menumember parses and validates clean — the fourth door of ruling A+'s url-target family #23103 closes.packages/spec/src/ui/(the action-row shapes) and@objectstack/lint's props gate (domain:spec); rationale: judge what will run.objectstack-fleet commentedon Oct 13, 2026
Unlock scan:
pm:blocked→pm:queue, grade unchanged (priority:p1·security). #23103 landedTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-13T10:03Z. ⛔ Not a claim, ⛔ not a dispatch.onSuccessisz.unknown(), soonSuccess.navigate: 'javascript:…'on anaction:button/action:icon/action:grouporaction:menumember parses and validates clean — the fourth door of ruling A+'s url-target family #23103 closedcompleted(PR feat(spec)!: a page action row's onSuccess is ActionSchema's own block, and its executor key refuses the 'navigation' alias #23164, the rows'onSuccessby reference). The rows' schema this card changes is now stable onmain.6139317450):type ?? actionType ?? name) decides whentargetis judged;nameis not refused as an executor spelling;ActionSchema's "onSuccessonly besideapi/script" applies to the rows' effective executor.7 remaining items
objectstack-fleet commentedon Oct 13, 2026
✅ Contract review PASS; the ruled half, PR #23289 at
f1a31cee7, is ready with auto-merge armed (Part of #23168)domain:specseat 2 (#18549) ·os-justin· sessionsession_01BRQ9Mp1yH7R1az6MaUM18H· 2026-10-13T15:10Z · holder of claim6145383729. Thread-read:6148164722.Contract review on this head:
6148416179,CONTRACT_REVIEW_TIER, PASS. It judges right:row[executorKey] || row.name), which is the runner's resolution exactly,||included;urlandnavigation), applied on all four rows and nowhere else;nameleft unrefused, with the declared key outranking it;minorwithClause-②: no (narrowing).It found no partial
onSuccesshalf in the diff. Triage's fourth pin is not delivered by this head, by design.Pre-queue checks on
f1a31cee7:EXPECTED_SKIPSroster; 0 failures;mergeable: clean.check-governed-merges --pr 23289: not governed, 410 changed lines.Part of #23168and no closing keyword.Landed through the relay:
pr_ready+automerge_enable(run 38419644195).Next:
onSuccesshalf waits on triage's answer to the fork question (pm:retriage,6148164722). The PR that lands it carriesFixes #23168, together with the false-reason fix inActionSchema'sonSuccessmessage.actionType", while the refinement also covers an empty key.Generated by Claude Code
objectstack-fleet commentedon Oct 13, 2026
Landed: the ruled half, PR #23289 →
b529a5c23(Part of #23168). The card goes back topm:queuefor theonSuccesshalf, which triage has ruled Cdomain:specseat 2 (#18549) ·os-justin· sessionsession_01BRQ9Mp1yH7R1az6MaUM18H· 2026-10-13T15:40Z · holder of claim6145383729, released by this act. Thread-read:6148718282.b529a5c23, an ancestor oforigin/main(175e8d414). 6 files, +368 / −42, not governed.Part of #23168and carries no closing keyword. One issue closed in the merge window, docs(skills install): the end state after create-objectstack installs @objectstack/skills — one pinned spelling with --copy, one next.0 exception, and the 17→18 skills step in the upgrade guide (ruling A on #22649, second docs pass) #23275, and PR docs(skills): the end state after create-objectstack installs @objectstack/skills — one pinned --copy spelling, one next.0 exception, and the 17.x to 18 skills step (#23275) #23295 (Fixes #23275, another lane) closed it, not this PR. This card stays open.6148718282: it said theonSuccesshalf still waited on triage. Triage had already answered at 14:57Z (6148462268): option C,pm:retriageremoved.onSuccesshalf, under triage's C.onSuccessis refused beside the runner built-insurl,navigation,modal,flowandform, and beside a row with no effective executor; a host handler name stays open. Two items ride the same PR:ActionSchema'sonSuccessrefusal message, whose stated reason is false, and the describes' "noactionType" wording, which should also cover an empty key. That PR carriesFixes #23168.Release: session
session_01BRQ9Mp1yH7R1az6MaUM18H(domain:spec#2) · cause: the ruled half landed (Part of) and this session's three slots are full · to:pm:queue, unassigned. It is a p1securitycard, so it leads the lane's queue at the next pick; this seat re-claims it at its next free slot unless another seat of the lane takes it first. Resume-from: none (the half starts frommain).Generated by Claude Code
objectstack-fleet commentedon Oct 13, 2026
Claim: PM loop round 1, rolling slot (#23168, the
onSuccesshalf under triage's fork answer C,6148462268) · 2026-10-13T17:57ZSession:
session_01BRQ9Mp1yH7R1az6MaUM18HAccount:
os-justin(the seat's linked user asGET /useranswers it; the card's assignee from this act)Branch:
claude/issue-23168-onsuccess-row-executorWorktree:
objectstack-issue-23168-bDomain:
domain:specSeat:
domain:spec#2(seat post #18549)File surface (at
origin/main543b33deeor later, which carries the ruled halfb529a5c23; stop on breach and explain in the report):packages/spec/src/ui/component.zod.ts: the rows'onSuccessrefinement on the effective executor (effectiveRowExecutor, landed by PR feat(spec)!: a page action row's url target is judged on the executor the runner runs, its name leg included #23289);packages/spec/src/ui/action.zod.ts:ActionSchema'sonSuccessrefusal message, whose stated reason is false (triage: "corrected in the same PR");actionType" wording, which also covers an empty key;content/docs/references/ui/*.mdxthrough the generators, a step-18 semantic entry if the gate asks for one, and one.changeset/23168-*.md.Container & model:
S,mode:subagent,model: default tier(dispatch-gates --tier: no path-derived mandate). ACONTRACT_REVIEW_TIERreview by an isolated subagent precedes the queue.Clause-②: no (narrowing)
Responsibility: the page action rows' schema in
packages/spec/src/ui/component.zod.ts, which acceptsonSuccessbeside a built-in that cannot use it and beside a row with no executor |ActionSchemaalready refuses the same shape for actions, not for the rows | any author or AI writing a page action row (measured in the previous round: a navigating row withonSuccess.navigatenavigates twice)Thread-read: 6149320013
Serial constraints cleared (all open PRs' file lists read in this act; the lane's queue re-read in this act, with this card the one open p1 in it and no
pm:blockingcard):packages/spec/src/ui/**or the ui generated shards; the last change to these files is this card's own ruled half (b529a5c23).builtinValueJudged/carriesInterpolationToken) outlived every interpolation — a built-in config value carrying a{token}registers past its type, then fails every run #23260, in flight from this seat, work inconversions,data,migrations/entries/semantic/andautomation, disjoint from this surface; a semantic entry added here is a new file.This act moves the card
pm:queue→pm:dispatchedand assignsos-justin. The PR carriesFixes #23168.Generated by Claude Code
objectstack-fleet commentedon Oct 13, 2026
os-dev-report
{
"issue": 23168,
"status": "done",
"branch": "claude/issue-23168-onsuccess-row-executor",
"pr": "#23331",
"session": "session_01BRQ9Mp1yH7R1az6MaUM18H",
"premise_still_valid": true,
"summary": "The onSuccess half under triage's fork answer C ships in draft PR #23331 (line 1
Fixes #23168, line 2Clause-②: no (narrowing), assignee os-justin). A page action row'sonSuccess(action:button, action:icon, action:group / action:menu members) is now refused beside the runner built-ins url, navigation, modal, flow and form, and on a row with no effective executor, read once througheffectiveRowExecutor(runner||, empty key included); api, script and any host-handler name (e.g.run,name: 'clone_task') stay open. The rule is a sibling refinementrefuseRowOnSuccessBesideExecutorchained on all four rows, predicaterowOnSuccessRefusedbesideeffectiveRowExecutor, built-in table =ActionType.options+ROW_URL_NAVIGATOR_EXECUTORS(read from the contract, not re-spelled); the props gate reports it atproperties.onSuccess/properties.actions.N.onSuccesswith no lint change. ActionSchema's reach is unchanged (url/modal/flow/form) but its false reason is replaced: one refusal text for the action and the rows lives in a new non-barrel modulepackages/spec/src/ui/action-on-success-executor.ts, with a per-executor true reason and prescription. The describes' 'noactionType' wording now says 'no or an empty'. A new D3 entryui-page-action-row-on-success-beside-executor-refused, a minor BREAKING changeset with FROM -> TO, and gen:docs output ride along. Zone 2 measured: (1) the runner's builtinExecutors at pin 1fb1ced36243 (byte-identical at objectui main 72594f29) is exactly script/url/modal/flow/api/form/navigation, holds; (2) one resolution, holds; (3) the url-target judgment is a per-row factory, not one shared refinement object, so the onSuccess rule is a sibling factory chained identically on all four rows, no lint change, holds with that shape; (4) partly disproved: the console's modal does not hop 'a second time', because useActionModal resolves on close (saved form = success with the record), so the hop follows an undeclared result, and the console's flow handler answers success at the run's start (judgeFlowLaunch: paused screen and refused end both success:true), so the text states each executor's real behaviour rather than one blanket reason; (5) producers: zero in this repo and hotcrm 0f84c9c; in objectui exactly one red site (see objectui_red_sites), the run-handler test stays green, and the registry-inputs parity probes are unchanged (simulated). check-adr-0087-registration asked for the marker, so a NEW entry file was added.","objectui_red_sites": [
"packages/types/src/tests/held-public-block-arms-10872.test.ts:224 (objectui main 72594f29 and pin 1fb1ced36243):
judge({ type, properties: { onSuccess: { navigate: '/tasks' } } }).successasserted true for action:button and action:icon on both FACES (both reach ComponentPropsMap by reference via public-blocks.zod.ts). Under this PR it is refused atproperties.onSuccess(no executor, no name). Remedy, one line: give that rowactionType: 'api'and atarget.","Measured NOT red: action-button-icon-inputs-11168.test.tsx:474-490 (
actionType: 'run', host handler, open; not parsed through the spec); action-onSuccess-forward.test.tsx (type: 'api', not parsed through the spec); apps/console/src/tests/registry-inputs-spec-parity.test.ts (its object-arm and member probes all fail inside the onSuccess block first, so the row-level check never runs; verdicts unchanged, simulated against this branch's rows)."],
"tests": "Heavy runs via os-verify-lock.sh, verdicts are its VERDICT lines. At a2bfa8b (pre-merge; the merge of origin/main 404fc47 touched only plugin-auth + one changeset): spec build exit 0 (dropped-refinements ledger unmoved 759/234); check:generated 'All 14 generated artifacts are up to date' exit 0 (gen:docs regenerated component.mdx); spec full
vitest run --project local: 669 files, 20370 passed, 1 todo, VERDICT command-exit 0; spec typecheck exit 0 (test-typecheck held 52/246/135); lint full 138 files 6694 passed 5 skipped, lint typecheck exit 0; validate-component-props-row-on-success.test.ts 32 passed (16 before); spec row pins file component-action-row-on-success-executor.test.ts 117 tests (section 3 new: each refused built-in x4 rows, navigation, name leg, no executor, empty key, accepted api/script/run/name-leg/declared-key-outranks, ONE TEXT row==ActionSchema after subject, each with a negative control) plus action-on-success.test.ts, url-target-doors, effective-executor: 4 files 208 passed. Ablation at a2bfa8b via scripts/ablation-replace.mjs WRAP mode (trap restore): rowOnSuccessRefused body ->return executor === 'ablated-23168-onsuccess-row';, anchor 1->0, blob 42ffdc3eb2c1->b98144cf5270; spec src leg 52 failed / 65 passed (exactly section 3's refusal pins); spec rebuilt, ablation-dist-preflight marker present in 14 dist files exit 0; lint dist leg 8 failed / 24 passed (2 refusal pins x 4 rows); restore blob == HEAD 42ffdc3eb2c1, git diff HEAD empty; rebuild, preflight --absent: marker absent from all 232 dist files, tree clean, exit 0; lint pins 32/32 after restore. eslint narrowed at 592e1bd: population = the 7 TS files of the diff (.md/.mdx resolve to no config, --print-config undefined); --format json 7 files 0 errors 0 warnings; invariance: no type-aware linting (no parserOptions.project, eslint.config.mjs:327-329). Workspace build for the prerequisite gates: turbo run build 74/74 (55 cached), VERDICT command-exit 0.","gates": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths) at 592e1bd: 111 derived (the PM lead list's 86 plus the doc/docs families, check:generated, check:skill-examples, check:spec-changes, check:upgrade-guide, check:doc-security-posture and others the diff adds, chiefly via content/docs/references/ui/component.mdx). Exit codes recorded before any pipe. 110 run, every final exit 0. 6 first read PREREQUISITE NOT MET (exit 3) on an unbuilt workspace: lint check:doc-formula-expressions, lint check:doc-security-posture, spec check:skill-examples, check:docs-transcript-drift, check:dual-build-cjs-loads, check:lean-entry-closure; all 6 exit 0 after the workspace build at the same head. NOT MEASURED: pnpm check:type-check-debt, reason: killed by my own 900 s per-command timeout (exit 124) while --re-measure re-ran tsc over the DEBT ledger's one entry, the workspace root program (scripts/ and top-level configs); this diff touches nothing in that program, no tsconfig, typecheck script or ledger entry. Reconcile: 'Run reconciliation — 111 derived, 110 run, 1 NOT-MEASURED, 0 UNRUN.' and '✓ dispatch-gates --ran: 111 derived famil(ies) accounted for — 110 run, 1 NOT-MEASURED (0 DERIVED from a recorded exit 3, 1 a KILL code with a stated reason).' exit 0. check-adr-0087-registration: registered ui-page-action-row-on-success-beside-executor-refused (new here). check:spec-changes 126 converted / 442 migrated. check:objectui-pin-citations 55 asserting match 1fb1ced36. check:authorable-surface exit 0, check:api-surface exit 0 (new module not exported). check:nul-bytes exit 0; raw control-byte scan of the 9 touched files 0 lines.",
"line_budget": "Not applicable: no skills/** or governed path (.claude/, docs/adr/, AGENTS.md, CLAUDE.md untouched). Diff vs merge base 404fc47: 9 files, +482 / -56 = 538 changed lines.",
"files_changed": [
".changeset/23168-page-action-row-on-success-beside-executor.md",
"content/docs/references/ui/component.mdx (gen:docs)",
"packages/lint/src/validate-component-props-row-on-success.test.ts",
"packages/spec/src/migrations/entries/semantic/18.ui-page-action-row-on-success-beside-executor-refused.ts",
"packages/spec/src/ui/action-on-success-executor.ts (new, relative import only, not in the ui barrel)",
"packages/spec/src/ui/action-on-success.test.ts",
"packages/spec/src/ui/action.zod.ts",
"packages/spec/src/ui/component-action-row-on-success-executor.test.ts",
"packages/spec/src/ui/component.zod.ts"
],
"mcp_calls": "0",
"api_writes": "2 relay strokes, 3 executed REST writes. Stroke 1 (pr_create): POST /repos/objectstack-ai/objectstack/dispatches (204, request fw-20261013T201032Z-4ea776, run 38460734493 success), executing POST /repos/objectstack-ai/objectstack/pulls (draft, #23331) + POST /repos//issues/23331/assignees (os-justin); read-back 12295 bytes sent = 12295 stored, identical. Stroke 2 (comment): POST /repos/objectstack-ai/objectstack/dispatches executing POST /repos//issues/23168/comments (this report). Zero label writes: the diff publishes and carries a changeset, so skip-changeset does not apply, and the dispatch named no other label. git push (not REST): the empty branch probe, then 394b0ef, 861f567, a2bfa8b, 592e1bd. REST reads only otherwise (issue, comments, PR read-back).",
"deviations": [
"Commit trailers use AGENTS.md's model-free pair (
Claude-Session+Co-authored-by: Claude) instead of the harness reminder's model-namedCo-Authored-By, and the PR body ends in AGENTS.md's session-URL footer instead of the reminder's form; AGENTS.md takes precedence and the pre-push hook refuses a model identifier.","Scratchpad: I used
issue-23168/(andissue-23168/gates/), which the previous round of this same card also used; my files carry distinct names except the re-fetched issue.json / comments.json / install.log, and nothing of an in-flight run was overwritten (that round is closed).","A gate-rerun loop spelled
bash -c \"$cmd\"was blocked by the harness safety check; nothing ran. The same 6 commands then ran from a script file with each command spelled explicitly.","The workspace build waited 2 lock budgets (2 x 540 s, exit 99, slot kept) behind holder pid 4619 (issue-23259's suite chain), then acquired on the third attempt; it ran nohup'd with the waits done as foreground
tail --pid."],
"open_questions": [],
"out_of_scope_findings": [
"carrier: none (承接者:无) · noted in PR #23331 Acceptance notes, not filed ·
operation: 'update'rows: the runner readsoperationbefore any executor (ActionRunner.ts:1384-1388 at pin 1fb1ced36243), so a row withoperation: 'update'and no executor key runs its declarative write and then hops; ruling C refuses it as a no-executor row, and the prescribedactionType: 'script'changes nothing at run time (ActionSchema itself materialises type 'script' for such an action). Conversely ActionSchema refuses onSuccess besideoperation: 'update'on any type while the rows (operation is z.unknown()) never carried that rule. Observation, read from source, no producer. Dedupe words: page action row operation update onSuccess; declarative update row hop; row operation before executor.","carrier: the next PR to touch component.zod.ts's rows-wide 'VALUE posture' docblock · noted, not filed · that docblock still lists
onSuccessamong the z.unknown() keys, stale since the rows took ActionSchema's block by reference. Dedupe words: action rows value posture docblock onSuccess unknown."]
}
objectstack-fleet commentedon Oct 13, 2026
✅ ACCEPT: the
onSuccesshalf, PR #23331 at592e1bd8a(Fixes #23168). Next: the contract-tier review, then the queuedomain:specseat 2 (#18549) ·os-justin· sessionsession_01BRQ9Mp1yH7R1az6MaUM18H· 2026-10-13T20:17Z · holder of claim6151841916. Report:6154277169. Thread-read: the report.Read on GitHub:
main. Line 1 isFixes #23168, line 2Clause-②: no (narrowing). 9 files, +482 / −56 = 538 lines.check-governed-merges --pr 23331: not governed. CI was still running at this read.onSuccessis refused besideurl,navigation,modal,flowandform, and on a row with no effective executor, read once througheffectiveRowExecutor(the runner's||, the empty key included).api,scriptand every host-handler name stay open. The built-in table is read from the contract (ActionTypeplus the url navigators), not re-spelled. The rule is a sibling refinement chained on all four rows, reported by the props gate with no lint change.ActionSchema's false reason is replaced by one refusal text shared with the rows, with a true reason per executor;ActionSchema's reach is unchanged. The describes say "no or an empty" executor key.Measured, and one assumption disproved (a good outcome): the console's
modaldoes not hop a second time (it resolves on close with the saved record), and theflowhandler answers success at the run's start; the refusal text therefore states each executor's real behaviour instead of one blanket reason. The runner's built-in table at the pin1fb1ced36243(byte-identical at objectui72594f29) is exactly the seven the rule reads.Evidence accepted: gates: 111 derived, 110 run, all exit 0; 1 NOT MEASURED (
check:type-check-debt, killed by the dev's own 900 s timeout while re-measuring the root program the diff does not touch), reconciled by--ran; CI runs it. Suites: spec (669 files, 20,370 tests), lint (6,694), the row pins (117 tests). Ablation ofrowOnSuccessRefusedwent red on exactly section 3's refusal pins at the source leg (52) and the lint dist leg (8), and was restored (blob equal to HEAD). A new D3 entry is registered, with a FROM → TO changeset.Report items, one line each:
held-public-block-arms-10872.test.ts:224(an executor-less row withonSuccessasserted accepted), the one red site: filed types(test):held-public-block-arms-10872.test.ts:224asserts an executor-less page action row withonSuccessis accepted — refused once the spec release carrying objectstack#23331 is installed; give the rowactionType: 'api'objectui#12338,pm:on-holduntil a spec release carrying this PR is installable (cross-repo rule 3, as triage asked).operation: 'update'and no executor runs its declarative write and then hops, so its prescription (actionType: 'script') changes nothing at run time; no producer measured, andActionSchemarefuses the same shape: Acceptance notes.onSuccessamong thez.unknown()keys: Acceptance notes (rides the next PR that edits that docblock).Generated by Claude Code
objectstack-fleet commentedon Oct 13, 2026
✅ Contract review PASS; the
onSuccesshalf, PR #23331 at592e1bd8a, is ready with auto-merge armed (Fixes #23168)domain:specseat 2 (#18549) ·os-justin· sessionsession_01BRQ9Mp1yH7R1az6MaUM18H· 2026-10-13T20:40Z · holder of claim6151841916. Thread-read:6154370990.6154546946,CONTRACT_REVIEW_TIER, PASS. The refused set is triage's C exactly (the five built-ins and no effective executor, read from the contract's vocabulary, equal to the runner's table at the pin); the shared refusal text is true per executor;ActionSchema's reach is unchanged; nothing published widens;minorwith BREAKING andClause-②: no (narrowing)are right.operationasz.unknown(), sooperation: 'update'plusonSuccessparses on a row whileActionSchemarefuses it: filed spec(ui): a page action row withoperation: 'update'andonSuccessparses, whileActionSchemarefuses the same shape — the rows typeoperationasz.unknown()and never carried the rule #23332. No change to this PR.592e1bd8a: 36 success and 5 skipped, every skip on the expected-skips roster, 0 failures;mergeable_stateclean;check-governed-merges --pr 23331: not governed, 538 changed lines.pr_ready+automerge_enable.Next: confirm MERGED, the mis-close scan (this card closes; nothing else should), and the landing record; objectstack-ai/objectui#12338 waits on the release.
Generated by Claude Code
objectstack-fleet commentedon Oct 13, 2026
Landed: the
onSuccesshalf, PR #23331 →1623fafc3(Fixes #23168). The card closedcompletedon the mergedomain:specseat 2 (#18549) ·os-justin· sessionsession_01BRQ9Mp1yH7R1az6MaUM18H· 2026-10-13T21:17Z · holder of claim6151841916. Thread-read:6154776206.1623fafc3, an ancestor oforigin/main. 9 files, +482 / −56, not governed. Contract review PASS6154546946.b529a5c23) judges a row's urltargeton the executor the runner runs; this half refusesonSuccessbesideurl,navigation,modal,flowandformand on a row with no effective executor, under triage's C, with a true reason per executor shared withActionSchema.Fixes #23168. Two issues closed in the merge window: this card, and docs(permissions): authentication.mdx "How enforcement works" says only the REST server and the runtime dispatcher block an ADR-0069-held session — the raw-host route families gate too since PR #23200 and PR #23285 #23288, which PR docs(permissions): the "How enforcement works" callout names every transport seam that consults the ADR-0069 gate #23333 (Fixes #23288,domain:devx) closed in the same queue batch; this PR's body does not name it.pm:dispatchedis removed in this act.held-public-block-arms-10872.test.ts:224asserts an executor-less page action row withonSuccessis accepted — refused once the spec release carrying objectstack#23331 is installed; give the rowactionType: 'api'objectui#12338 (the one objectui test that turns red at the next spec bump;pm:on-holduntil the release is installable), spec(ui): a page action row withoperation: 'update'andonSuccessparses, whileActionSchemarefuses the same shape — the rows typeoperationasz.unknown()and never carried the rule #23332 (the rows'operation: 'update'posture againstActionSchema).mainpublishes nothing; the release is the Version Packages PR, a human act.Generated by Claude Code