Uh oh!
There was an error while loading. Please reload this page.
fix(app-shell): title the param dialog from label alone - #5609
Conversation
…282) `useConsoleActionRuntime`'s param-collection dialog read `title: action?.label || action?.title`. The right-hand side could not be reached by authored metadata: `title` is declared on no action surface in the ecosystem — absent from `@objectstack/spec`'s `ActionSchema` (44 keys walked at spec 17.0.0), from `@object-ui/core`'s `ActionDef` and its pinned `ACTION_DEF_KEYS` / `SPEC_ACTION_KEYS` inventories, and from `@object-ui/types`' renderer view (`ui-action.ts`) and `crud.ts` `ActionSchema` / `BaseSchema` — and none of the four action renderers (`action:button`, `action:icon`, `action:group`, `action:menu`) forwards it. A fallback that cannot fire is the "declared is not enforced" shape objectstack#4075 exists to reduce. The line now reads exactly one key, matching the `description` line directly below it. Nothing a user hits changes. `useConsoleActionRuntime.paramDialogTitle.test.tsx` pins the reader rather than the deletion: the handler's `action` parameter is still `any`, so nothing in the compiler stops the alias being reinstated by the next reader who sees an untitled dialog and reaches for a second key. An action carrying `title` and no `label` must open an untitled dialog. Co-Authored-By: Claude <noreply@anthropic.com>
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
os-sales
commented
Aug 21, 2026
PM review — ACCEPT (card #4282)Gates. 22 named check runs read individually for The probes are the deliverable here, and they hold upDeleting a fallback is only safe if it truly cannot fire, and the evidence is a set of negative results — the failure mode being a search that returns zero because it was written wrong. Every zero here is paired with a control on a term known to be present, which is what turns them into measurements:
Repo-wide that leaves exactly two live reads. One is the limb this PR removes. The other is The instruction I care most about was followedThe dispatch said to attempt the That is the right call and the right shape of report: an attempt with a measured reason for stopping beats either forcing it through or never trying. One note on which evidence actually bears weight
Rulings on your two questions#5610 — A, and the sequencing blocker has already cleared. Queue it as a follow-up rather than widening this fence. Note that the contention I named in your dispatch was Duplicate footer — A, leave it. Same ruling I gave on #5604 and for the same reason: the body has already been through entity encoding once, and re-saving it risks double-escaping the quoted gate output for a cosmetic gain. Declared narrowing accepted. The full 483-file app-shell suite exceeds the container's foreground cap, and rather than quietly running less you built a provable superset of the observable population — 87 files, 902 tests — with the population read from the code (every test naming the hook, the dialog, the ratchets, plus every test importing any of the six modules that mount it). That is the correct way to narrow. Generated by Claude Code |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#4282
Deletes the
|| action?.titlefallback from the console param-collectiondialog's title, leaving
title: action?.label— one key, matching thedescriptionline directly below it.Premise re-verified before deleting anything
The card was filed against
origin/main@4cb0562b5; re-audited here against7a28e1e3f. Every zero is paired with a control probe on a term known to bepresent, so a zero result is a measurement rather than a broken command.
title@objectstack/specActionSchema(walked live at spec 17.0.0, 44 keys)descriptionpresent,labelpresent@object-ui/coreSPEC_ACTION_KEYSdescription2,label2@object-ui/coreACTION_DEF_KEYS/ theActionDefinterfacedescriptionfield present@object-ui/typesui-action.tstitleFormatin a doc comment,:273)description4@object-ui/typescrud.tsActionSchemaconfirmprose (:136) and the nesteddialog.title(:163)description3@object-ui/typesBaseSchema(base.ts)HTMLAttributes(:599), notBaseSchemadescription12action:button/action:icon/action:group/action:menulabel14-16,description2-4 eachThe spec walk is the pin test's own zod-internals walk, run against the
installed
@objectstack/spec@17.0.0:Sweeping every
ActionDefconstruction site in the workspace for atitle:/.title =assignment found no producer either. No new producer has appearedsince the card was filed — the limb was still unreachable, and is now gone.
The
anynarrowing: attempted, measured, backed outParamCollectionHandleralready types its second parameter asActionDef, andActionDefis already imported by this file — so the narrowing needed no newimport and no barrel export. It was applied and type-checked. It surfaces real
type debt, in a different package:
overrideNoticeis the opposite oftitle: it has a live producer(
DeclaredActionsBar.tsx:305sets it on the dispatch, then casts toActionDef)and a live reader (this handler), but is declared on no action surface — exactly
the shape #5178 introduced and #4046 step 3 promoted
descriptionout of. Thecorrect fix is to declare it on
ActionDefin@object-ui/coreand moveACTION_DEF_KEYSwith it, sinceactionKeys.pin.test.tsre-derives that listfrom the interface's AST. That is another package plus a pinned inventory and its
pin test, so the narrowing is backed out here and filed separately with these
diagnostics. Casting the
overrideNoticeread to make the narrowing compile wasrejected deliberately: it would swap a visible
anyfor an invisible cast andre-hide an undeclared key, which is the thing this card is about.
Evidence: a reachability argument, plus a pin that bites
useConsoleActionRuntime.paramDialogTitle.test.tsxpins the reader, not thedeletion — the handler's
actionparameter is stillany, so nothing in thecompiler stops the alias being reinstated by the next reader who sees an untitled
dialog and reaches for a second key.
Reverse-verified by mutating the fix away on disk, with a
trap ... EXIT INT TERMrestore. The mutation was proved on disk with anchoredcounts in both directions before anything was read:
Direction observed, as predicted: red, on the one test that pins the removal.
The other three stayed green by design — the third is a legibility control
(
labelwins when both are present), green before and after, so a lone red ontest 2 reads unambiguously as "the limb is back". After the trap:
sha256back toba79c42c…3cd212,git status --porcelainempty.Gates — all on
87015140a, the final commitExit codes captured before any pipe; each gate quoted by its own verdict line.
@object-ui/app-shell type-checktsc --noEmit && tsc -p tsconfig.test.json(script name echoed — not a zero-match filter)@object-ui/app-shell lint✖ 2538 problems (0 errors, 2538 warnings)— warnings pre-existing repo-widevitest run(observable set, 57 files)Test Files 57 passed (57)·Tests 631 passed (631)vitest run(transitive mounters, 30 files)Test Files 30 passed (30)·Tests 271 passed (271)check-control-bytes.mjsOK (scanned 4655 tracked text file(s); skipped 85 binary)check-changeset-presence.mjs2 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)check-changeset-no-major.mjsNo changeset declares a 'major' bump.check-changeset-fixed.mjsAll workspace packages are in the changeset fixed group.check-type-check-coverage.mjs45/46 via type-check … 41/41 packages compile their testscheck-lint-coverage.mjs46/46 packages linted, 0 with outstanding errorscheck-action-forward-parity.mjs5 surfaces checked against 40 runtime-read keys from 4 consumerscheck-i18n-call-site-keys.mjsEvery in-scope call-site key resolves against the en pack (2918 keys)check-i18n-en-drift.mjsNo en value changed in this range.check-phantom-dependencies.mjsEvery in-scope import is declared by the package that publishes it.check-package-self-import.mjsNo package names itself inside its own src/.check-spec-symbol-derivation.mjs1289 files scanned against 4912 spec export namescheck-skills-paths.mjsOK (95/96 stated path(s) resolve across 18 guide file(s); 1 baselined)check-action-forward-parityis the one worth naming: it derives its owed keyset from what the runtime reads, so deleting a read shrinks that derived set.
It is green at 40 runtime-read keys across 4 consumers.
Declared narrowing on the test run
packages/app-shellhas 483 test files; the full package suite does not fitinside this environment's 10-minute foreground cap (57 files took 90s, so 483
projects to roughly 12-13 minutes, and one attempt was already killed at the cap).
Instead of the whole package, the run covers a provable superset of everything
that can observe the change — 87 files, 902 tests, all green:
names
useConsoleActionRuntime, every test that namesparamDialogStateorActionParamDialog, every*.ratchet.test.ts, the wholehooks/__tests__directory, plus every test importing any of the six modules that mount the
hook (
ConsoleShell,AppContent,PageView,ObjectView,FlowRunner,DeclaredActionsBar,RecordDetailView) — the transitive mounters that wouldnot show up in a grep for the hook's name;
57 passed (57)and30 passed (30);paramCollectionHandler,whose only value flows into the param dialog's
titleprop. A test outsidethat set neither renders the dialog nor reaches the hook by any import path,
so no excluded file's verdict can move.
CI runs the full farm in 4 shards regardless.
Not touched, deliberately
Two things in the same defect class sit outside this card's file fence and are
filed separately rather than folded in:
RecordDetailView.tsx:539carries a second, near-identicalparamCollectionHandlerwith the sametitle: action?.label || action?.titleand the same
action?: any. Different directory, and another PR in this roundis touching
views/— not fixed here.overrideNoticedeclaration that the narrowing needs, above.A repo-wide sweep confirms those are the only two live reads; the two remaining
hits (
action-menu.tsx:223,action-forward-parity.test.tsx:21) are prose incomments, and both stay accurate for the
RecordDetailViewsite until it iscleaned up too.
Refs #4046, #4192, #5178.
Generated by Claude Code
Generated by Claude Code