Skip to content

fix(plugin-detail): related list with actions: [] no longer draws the built-in New - #26277

Merged
objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/project-thread-pp8ksi
Oct 27, 2026
Merged

objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/project-thread-pp8ksi

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steve · project thread
Fixes #26182

Clause-②: no
Metadata-protocol: semantic

What

record:related_list with an authored actions: [] no longer draws the built-in New. The empty list is the author's choice of no actions on that list, and the generic create form is now part of "no actions". It holds per list, whatever the child object's userActions.create says.

  • ui/packages/plugin-detail/src/renderers/record-related-list.tsx: a new createWithheldByAuthor (an authored array of length 0) withholds onNew from RelatedList. Absent actions, or a non-empty list, passes the host's onCreate exactly as before.
  • The composition-rule comment in the same file now states the [] arm. The old text said the built-ins "stay the host's either way".
  • No host change: the app-shell bridge still decides create object-wide; the renderer drops it for one list.
  • README (RelatedList section) documents the authoring move.

Why the meaning changes (Metadata-protocol: semantic)

Before, actions: [] hid the list's authored and default actions while New stayed. A pinned case in RecordRelatedListRenderer.authoredActions-11163.test.tsx said so ("while New stays, because it is not an action id"). That pin was the implementer's reading, not the ruling: the ENFORCE ruling on #20665 (comment 5902374623) says "the authored list is what renders when it is present" and names the Salesforce shape, where New is one of the list's own buttons. The card's triage grade reads [] the same way: "An explicit empty list means no actions; absent keeps the defaults."

The value [] was already accepted, so no key is added or removed and the spec is untouched. What changes is what the value means, so this declares semantic, and the changeset grades @object-ui/plugin-detail minor. The claim on the card carried Metadata-protocol: none; the seat agreed to the semantic declaration and will amend the claim.

Producers measured at 3d50a43b: no in-repo page, example, platform page or synthesiser (buildDefaultPageSchema) authors actions: [] on a record:related_list, so no shipped page loses New.

Tests

At head 58a07ee6 (the code commit is 16f675b7; e7398539 re-records the sdui source digest; 58a07ee6 merges origin/main):

  • RecordRelatedListRenderer.authoredActions-11163.test.tsx: 15/15 passed. The old [] case is inverted (New absent though the host offers create). A new block pins the rule: CONTROL with no actions draws New and runs the host's create; a non-empty list keeps New; [] on one list leaves a sibling list of the same object with New.
  • Ablation (scripts/ablation-replace.mjs, onNew={createWithheldByAuthor ? undefined : handlers?.onCreate} back to onNew={handlers?.onCreate}): the two [] cases went red, 2 failed / 13 passed. Restore verified: blob equals HEAD, git diff HEAD empty. The test imports the package source, so no build leg was needed.
  • pnpm --dir ui exec vitest run --maxWorkers=2 packages/plugin-detail/: 304 files passed, 1 skipped; 2828 tests passed, 18 skipped. Exit 0.
  • pnpm --filter @object-ui/plugin-detail type-check: exit 0.
  • node scripts/gen-sdui-manifest-node.mjs: 1 of 27 package records re-recorded (plugin-detail); sdui.manifest.json unchanged. node scripts/check-sdui-manifest.mjs exit 0 after the main merge.
  • node scripts/pm/dispatch-gates.mjs --commands: 47 derived families, all run, each exit 0; --ran reconciled 47/47 with exit codes recorded, 0 NOT-MEASURED.
  • pnpm --dir ui run check:new-line-citations: 0 new citations.
  • Console build after the main merge, pnpm exec turbo run build --filter=@object-ui/console --concurrency=2: 71/71 tasks, exit 0.

Acceptance notes


Generated by Claude Code

… built-in New

An authored `actions: []` on `record:related_list` is the author's choice
of no actions on that list, the generic create form included. It holds
per list, independent of the child object's userActions.create. Absent
or non-empty `actions` keep New as before; row affordances unchanged.

Claude-Session: https://claude.ai/code/session_01V5ckU1cUgQX9SC8V9TG3dh
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: TIER_DEFAULT
Head-sha: 58a07ee6fbadb733fc9edadf57c26d174ecba5a3
Local-runs: none

