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
refactor(actions): navigation becomes a declared alias of url, sharing one navigator (#2944) (#2994)
The last open item of #2944. `ActionRunner` dispatched a seventh action type,
`navigation`, that `@objectstack/spec`'s `ActionType` does not contain. The issue
asked to promote it upstream or delete the case. Neither, as stated.
Promoting is wrong: the spec already names this operation — `type: 'url'`, with
`openIn` for the new-tab choice — so a seventh type would put a second spec name
on one operation, which is the failure this audit exists to describe, committed
one level up. Deleting is worse because it is silent: `{ type: 'navigation',
to: … }` is authored today (`element:button` CTAs), and without the case the
action falls through to `executeActionSchema`, which returns `{ success: true }`
— a green toast that navigates nowhere (#2960's trap).
The alias had already drifted, which is the actual finding. `executeNavigation`
was quietly the weaker of two implementations of one concept: no `${param.X}`
interpolation, `openIn` ignored, no `/api/…` full-page short-circuit. So
`{ type: 'navigation', to: '/x?p=${param.p}' }` shipped the literal
`${param.p}` while the identical `url` action resolved it.
So it stays and stops being dialect. `ObjectUiLocalActionType` /
`OBJECTUI_LOCAL_ACTION_TYPES` declare it as objectui's own alias of `url` — the
treatment #2985 gave the page-visualization names — and both names now route
through one `navigateTo`. `url` gains `replace` pass-through, the one modifier
only the alias carried; `replace` is omitted from the `NavigationHandler`
options when unset, so hosts see the shape they already saw.
The guard is structural rather than another assertion: built-in dispatch is a
table typed `Record<RunnableActionType, …>` instead of a `switch`, so an
`ActionType` the spec ADDS stops compiling until an executor exists for it —
#2942's "validates at save, renders nothing at run time" class, made a build
error for actions. `spec-derived-unions.test.ts` additionally asserts
`navigation` is absent from the spec enum, so the day it is adopted upstream the
test fails and names the alias to retire.
Also covers the `/api/` short-circuit and `apiBase` promotion, which had none —
the project runs in a node env with no `window`, which is why — and corrects the
audit doc's Direction B row, which still prescribed promoting.
Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: docs/audits/2901-spec-enum-renderer-coverage.md
+32-3Lines changed: 32 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,7 +2,7 @@
2
2
3
3
Audited against `@objectstack/spec@16.0.0-rc.0` and `objectui@cd09a7b90`.
4
4
**Re-validated against `17.0.0-rc.0`** — see the addendum at the end, which also
5
-
carries three corrections. Read it before acting on any row below.
5
+
carries four corrections. Read it before acting on any row below.
6
6
7
7
Every row below was confirmed by reading the dispatch code. Nothing was exercised at
8
8
runtime, so symptoms are read off the code path, not observed. Rows the auditors could
@@ -165,7 +165,7 @@ value corruption rather than disclosure.
165
165
166
166
| Name | Location | Call |
167
167
|---|---|---|
168
-
|`navigation` action type |[ActionRunner.ts:539](../../packages/core/src/actions/ActionRunner.ts)|Fully implemented (`executeNavigation`). **Promote** — it is a real capability the spec doesn't name. |
168
+
|`navigation` action type |[ActionRunner.ts](../../packages/core/src/actions/ActionRunner.ts)|~~Promote~~ — **resolved as a declared alias instead.** See the correction below. |
169
169
|`combo` chart type |[normalizeChartSchema.ts:63](../../packages/plugin-charts/src/normalizeChartSchema.ts)| Drawn at `AdvancedChartImpl.tsx:819`. Promote or delete. |
170
170
|`system` theme mode | ThemeProvider ×2 | Delete — the spec name is `auto`, and `react/ThemeContext.tsx` already gets it right. |
171
171
|`scale-fade` preset |[useAnimation.ts:71](../../packages/react/src/hooks/useAnimation.ts)| Delete with the hyphen/underscore fix. |
@@ -358,6 +358,33 @@ On a list whose only narrowing is that filter, the user sees rows they had asked
358
358
to exclude. It is not a permission bypass — row-scoping `$and`-composes as a
359
359
separate arm and survives — but it ranks with Tier 1, not Tier 2.
360
360
361
+
## Correction 4 — "promote `navigation` upstream" was the wrong call
362
+
363
+
Direction B told the reader to promote the `navigation` action type into the spec,
364
+
on the grounds that it is "a real capability the spec doesn't name". The capability
365
+
is real; the framing was not. The spec already names it — `type: 'url'`, with
366
+
`openIn` for the new-tab choice — so promoting `navigation` would have put a second
367
+
spec name on one operation. That is the failure this audit exists to describe,
368
+
committed one level up.
369
+
370
+
The row *also* under-read the cost of the stated alternative. Deleting the case
371
+
does not surface an error: the action falls through to `executeActionSchema`, which
372
+
returns `{ success: true }`, so the user gets a success toast and no navigation —
373
+
#2960's trap, reached by way of a cleanup.
374
+
375
+
What was missing from the row is that the alias had **already drifted**, which is
376
+
the ordinary outcome of two implementations of one concept. `executeNavigation` did
377
+
no `${param.X}` interpolation, ignored `openIn`, and skipped the `/api/…`
378
+
full-page short-circuit — so `{ type: 'navigation', to: '/x?p=${param.p}' }`
379
+
shipped the literal `${param.p}` while the identical `url` action resolved it.
380
+
381
+
Resolved by declaring it (`ObjectUiLocalActionType`) and routing both names
382
+
through one navigator — the same treatment #2985 gave the page-visualization names.
383
+
Generalizing: a "renderer-local dialect" row needs a third option next to promote
384
+
and delete — **alias to the spec name it duplicates** — and picking between the
385
+
three starts with asking whether the spec really lacks the concept or just spells
386
+
it differently.
387
+
361
388
## Status of the items above
362
389
363
390
| Item | Status |
@@ -366,7 +393,9 @@ separate arm and survives — but it ranks with Tier 1, not Tier 2.
|`DensityModeSchema` inert | Resolved upstream (removed in spec 17) |
368
395
| Packages lacking the `@objectstack/spec` devDep |**6** — `plugin-list` and `data-objectstack` gained it in #2974; `plugin-charts`, `plugin-dashboard`, `plugin-report`, `components`, `mobile` and `fields` remain. (The body above says 6 because it counted a 13-package "needs a guard" list that omitted `fields`; adding `fields` and removing the two now fixed leaves 6 either way.) |
0 commit comments