Skip to content

fix(app-shell): title RecordDetailView's param dialog from label alone - #5618

Merged
os-sales merged 1 commit into
mainfrom
claude/issue-5610-recorddetailview-action-title
Aug 21, 2026
Merged

fix(app-shell): title RecordDetailView's param dialog from label alone#5618
os-sales merged 1 commit into
mainfrom
claude/issue-5610-recorddetailview-action-title

Conversation

@os-sales

Copy link
Copy Markdown
Collaborator

Fixes#5610

Deletes the || action?.title fallback from RecordDetailView'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 #5609 removed from
useConsoleActionRuntime. RecordDetailView builds its own action runtime rather
than routing through that hook, so the two near-identical paramCollectionHandlers
drifted as a pair and the first fix could not reach this site.

Premise re-verified before deleting anything, on current origin/main

The card cited :539 and :500. Both are still accurate at 6606337e3 — quoted
at the numbers actually found:

  • :500const paramCollectionHandler = useCallback((params: ActionParamDef[], action?: any) => {
  • :539title: 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.

Producer surfaceprobe: titlecontrolverdict
@objectstack/specActionSchema (walked live at spec 17.0.0, 44 keys)absentdescription present, label presentno producer
@object-ui/coreactionKeys.ts (ACTION_DEF_KEYS / SPEC_ACTION_KEYS)0 quoted entries'description' 2, 'label' 2no producer
@object-ui/coreActionDef interface (ActionRunner.ts:112)0 fieldsdescription 1, label 1no producer
@object-ui/typesui-action.ts1 hit, prose only (titleFormat, :273)description 4, label 10no producer
@object-ui/typescrud.tsActionSchema (body 88..262)0 own fields — only retired-confirm prose (:136) and the nesteddialog.title (:161-163)label 2no producer
@object-ui/typesBaseSchema (base.ts body 69..333)0description 5, label 17no producer, and none inherited
base.ts:599title?: stringbelongs to HTMLAttributes (:595), notBaseSchemathe DOM tooltip attribute
action:button / action:icon / action:group / action:menu2 / 1 / 1 / 4 hits, all prose in comments; 0 code-level forwardslabel 13-14 each, description 1-3 eachnone forwards it

The spec walk is the pin test's own zod-internals walk, run against the installed
@objectstack/spec@17.0.0:

total keys : 44
probe title : false
control description: true
control label : true

Two probe corrections worth naming, since they change how the table reads:

  • crud.ts's ActionSchema has no description either (0 in the interface
    body). fix(app-shell): title the param dialog from label alone #5609's row used a file-scoped description count as its control against
    an interface-scoped probe. Scoped consistently, label (2) is the only valid
    in-scope control there — a control that is itself absent cannot falsify anything.
  • Two title substrings 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 still
    read 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 live
overrideNotice producer) sets zerotitle: keys on its dispatch; and
useActionTextLocalizer — the one thing that rewrites an action on the way to the
runner — emits label, confirmText and successMessage only. A repo-wide sweep
for reads of the key returns exactly one live read, the one this PR removes
(control: 52 action?.label reads):

