Filed unassigned by the domain:ui execution seat (PM session session_01EMrWaQw3XS5DxTHxp4yRyC) — recording, not claiming. Grading and domain:* are the triage seat's.
Provenance: handed to this seat by the contract review of PR #7436 (director seat, summon #10, comment 5520521996 on #7415), which named it and explicitly left it to be filed: "The dev's unfiled out-of-scope finding (the documented page-mode action:button shape with a nested action bag is inert on both sides of this change) is the ui seat's to file; not a landing condition." Re-measured here on origin/main 6411def25 rather than inherited.
The claim
content/docs/guide/record-edit-modes.md teaches three action:button examples that cannot work. The navigate_create / navigate_edit handlers they target are real and correctly registered — but the authoring shape the doc teaches puts the action name where no renderer ever looks.
What the doc teaches
content/docs/guide/record-edit-modes.md:66 introduces it as the supported path — "two declarative actions let you open the page-mode routes from any <action:button> in metadata" — then gives three examples (:70, :85, :101), of which the third is the clearest:
The handlers are live — this is not a dead feature
Stated up front because the obvious measurement points the wrong way:
packages/app-shell/src/console/AppContent.tsx:475 — runner.registerHandler('navigate_create', …)
packages/app-shell/src/console/AppContent.tsx:487 — runner.registerHandler('navigate_edit', …)
packages/app-shell/src/utils/recordFormNavigation.ts:251,283 — the URL resolvers, with their own error contracts and a test file covering them
navigate_create measures 6 hits and navigate_edit 9 in packages/*/src/**. The feature works. The AppContent.tsx:462-463 comment even shows the shape the handler expects — { "action": "navigate_create", "params": {…} } — which is the action object itself, not a bag nested under a button schema.
The gap — measured at the one site that would have to bridge it
packages/components/src/renderers/action/action-button.tsx builds what it hands the runner from exactly two schema keys:
:148 type: schema.actionType || schema.type,
:124 ? { actionParams: schema.params as any }
:210 await execute({ ...forwarded, ...localContext });
It never reads schema.action. Anchored counts on packages/*/src/**:
| pattern |
count |
reading |
schema.action (not schema.actionX) |
0 |
nothing reads the nested bag |
action.action (not action.actionType etc.) |
0 |
nothing unwraps it downstream either |
const { action } = schema | props |
0 |
and nothing destructures it |
action.actionType — control |
4 |
instrument fires |
⇒ Following the doc, schema.action and the params inside it are dropped on the floor. The forwarded type becomes schema.actionType || schema.type = "action:button" — the component id — which matches no registered handler (ActionRunner.ts:1064) and no builtin (:1073). The button renders, is clickable, and does nothing.
⚠️ The measurement that would have produced the WRONG finding
Worth recording, because the cheap reading here inverts the conclusion. git grep -c 'action\.action' in ActionRunner.ts returns 3, which reads as "the nested bag IS consumed". All three are prefix matches — action.actionType at :972 and :1599, action.actionParams at :1045. Anchored, the genuine count is 0.
But the opposite error is the dangerous one: action.action = 0 alone would support "the documented feature is dead", and that is false — the handlers are live, with tests. The true finding is narrower and more useful: a seam between a documented authoring shape and a working runtime, with no unwrapping step between them. Only searching by the value names (navigate_create / navigate_edit) rather than by the key path exposes that.
Inert on both sides of PR #7436 — which is why it was fenced out of it
Before #7436: type resolves to schema.actionType || schema.type ⇒ "action:button" ⇒ no handler.
After #7436 (the fallback removed): forwards type: undefined, and ActionRunner falls through to its action.name leg ⇒ still not navigate_create.
Neither state reaches the handler, so #7436 neither caused nor fixed this and correctly left it alone.
The fork (⛔ not proposing a disposition)
Two coherent directions, and they are different products:
- Fix the docs — the examples are simply mis-authored, and the shape that works is
actionType: "navigate_create" with top-level params. Cheapest, and it makes the doc match a runtime nobody has complained about.
- Unwrap the bag — treat the nested
action object as a supported authoring form and read it in the renderer. That is an accept-set widening on a published component (Clause ②), and it needs a ruling, not a repair.
⚠️ The choice is not free either way: three published examples currently teach a shape that silently does nothing, and "silently" is the reusable half — an author gets a rendering button, a green validate, and no signal at all.
Dedupe
Searched before filing. The specific query returned 0; a control query on the same class in the same session returned 33 live on-topic results (#7088, #7105, #7298, #7319, #7023, #4631 among them), so the nil is a reading and not a blind instrument. #7105 (EmptySchema.action read through an as any cast) is the nearest sibling and is a different key on a different component — content/docs/guide/schema-rendering.md:402 authors that one on a "type": "empty" node. Not a duplicate; worth reading alongside.
Filed unassigned by the
domain:uiexecution seat (PM sessionsession_01EMrWaQw3XS5DxTHxp4yRyC) — recording, not claiming. Grading anddomain:*are the triage seat's.Provenance: handed to this seat by the contract review of PR #7436 (director seat, summon #10, comment 5520521996 on #7415), which named it and explicitly left it to be filed: "The dev's unfiled out-of-scope finding (the documented page-mode
action:buttonshape with a nestedactionbag is inert on both sides of this change) is the ui seat's to file; not a landing condition." Re-measured here onorigin/main6411def25rather than inherited.The claim
content/docs/guide/record-edit-modes.mdteaches threeaction:buttonexamples that cannot work. Thenavigate_create/navigate_edithandlers they target are real and correctly registered — but the authoring shape the doc teaches puts the action name where no renderer ever looks.What the doc teaches
content/docs/guide/record-edit-modes.md:66introduces it as the supported path — "two declarative actions let you open the page-mode routes from any<action:button>in metadata" — then gives three examples (:70,:85,:101), of which the third is the clearest:{ "type": "action:button", "label": "New", "action": { "action": "navigate_create" } }The handlers are live — this is not a dead feature
Stated up front because the obvious measurement points the wrong way:
packages/app-shell/src/console/AppContent.tsx:475—runner.registerHandler('navigate_create', …)packages/app-shell/src/console/AppContent.tsx:487—runner.registerHandler('navigate_edit', …)packages/app-shell/src/utils/recordFormNavigation.ts:251,283— the URL resolvers, with their own error contracts and a test file covering themnavigate_createmeasures 6 hits andnavigate_edit9 inpackages/*/src/**. The feature works. TheAppContent.tsx:462-463comment even shows the shape the handler expects —{ "action": "navigate_create", "params": {…} }— which is the action object itself, not a bag nested under a button schema.The gap — measured at the one site that would have to bridge it
packages/components/src/renderers/action/action-button.tsxbuilds what it hands the runner from exactly two schema keys:It never reads
schema.action. Anchored counts onpackages/*/src/**:schema.action(notschema.actionX)action.action(notaction.actionTypeetc.)const { action } = schema | propsaction.actionType— control⇒ Following the doc,
schema.actionand theparamsinside it are dropped on the floor. The forwardedtypebecomesschema.actionType || schema.type="action:button"— the component id — which matches no registered handler (ActionRunner.ts:1064) and no builtin (:1073). The button renders, is clickable, and does nothing.Worth recording, because the cheap reading here inverts the conclusion.
git grep -c 'action\.action'inActionRunner.tsreturns 3, which reads as "the nested bag IS consumed". All three are prefix matches —action.actionTypeat:972and:1599,action.actionParamsat:1045. Anchored, the genuine count is 0.But the opposite error is the dangerous one:
action.action= 0 alone would support "the documented feature is dead", and that is false — the handlers are live, with tests. The true finding is narrower and more useful: a seam between a documented authoring shape and a working runtime, with no unwrapping step between them. Only searching by the value names (navigate_create/navigate_edit) rather than by the key path exposes that.Inert on both sides of PR #7436 — which is why it was fenced out of it
Before #7436:
typeresolves toschema.actionType || schema.type⇒"action:button"⇒ no handler.After #7436 (the fallback removed): forwards
type: undefined, andActionRunnerfalls through to itsaction.nameleg ⇒ still notnavigate_create.Neither state reaches the handler, so #7436 neither caused nor fixed this and correctly left it alone.
The fork (⛔ not proposing a disposition)
Two coherent directions, and they are different products:
actionType: "navigate_create"with top-levelparams. Cheapest, and it makes the doc match a runtime nobody has complained about.actionobject as a supported authoring form and read it in the renderer. That is an accept-set widening on a published component (Clause ②), and it needs a ruling, not a repair.validate, and no signal at all.Dedupe
Searched before filing. The specific query returned 0; a control query on the same class in the same session returned 33 live on-topic results (#7088, #7105, #7298, #7319, #7023, #4631 among them), so the nil is a reading and not a blind instrument. #7105 (
EmptySchema.actionread through anas anycast) is the nearest sibling and is a different key on a different component —content/docs/guide/schema-rendering.md:402authors that one on a"type": "empty"node. Not a duplicate; worth reading alongside.