Uh oh!
There was an error while loading. Please reload this page.
fix(app-shell): title RecordDetailView's param dialog from label alone - #5618
Conversation
Deletes the `|| action?.title` fallback from the record detail view's param-collection dialog title, leaving `title: action?.label` — one key, matching the `description` line directly below it. This is the second copy of the limb objectui#4282 removed from `useConsoleActionRuntime`. `RecordDetailView` builds its own action runtime rather than routing through that hook, so the two near-identical `paramCollectionHandler`s drifted as a pair and the first fix could not reach this site. `title` is declared on no action surface: absent from `@objectstack/spec`'s `ActionSchema` (44 keys walked at spec 17.0.0), from `ActionDef` and its pinned `ACTION_DEF_KEYS` / `SPEC_ACTION_KEYS`, and from `@object-ui/types`' `ui-action.ts`, `crud.ts` `ActionSchema` and `BaseSchema`; forwarded by none of the four action renderers. So the right-hand side of that `||` could not fire from authored metadata. `RecordDetailView.paramDialogTitle.test.tsx` pins the reader rather than the deletion — the handler's `action` parameter is `any`, so nothing in the compiler stops the alias being reinstated. Also refreshes the two prose mentions that go stale with the limb: `action-menu.tsx` and `action-forward-parity.test.tsx` both quoted the expression verbatim, and the latter cited long-stale line numbers; both now cite the symbol instead of a line number. 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 #5610)Gates. 22 named check runs read individually for You corrected the PR this one mirrors, and one of those corrections is a miss in my review#5609's That table went out under my ACCEPT. The rule this seat runs on is "every zero paired with a control", and I checked that a control existed without checking it was scoped to the same thing as the probe. A control that cannot fail is decoration. The conclusion survives — The Reporting both rather than silently reproducing the sibling's table is exactly right. A mirror card is the one place where inherited evidence is most likely to be copied unexamined. The gate that shrank, visibly
Verified, not assumed — including where the card turned out to be rightI told you the card's The producer sweep is thorough in the way that matters for a deletion: spec Both prose sites, and a durable fix for themBoth were stale, both refreshed — and AblationCommitted the fix first so the restore had a real recovery point; mutation proven on disk in both directions with anchored counts and sha256 before reading any result; red on exactly the one test pinning the removal, with the three green ones named as by-design rather than counted as evidence — including the legibility control that is green on both sides because Narrowing is a superset read from the code across four named categories including transitive mounters a name-grep would miss — 87 files, 928 tests — with The Generated by Claude Code |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#5610
Deletes the
|| action?.titlefallback fromRecordDetailView's param-collectiondialog title, leaving
title: action?.label— one key, matching thedescriptionline directly below it.
This is the second copy of the limb #5609 removed from
useConsoleActionRuntime.RecordDetailViewbuilds its own action runtime ratherthan routing through that hook, so the two near-identical
paramCollectionHandlersdrifted as a pair and the first fix could not reach this site.
Premise re-verified before deleting anything, on current
origin/mainThe card cited
:539and:500. Both are still accurate at6606337e3— quotedat the numbers actually found:
:500—const paramCollectionHandler = useCallback((params: ActionParamDef[], action?: any) => {:539—title: action?.label || action?.title,Every zero is paired with a control probe on a term known present, so a zero is a
measurement rather than a broken command.
title@objectstack/specActionSchema(walked live at spec 17.0.0, 44 keys)descriptionpresent,labelpresent@object-ui/coreactionKeys.ts(ACTION_DEF_KEYS/SPEC_ACTION_KEYS)'description'2,'label'2@object-ui/coreActionDefinterface (ActionRunner.ts:112)description1,label1@object-ui/typesui-action.tstitleFormat,:273)description4,label10@object-ui/typescrud.tsActionSchema(body88..262)confirmprose (:136) and the nesteddialog.title(:161-163)label2@object-ui/typesBaseSchema(base.tsbody69..333)description5,label17base.ts:599title?: stringHTMLAttributes(:595), notBaseSchemaaction:button/action:icon/action:group/action:menulabel13-14 each,description1-3 eachThe spec walk is the pin test's own zod-internals walk, run against the installed
@objectstack/spec@17.0.0:Two probe corrections worth naming, since they change how the table reads:
crud.ts'sActionSchemahas nodescriptioneither (0 in the interfacebody). fix(app-shell): title the param dialog from
labelalone #5609's row used a file-scopeddescriptioncount as its control againstan interface-scoped probe. Scoped consistently,
label(2) is the only validin-scope control there — a control that is itself absent cannot falsify anything.
titlesubstrings inside@object-ui/core(ActionRunner.ts:129,actionKeys.ts:103) are not mentions of this key: they are the words"subtitle" and "titled itself" in prose about
description. Both stillread correctly and are left alone.
Producer sweep — no new producer has appeared since #5609 landed. Zero
.title =assignments on any action-shaped object;DeclaredActionsBar(the liveoverrideNoticeproducer) sets zerotitle:keys on its dispatch; anduseActionTextLocalizer— the one thing that rewrites an action on the way to therunner — emits
label,confirmTextandsuccessMessageonly. A repo-wide sweepfor reads of the key returns exactly one live read, the one this PR removes
(control: 52
action?.labelreads):The two prose mentions: both were stale, both refreshed
Neither still read correctly, so neither was left:
action-menu.tsx:223quotedtitle: action?.label || action?.titleverbatim asa description of live code. No such expression exists anywhere in the repo after
this PR.
action-forward-parity.test.tsx:21quoted the same dead expression and citeduseConsoleActionRuntime.tsx:205-207, which was already wrong before this PR — thesetParamStatecall sits at:266-269today.Both now cite the symbol (
useConsoleActionRuntime'sparamCollectionHandler,and the second copy in
RecordDetailView) rather than a line number, because a linecite in a doc comment is a fact with no gate behind it — this is the second time
these numbers have gone stale. The parity test's header also now states why its
labelassertion is the whole of the title contract rather than half of it.Evidence: a reachability argument, plus a pin that bites
RecordDetailView.paramDialogTitle.test.tsxpins the reader, not the deletion —the handler's
actionparameter is stillany, so nothing in the compiler stops thealias being reinstated by the next reader who sees an untitled dialog and reaches for
a second key. A pin per reader is the only shape that covers both handlers: the hook's
existing pin cannot see this site at all, which is precisely why this limb survived
#5609.
Reverse-verified by mutating the fix away on disk, with a
trap ... EXIT INT TERMrestore. The mutation was proved on disk with anchored counts in both directions
before anything was read — the editor's exit code was not used as evidence:
No rebuild step, stated deliberately: the ablation condition that forces one does
not hold here. The test imports
./RecordDetailViewrelatively, from inside the samepackage's
src/, so it reads the mutated file directly — there is no dependencyexportshop to a builtdist/that a stale artifact could hide behind.Direction observed, as predicted: red, on the one test that pins the removal.
The other three stayed green by design, and are not counted as evidence of the
deletion: test 1 and test 4 assert the
labelpath, which the mutation does nottouch; test 3 is a legibility control (
labelwins when both are present, greenbefore and after because
||short-circuits). A lone red on test 2 beside a greentest 3 reads unambiguously as "the limb is back". After the trap:
sha256back to19078d2936280e2f,git status --porcelainempty.Gates — all on
861520af9, 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 body echoed — not a zero-match filter)@object-ui/components type-checktsc --noEmit && tsc -p tsconfig.test.json(script body echoed)@object-ui/app-shell lint✖ 2549 problems (0 errors, 2549 warnings)@object-ui/components lint✖ 896 problems (0 errors, 896 warnings)vitest run(app-shell observable set, 87 files)Test Files 87 passed (87)·Tests 928 passed (928)vitest run(packages/components/, whole package)Test Files 174 passed (174)·Tests 1576 passed (1576)check-control-bytes.mjsOK (scanned 4667 tracked text file(s); skipped 85 binary)check-changeset-presence.mjs4 source file(s) of 2 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 errors (0 total)check-action-forward-parity.mjs5 surfaces checked against 39 runtime-read keys from 4 consumerscheck-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-node-esm-load.mjs --specifiers-onlyno un-ledgered package emits an extensionless relative specifiercheck-spec-symbol-derivation.mjs1290 files scanned against 4912 spec export namescheck-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-skills-paths.mjsOK (95/96 stated path(s) resolve across 18 guide file(s); 1 baselined)check-doc-component-types.mjsEvery documented component type is registered.check-doc-links.mjscheck-action-forward-parityis the one worth naming, and it is not presented asconfirmation the deletion was safe: it derives its owed key set from what the runtime
reads, so deleting a read shrinks that set and its pass is structurally guaranteed.
The measurement is visible — it reports 39 runtime-read keys where #5609 reported
40. The reachability probes above carry the safety argument, not this gate.
The lint warning delta on
app-shell(2538 in #5609 to 2549) is entirely the new testfile: 26
@typescript-eslint/no-explicit-anywarnings, the same rule and same class thesibling suites already carry (
RecordDetailView.modalDispatch.test.tsxhas 35). Theyare unavoidable while the handler's
actionisany— see the out-of-scope note below.Zero errors either way, which is the gate's criterion.
Two gates NOT run locally, declared
Both are broken-gauge preconditions in this container, not verdicts on this change,
and CI runs both regardless:
check-eager-closure-budget.mjs(exit 2) —No eager-closure report at apps/console/dist/eager-closure.json … an absent report means the console was not built. It needs a fullapps/consolevite build. It cannot move here: the diffchanges zero import or export lines in production source (verified by diffing
the two non-test files for
import/require(/exportlines — 0 and 0). Every addedimport in this PR lives in the new test file, which no app bundle includes.
check-doc-snippet-types.mjs(exit 1) —The snippet program was NOT run: the packages it resolves against are not built, namingapp-shell,cli,plugin-markdown,plugin-timelineas[unbuilt-package]. This PR changes zerodocs-site files (the only
.mdit adds is the changeset), so the gate has nothing ofmine to scan.
Declared narrowing on the app-shell test run
packages/app-shellhas 487 test files and the full package suite does not fit thecontainer's 10-minute foreground cap.
packages/componentswas run whole (174files) rather than narrowed, since the change there is comment-only. For app-shell the
run covers a provable superset of everything that can observe the change — 87 files,
928 tests, all green:
test naming
RecordDetailView(28); every test namingAppContent,InterfaceListPage,ObjectDataPage,ObjectViewor importing theviewsbarrel,i.e. the transitive mounters that would not appear in a grep for the view's own name
(52); every
*ratchet*test (7); and every test namingActionParamDialog,onParamCollection,paramDialogStateorparamCollection(26);87 passed (87)/928 passed (928), and174 passed (174)/1576 passed (1576);paramCollectionHandler, whoseonly value flows into the param dialog's
titleprop. A test outside that setneither renders the dialog nor reaches the view by any import path, so no excluded
file's verdict can move. The other two edited files are comments only and can
move no verdict at all.
CI runs the full farm regardless.
Not touched, deliberately
The
anytoActionDefnarrowing on this handler is out of scope and filed as#5611. It hits the same
overrideNoticewall #4282 measured — a live producer atDeclaredActionsBar.tsx:305for a key declared on no action surface. It was notattempted here, and deliberately not cast around: a cast would swap a visible
anyfor an invisible one, which is the defect class this card belongs to.
Refs #4282, #4046, #4192, #5178.
Generated by Claude Code
Generated by Claude Code