packages/app-shell/src/views/RecordDetailView.tsx:539 ** this one, removed here
packages/app-shell/src/hooks/useConsoleActionRuntime.tsx:260 prose (#5609's own comment)
packages/app-shell/src/hooks/__tests__/useConsoleActionRuntime.paramDialogTitle.test.tsx:13 prose
packages/components/src/renderers/action/action-menu.tsx:223 prose - refreshed here
packages/components/src/renderers/action/__tests__/action-forward-parity.test.tsx:21 prose - refreshed here

The two prose mentions: both were stale, both refreshed

Neither still read correctly, so neither was left:

  • action-menu.tsx:223 quoted title: action?.label || action?.title verbatim as
    a description of live code. No such expression exists anywhere in the repo after
    this PR.
  • action-forward-parity.test.tsx:21 quoted the same dead expression and cited
    useConsoleActionRuntime.tsx:205-207, which was already wrong before this PR — the
    setParamState call sits at :266-269 today.

Both now cite the symbol (useConsoleActionRuntime's paramCollectionHandler,
and the second copy in RecordDetailView) rather than a line number, because a line
cite 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
label assertion is the whole of the title contract rather than half of it.

Evidence: a reachability argument, plus a pin that bites

RecordDetailView.paramDialogTitle.test.tsx pins the reader, not 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. 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 TERM
restore. 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:

sha256 before mutation : 19078d2936280e2f
anchor 'title: action?.label,' before : 1
mutant 'title: ...label || ...title,' before : 0
python replace: 1 site rewritten
sha256 after mutation : b649b9421c1e52cc (differs -> not a no-op)
removed 'title: action?.label,' after : 0
injected 'title: ...label || ...title,' after : 1
git diff --stat: 1 file changed, 1 insertion(+), 1 deletion(-)

No rebuild step, stated deliberately: the ablation condition that forces one does
not hold here. The test imports ./RecordDetailViewrelatively, from inside the same
package's src/
, so it reads the mutated file directly — there is no dependency
exports hop to a built dist/ that a stale artifact could hide behind.

Direction observed, as predicted: red, on the one test that pins the removal.

AssertionError: expected 'Should Not Win' to be undefined
Test Files 1 failed (1)
Tests 1 failed | 3 passed (4)
VITEST_EXIT=1
TRAP: restored packages/app-shell/src/views/RecordDetailView.tsx

The other three stayed green by design, and are not counted as evidence of the
deletion: test 1 and test 4 assert the label path, which the mutation does not
touch; test 3 is a legibility control (label wins when both are present, green
before and after because || short-circuits). A lone red on test 2 beside a green
test 3 reads unambiguously as "the limb is back". After the trap: sha256 back to
19078d2936280e2f, git status --porcelain empty.

Gates — all on 861520af9, the final commit

Exit codes captured before any pipe; each gate quoted by its own verdict line.

Gateexitits own verdict
@object-ui/app-shell type-check0tsc --noEmit && tsc -p tsconfig.test.json (script body echoed — not a zero-match filter)
@object-ui/components type-check0tsc --noEmit && tsc -p tsconfig.test.json (script body echoed)
@object-ui/app-shell lint0✖ 2549 problems (0 errors, 2549 warnings)
@object-ui/components lint0✖ 896 problems (0 errors, 896 warnings)
vitest run (app-shell observable set, 87 files)0Test Files 87 passed (87) · Tests 928 passed (928)
vitest run (packages/components/, whole package)0Test Files 174 passed (174) · Tests 1576 passed (1576)
check-control-bytes.mjs0OK (scanned 4667 tracked text file(s); skipped 85 binary)
check-changeset-presence.mjs04 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s)
check-changeset-no-major.mjs0No changeset declares a 'major' bump.
check-changeset-fixed.mjs0All workspace packages are in the changeset fixed group.
check-type-check-coverage.mjs045/46 via type-check … 41/41 packages compile their tests
check-lint-coverage.mjs046/46 packages linted, 0 with outstanding errors (0 total)
check-action-forward-parity.mjs05 surfaces checked against 39 runtime-read keys from 4 consumers
check-phantom-dependencies.mjs0Every in-scope import is declared by the package that publishes it.
check-package-self-import.mjs0No package names itself inside its own src/.
check-node-esm-load.mjs --specifiers-only0no un-ledgered package emits an extensionless relative specifier
check-spec-symbol-derivation.mjs01290 files scanned against 4912 spec export names
check-i18n-call-site-keys.mjs0Every in-scope call-site key resolves against the en pack (2918 keys)
check-i18n-en-drift.mjs0No en value changed in this range.
check-skills-paths.mjs0OK (95/96 stated path(s) resolve across 18 guide file(s); 1 baselined)
check-doc-component-types.mjs0Every documented component type is registered.
check-doc-links.mjs0(green)

check-action-forward-parity is the one worth naming, and it is not presented as
confirmation 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 test
file: 26 @typescript-eslint/no-explicit-any warnings, the same rule and same class the
sibling suites already carry (RecordDetailView.modalDispatch.test.tsx has 35). They
are unavoidable while the handler's action is any — 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 full apps/console vite build. It cannot move here: the diff
    changes zero import or export lines in production source (verified by diffing
    the two non-test files for import/require(/export lines — 0 and 0). Every added
    import 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, naming app-shell, cli,
    plugin-markdown, plugin-timeline as [unbuilt-package]. This PR changes zero
    docs-site files (the only .md it adds is the changeset), so the gate has nothing of
    mine to scan.

Declared narrowing on the app-shell test run

packages/app-shell has 487 test files and the full package suite does not fit the
container's 10-minute foreground cap. packages/components was run whole (174
files) 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:

  1. the population was read from the code, not guessed — the union of: every app-shell
    test naming RecordDetailView (28); every test naming AppContent,
    InterfaceListPage, ObjectDataPage, ObjectView or importing the views barrel,
    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 naming ActionParamDialog,
    onParamCollection, paramDialogState or paramCollection (26);
  2. the counts are vitest's own: 87 passed (87) / 928 passed (928), and
    174 passed (174) / 1576 passed (1576);
  3. invariance: the change alters one expression inside paramCollectionHandler, whose
    only value flows into the param dialog's title prop. A test outside that set
    neither 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 any to ActionDef narrowing on this handler is out of scope and filed as
#5611. It hits the same overrideNotice wall #4282 measured — a live producer at
DeclaredActionsBar.tsx:305 for a key declared on no action surface. It was not
attempted here, and deliberately not cast around: a cast would swap a visible any
for 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

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>
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 52 chunks)3785.7 KB3867.2 KB
Main entry chunk (gzip)151.6 KB350 KB
Entry fileindex-DC-FKCpI.js
StatusPASS

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