① Derived judgments

  • The spec row is packages/spec/src/ui/component.zod.ts:1834, actions: z.array(z.string()).optional(), described as "Action IDs available for related records". An authored empty list reads most naturally as "no actions are available for related records". Withholding the generic create form there matches the row's wording. Keeping New could only be justified by "New is not an action id", which is the reading objectui#11163 pinned. The maintainer's ENFORCE ruling it cites (spec(ui): record:related_list.actions is declared ("Action IDs available for related records") and read by no renderer — ENFORCE or RETIRE under ruling A′ ④ (the remainder of objectui#8071) #20665) required that actions be read; it did not rule on New.
  • The change is per list and keyed only on Array.isArray(actions) && actions.length === 0. Absent and non-empty lists are byte-identical in behaviour, and the object-wide userActions.create verdict still decides every other list. Edit, Delete and View are untouched. This is the narrowest reading that delivers the card's Expected.
  • Blast radius: no metadata in this repository (examples, packages, apps, templates) authors actions: [] on a related list. The reporting app (hotclm contract_detail.page.ts, contract_renewals) authored it to mean exactly this.

② Contract declarations

  • Metadata-protocol: semantic: a changed meaning for an existing value, with minor on @object-ui/plugin-detail. Correct per AGENTS §3. The claim is amended to match.
  • Clause-②: no: no exported type, prop or runtime export changes; the renderer stays registered under the same type. Correct.
  • The README documents the composition rule, and the changeset carries the FROM → TO line ([] used to keep New and now withholds it), so a host that relied on the old reading finds the fix: omit actions, or list the actions.

③ Evidence

  • The inverted pin and two new cases cover [] withholding New, absent keeping it, and non-empty keeping it. Ablation turns both [] cases red. The target file is 15/15, and plugin-detail passes 2828 tests. Type-check exits 0, Console build exits 0, and 47/47 derived gates exit 0.
  • scripts/sdui-manifest.sources/plugin-detail.json is re-recorded; only the plugin-detail digest moves.

Implemented-by: card thread session_01V5ckU1cUgQX9SC8V9TG3dh
Reviewed-by: session_017iXjUm1rujwDk7f3KT1Pkk (seat domain:ui#1)

VERDICT: PASS

Landing is held for the plugin-detail line: #26060 (in the merge queue), then #26055. After those, this branch merges origin/main, re-records, and the seat arms it.

…-pp8ksi

# Conflicts:
#	scripts/sdui-manifest.sources/plugin-detail.json
… not a bare TS literal

Claude-Session: https://claude.ai/code/session_01V5ckU1cUgQX9SC8V9TG3dh
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · ACCEPT · seat domain:ui#1 · 2026-10-27T00:28Z

Head 85bada25 (read from the PR object). It is 1fd0208c (which merges main after seat 2's #26055) plus a README-only fix. The README fence at README.md:752 was a bare TS object literal that failed Lint UI check:doc-snippets (TS1005). It is now a ```json node, +3/-3. The seat's contract review PASS (6424203289) stands: there is no code change since.

  • Shape. Draft on main, Fixes #26182. 5 files, +96/-13: record-related-list.tsx, the objectui#11163 authored-actions test (the pin inverted: actions: [] now withholds the built-in New), the plugin-detail README, the re-recorded plugin-detail.json, and a root changeset (@object-ui/plugin-detail minor). Clause-②: no. Metadata-protocol: semantic: an authored actions: [] now means "no actions, New included", per the spec row at packages/spec/src/ui/component.zod.ts:1834.
  • Checks pinned to the head. All check runs completed: 0 failing, 0 pending. The seat's landing script checked this before posting.
  • Governed surface. check-governed-merges.mjs --pr 26277: not governed.
  • sdui. Only plugin-detail.json conflicted on the main merge. The dev took main's copy and regenerated under the lock rather than hand-merging: "1 of 26 registry package record(s) re-recorded: plugin-detail". check-sdui-manifest exits 0.
  • Evidence (dev, on 85bada25). check-doc-snippet-types.mjs: 809/809 blocks judged, 0 failed, exit 0. check-sdui-manifest exits 0 (the README is not a recorded source). On 1fd0208c: plugin-detail suite: 305 files passed and 1 skipped; 2845 tests passed and 18 skipped. plugin-detail type-check passes. Console build 71/71, VERDICT exit 0. 45/45 gates, 0 NOT-MEASURED.
  • Serial constraints. plugin-detail line: feat(plugin-detail): the record activity timeline folds consecutive updates by one actor into one row with a count #26055 (merged), then this PR, then seat 2's fix(plugin-detail): reveal the inline-edit pencil on keyboard focus #26325, which merges main and re-records after this lands. The ⛔ paths are untouched.

Landing: ready + auto-merge through the relay.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 27, 2026 00:29
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 27, 2026 00:29
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 27, 2026
Merged via the queue into main with commit f02e644 Oct 27, 2026
62 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-pp8ksi branch October 27, 2026 00:52
os-tesla pushed a commit that referenced this pull request Oct 27, 2026
#26277 re-recorded scripts/sdui-manifest.sources/plugin-detail.json while
the manifest's bytes stayed the same: one modify/delete conflict against
this PR's deletion of the records, resolved by deleting it.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6SEitNngAMugGbuJHWsDS
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

record:related_list: actions: [] still draws "New" — a page cannot drop the create button from one related list

1 participant