PackageSizeGzipped
app-shell (index.js)10.04KB3.72KB
app-shell (runtime-config.js)8.91KB2.99KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)1.17KB0.53KB
auth (AuthProvider.js)29.34KB7.05KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)6.35KB2.43KB
auth (index.js)2.77KB1.22KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.02KB0.89KB
auth (useIsWorkspaceAdmin.js)3.04KB1.45KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)506.99KB113.73KB
core (index.js)4.51KB1.80KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)159.80KB44.33KB
fields (index.js)238.85KB60.13KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.22KB3.08KB
i18n (provider.js)23.13KB7.63KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)7.77KB3.13KB
layout (index.js)38.95KB10.97KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)9.35KB3.31KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)4.42KB1.42KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)1.81KB0.83KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.62KB12.83KB
plugin-charts (index.js)64.72KB18.35KB
plugin-chatbot (index.js)181.41KB43.22KB
plugin-dashboard (index.js)128.36KB32.95KB
plugin-designer (index.js)212.30KB42.80KB
plugin-detail (index.js)242.15KB60.89KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)125.07KB30.43KB
plugin-gantt (index.js)164.10KB39.87KB
plugin-grid (index.js)200.79KB54.26KB
plugin-kanban (index.js)52.93KB14.60KB
plugin-list (index.js)111.70KB27.17KB
plugin-map (index.js)20.06KB6.62KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.49KB11.93KB
plugin-timeline (index.js)26.68KB7.66KB
plugin-tree (index.js)8.50KB2.88KB
plugin-view (index.js)84.50KB20.68KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)3.77KB1.33KB
react (SchemaRenderer.js)43.66KB14.77KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)1.33KB0.69KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (index.js)4.77KB2.16KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)10.76KB3.17KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.29KB0.24KB
sdui-parser (validate.js)6.92KB2.40KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)0.20KB0.18KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)0.20KB0.18KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.87KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-retry.js)4.32KB2.02KB
types (index.js)3.08KB1.53KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)0.20KB0.18KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-salesClaude

Copy link
Copy Markdown
CollaboratorAuthor

PM review — ACCEPT (card #5610)

Gates. 22 named check runs read individually for completed + success at 861520af9: 19 success, 3 skippedTest (coverage), the unexpanded Test (coverage shard …/4) matrix placeholder, and dependabot. All four real shards, Type Check, Lint, Build & E2E, Build Docs, Bundle Analysis, Live E2E, both doc type checks, Control Byte Scan, Skill Guide Path Check, Internal Docs Link Check and all three changeset checks green.

You corrected the PR this one mirrors, and one of those corrections is a miss in my review

#5609's crud.ts control was not falsifiable. Its probe searched the ActionSchemainterface body for title, but ran the description control file-scoped. crud.ts's ActionSchema has no description in its interface body either, so that control could not have failed whatever the answer was — label (2) is the only valid in-scope control there.

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 — title is genuinely absent from every producer surface, carried by the other probes and by your own independent walk — so nothing landed wrong, but the review discipline needs the extra clause and it is going on the seat post.

The @object-ui/core substrings were the words "subtitle" and "titled itself" in prose about description, not mentions of this key. Correctly read and correctly left alone.

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

check-action-forward-parity reports 39 runtime-read keys where #5609 reported 40. That is the structural point from #5609's ACCEPT made concrete: the gate derives its owed set from what the runtime reads, so deleting a read shrinks the set and its pass is guaranteed. You ran it, named the number, and explicitly declined to present it as independent confirmation. Precisely the right handling.

Verified, not assumed — including where the card turned out to be right

I told you the card's :539 and :500 had probably drifted. They had not, and you quote them at those numbers having checked. Re-deriving and finding the card accurate is the same work as re-deriving and finding it stale; only the outcome differs.

The producer sweep is thorough in the way that matters for a deletion: spec ActionSchema walked live at 17.0.0 (44 keys, title absent, controls present), zero .title = assignments anywhere, DeclaredActionsBar setting zero title: keys on its dispatch, and useActionTextLocalizer emitting only label / confirmText / successMessage.

Both prose sites, and a durable fix for them

Both were stale, both refreshed — and action-forward-parity.test.tsx additionally cited useConsoleActionRuntime.tsx:205-207 when the call sits at :266-269 today. Rewriting both to cite the symbol rather than a line number is the right response to numbers that "have now gone stale twice", and it is the same lesson #4559 and #4966 taught this round from the other direction.

Ablation

Committed 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 || short-circuits. The no-rebuild condition is stated with its reason (relative same-package import, no exports hop to a stale dist) rather than skipped.

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 packages/components run whole since its change is comment-only. The two unrun gates are broken-gauge preconditions in this container, each declared with why it cannot move on this diff.

The anyActionDef narrowing was left alone and not cast around, as scoped. It remains #5611.


Generated by Claude Code

@os-sales
os-sales marked this pull request as ready for review August 21, 2026 18:36
@os-sales
os-sales added this pull request to the merge queueAug 21, 2026
Merged via the queue into main with commit 8c87f05Aug 21, 2026
23 checks passed
@os-sales
os-sales deleted the claude/issue-5610-recorddetailview-action-title branch August 21, 2026 18:37
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RecordDetailView carries a second copy of the param dialog's dead action?.title fallback

2 participants

@os-sales@claude