Skip to content

feat(example-showcase): host record_header / record_more / record_related for showcase_task - #22036

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-22030-showcase-record-action-hosts
Oct 6, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-22030-showcase-record-action-hosts

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #22030

Clause-②: no

The showcase now hosts the three record action locations for showcase_task on stock pages, so records-forms.action-location-matrix A1 (record_header / record_more / record_related) and A3 (the url and api dispatch from the ⋯ overflow) can be run on a stock showcase boot. This is a fixture change in examples/app-showcase only: no product code, no checklist edit, and no new action.

What changed

Location Host Actions it shows (showcase_task)
record_header Task Detail (showcase_task_detail): a new page:header node, first in the page showcase_mark_done (script), showcase_log_time (form), showcase_archive_task (script), as inline buttons
record_more the same header's ⋯ overflow showcase_open_docs (url), showcase_recalc_estimate (api), showcase_recalc_selection (api)
record_related Project Detail (showcase_project_detail): a new related tab with the Tasks record:related_list showcase_log_time on each row (beside the list_item actions)
  • src/ui/pages/task-detail.page.ts: adds page:header with actions set to the task's six record_header / record_more action ids, which is the set the synthesized header would carry. No title, so the heading still comes from the record. The host's Edit / Share / Delete (order 100 to 120) still append after the authored actions.
  • src/ui/pages/project-detail.page.ts: keeps the tasks tab and its record:line_items grid unchanged. Adds a third tab, related, with record:related_list for showcase_task through project. It has no actions key, so the rows carry the host bridge's actions.
  • src/ui/actions/index.ts: comment only. The note on showcase_reopen_task said the Task Detail page "renders no header action bar". That is no longer true, so the comment now points to the header's id list and to the pin that holds it.
  • test/record-action-location-hosts.test.ts (new, 9 cases) pins both hosts on the page the console picks for each object. It also holds the authored lists equal to the declarations they copy: the header ids against the task's record_header / record_more set, and the related list's title / columns against the FK's relatedListTitle / relatedListColumns.
  • test/project-detail-tabs.test.ts: the tab strip is now details, tasks, related (3 items, was 2).

Why this shape (measured at the objectui pin a58626c88dc8)

  • The hosts must sit on the existing pages. usePageAssignment renders the FIRST type: 'record' page bound to an object, in declaration order, so a second page for either object would never be reached.
  • A kind: 'full' page renders only what it declares (objectui#7298 ruling, RecordDetailView). With no page:header node, the page had no title bar. PageHeaderProps.actions is spec-declared (z.array(z.string()), "Action IDs to show in header"). The renderer (containers.tsx, resolvedHeaderActions) looks the ids up in the object's metadata and places each one by its locations: record_header actions go inline, and an action that declares record_more without record_header always goes under ⋯. os validate (validate-action-name-refs) checks the same ids.
  • The tabs slot is a full replacement, so overriding it removed the synthesized related-list strip. showcase_task.project is the task's only parent relationship. RelatedRecordActionsBridge wraps the whole record-page body, authored or synthesized, and places list_item + record_related on the rows of any record:related_list under a parent record (ROW_LOCATIONS_IN_RECORD). For an authored node with no actions, the record-related-list renderer leaves those host actions untouched.
  • Alternative considered and not taken: moving project-detail's override from tabs to details, which would keep the synthesized strip itself. It would remove the authored page:tabs that the showcase 的 project-detail 页在 page:tabs 上授权 items[].key,renderer 读的是 items[].value —— 两个 tab 的稳定 URL token 从未生效 #5776 pin exists for, drop the ?tab=tasks token, and move the editable grid into the Details tab.
  • Deviation from the card's wording: the card asks for "the synthesized Tasks related list". The list here is an authored record:related_list node, the same component and the same bridge as the synthesizer's output, with title / columns copied from the FK (only the synthesizer reads relatedListTitle / relatedListColumns). The follow-up knownGap lift should say "the Tasks related list".

Guard: record:line_items stayed

It was not removed from project-detail. git grep -n 'record:line_items' over docs/qa/platform-checklist and examples/app-showcase/test at HEAD:

Verification (HEAD 3ae68e530c)

  • Showcase suite: pnpm --filter @objectstack/example-showcase test exit 0, 33 files / 408 tests.
  • The new and updated test files, verbose: 13/13 green.
  • validate and typecheck: pnpm --filter @objectstack/example-showcase validate exit 0 ("Validation passed"). None of its 72 warnings names either page, showcase_task.* or relatedList*. typecheck exit 0; tsc --listFiles includes both pages and both test files.
  • Ablation (one-time, scripts/ablation-replace.mjs WRAP mode, every leg restored with blob == HEAD and an empty git diff HEAD):
    • leg 1, task header retyped to page:card: 4 failed / 5 passed;
    • leg 2, related list retyped to record:highlights: 4 failed / 5 passed (re-run after hardening two cases that passed vacuously the first time, 2 failed / 7 passed);
    • leg 3, record_header added to showcase_reopen_task without adding it to the header: 1 failed, the set-equality case, naming showcase_reopen_task.
  • Gates: node scripts/pm/dispatch-gates.mjs --commands on the actual diff derived 42 commands. All 42 ran, each with a recorded exit code, and all exit 0. --ran: "42 derived famil(ies) accounted for — 42 run, 0 NOT-MEASURED (a DERIVED zero)". check:dual-build-cjs-loads first answered PREREQUISITE NOT MET (8 packages unbuilt); it was re-run after building them and answered exit 0. Named in the dispatch and green: check:page-declaration-shape, check:examples-live-imports, check:i18n-coverage ("none new"), check:react-page-adapter-contract, check:cross-package-test-inputs, check:org-identifier, check-undeclared-dep-imports (+ --self-test), check:nul-bytes.
  • Lint, narrowed: eslint --no-inline-config --format json over the 5 changed files: 5 files, 0 errors, 0 warnings. These files fall under the config block for examples/**/*.{ts,tsx,mts,cts}. The config never enables type-aware linting (no parserOptions.project), so this diff cannot change the verdict on any untouched file. The full pnpm lint run is CI's.
  • Live boot (backend only, pnpm dev:showcase -- --fresh on a random port, torn down afterwards): from the served GET /api/v1/meta/page list and GET /api/v1/meta/object/showcase_task:
    • record pages are served in the order showcase_project_detail, then showcase_task_detail, each the first for its object;
    • all six header ids match a served action, and they equal the served record_header / record_more set. Three are inline-eligible (script, form, script) and three are overflow-only (url, api, api);
    • project-detail serves the tabs details, tasks, related, with record:line_items kept;
    • record_related on the task is showcase_log_time, and a seeded project has 2 tasks.
  • NOT MEASURED: the browser render of the two surfaces. No console bundle exists in any checkout here, and building one would mean a full objectui build at the pin. The rendering claims above are read from objectui source at the pin.

Acceptance notes

Labels

skip-changeset: @objectstack/example-showcase is private and publishes nothing.


Generated by Claude Code

claude added 2 commits October 6, 2026 19:39
…ated for showcase_task

Task Detail (kind 'full') now composes a page:header listing the task's
record_header / record_more action ids, so the title bar and its overflow
menu exist. Project Detail keeps its record:line_items Tasks tab and adds a
Related tab with the Tasks record:related_list, whose rows carry the
record_related actions. A new showcase test pins both hosts.

Claude-Session: https://claude.ai/code/session_01VF48aw8RPG6wzDnMgp6rtw
Co-authored-by: Claude <noreply@anthropic.com>
…nd the FK declaration

Claude-Session: https://claude.ai/code/session_01VF48aw8RPG6wzDnMgp6rtw
Co-authored-by: Claude <noreply@anthropic.com>
Picks up the checkout-wall fix (099a94d) so the cancelled
Type Check source-gates job can run to completion.

Claude-Session: https://claude.ai/code/session_01VF48aw8RPG6wzDnMgp6rtw
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 6, 2026 21:00
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 6, 2026 21:00
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 6, 2026
Merged via the queue into main with commit 60ccda5 Oct 6, 2026
35 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-22030-showcase-record-action-hosts branch October 6, 2026 21:19
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 7, 2026
…the showcase hosts record_header / record_more / record_related (objectstack-ai#22040)

Fixes objectstack-ai#22039
Clause-②: no

The showcase change carded as objectstack-ai#22030 landed as PR objectstack-ai#22036 (`60ccda5a24`).
It hosts `record_header`, `record_more` and `record_related` for
`showcase_task` on stock showcase pages. This PR lifts the row-7 fixture
knownGap on `records-forms.action-location-matrix` that parked those
three locations, and A3's url / api dispatch legs, on that card. It is a
checklist-only diff: one item in
`docs/qa/platform-checklist/areas/records-forms.json`, no other item, no
code.

## What changed (item `records-forms.action-location-matrix`, revision 8
to 9)

| Key | Before | After |
|---|---|---|
| `revision` | 8 | 9 |
| `fixtures.knownGaps[1]` | "the showcase hosts NO surface for three of
the six locations on showcase_task …"; score them blocked(fixture); park
on objectstack-ai#22030 | Opens with a closed marker citing objectstack-ai#22030 / PR objectstack-ai#22036 /
`60ccda5a24` and this card, then says where each location now lives on a
stock boot (below) |
| `steps[1]` | surfaces listed as "a not-done task's detail title bar /
its ⋯ menu / a related-list section", plus "A location with no hosting
surface at this fixture (record_header / record_more / record_related)
is recorded blocked(fixture)" | Names each host: the Task Detail title
bar, that title bar's ⋯ menu, and the rows of the Tasks related list on
Project Detail's Related tab, "not the Tasks tab's line-items grid". The
blocked(fixture) sentence is gone |
| `acceptance[0].clause` | "… a parent record page that renders the
synthesized Tasks related list …" | "… renders the Tasks related list …"
(one word dropped) |
| `acceptance[0].verify` | "At this fixture record_header, record_more
and record_related are not hosted: record those three blocked(fixture),
never as missing placements" | "The stock fixture hosts all six
(fixtures.knownGaps names the page and tab …): an expected action absent
from its host is a missing placement, never blocked(fixture)" |
| `acceptance[2].clause` | "The url (open_docs) and api
(recalc_selection) legs live on record_more and share its fixture gap:
record them blocked(fixture) with it, never as dispatch failures" | "The
url (open_docs) and api (recalc_selection) legs live on record_more,
under the ⋯ overflow of the Task Detail title bar" |
| `history` | 8 entries | plus one: revision 9, 2026-10-06, ref
`objectstack-ai#22039`, citing objectstack-ai#22030 / PR objectstack-ai#22036 |

`fixtures.knownGaps[0]` (the objectui-pinned automation) is
byte-identical. No other key and no other item changed. A structural
diff of all 43 items against `60ccda5a24` shows exactly one changed
item.

### The rewritten knownGap: where each location lives

- **`record_header` / `record_more`: Task Detail**
(`showcase_task_detail`, the record page the console opens for any
task). Its first node is a `page:header`. The header renders the action
ids it lists, each placed by its own `locations`, and the host's Edit /
Share / Delete come after them.
- Inline on a desktop viewport: `showcase_mark_done` (script, shown only
while the task is not done), `showcase_log_time` (form) and
`showcase_archive_task` (script, disabled until done).
- Under the ⋯ overflow: `showcase_open_docs` (url),
`showcase_recalc_estimate` (api) and `showcase_recalc_selection` (api).
- The default inline budget is 3 (1 on mobile), and a `record_header`
action past it folds into ⋯. Read from the `page:header` renderer at the
objectui pin `a58626c88dc8`. The knownGap names this so that a runner on
a narrow viewport does not score a folded action as an extra
`record_more` placement.
- **`record_related`: Project Detail** (`showcase_project_detail`), on
the **Related tab** (`?tab=related`). The tab holds the Tasks related
list, a `record:related_list` for `showcase_task` through its `project`
FK. As PR objectstack-ai#22036 noted, this is an authored node, not the synthesizer's
strip. The page's `tabs` override still replaces that strip, and the tab
puts the list back with no `actions` key, so its rows carry
`showcase_log_time` beside the task's `list_item` actions. The Tasks tab
next to it is the `record:line_items` grid, which hosts no
`record_related` action.
- A missing action on any of these hosts is now a missing placement, not
blocked(fixture). The knownGap says the hosts were read from objectui
source and served metadata, not from a browser render, because PR objectstack-ai#22036
left the browser render NOT MEASURED. The first run against these hosts
will be their first browser evidence.

### Scope reading, declared

The dispatch scopes edits to `fixtures.requires`, `steps` and acceptance
`verify` lines that still send the runner to a surface that does not
host the leg. Two of the lines above are acceptance `clause` texts, not
`verify` lines. Revision 7 added the gap's blocked(fixture) instructions
in five places: knownGaps[1], step 2, clause 1, clause 1's verify, and
clause 3. The item diff between `ff29410ed2` and `18c7dfd2e6` shows
them. Clause 3's last sentence parks the url / api legs by itself, so
leaving it would keep A3 parked after the lift. Clause 1's "synthesized"
sends a runner to look for a strip that the page's `tabs` override
removes. The edit to each clause is the minimum that undoes revision 7's
gap text. Nothing else in either clause changed. `fixtures.requires` is
unchanged: it describes the fleet and does not point the runner at any
surface.

## Verification (head `d99f1f57ee`, after the final commit)

- **Derived gates:** `node scripts/pm/dispatch-gates.mjs --commands
--repo objectstack-ai/objectstack` on the actual diff derived 13
commands. The list is the same at both commits. Each was run with its
exit code recorded before any pipe, and all 13 exited 0. `--ran`: "13
derived famil(ies) accounted for — 13 run, 0 NOT-MEASURED (a DERIVED
zero — all 13 recorded an exit code and none of them is 3)".
- `check:platform-checklist`: "OK — 15 areas, 276 items (272 active, 2
planned) …".
- `check:doc-authoring`, `check:cross-package-test-inputs` and
`check:nul-bytes` are green.
- On the first commit, `check:doc-formula-expressions` answered
PREREQUISITE NOT MET (exit 3: `@objectstack/formula` and
`@objectstack/lint` were unbuilt). Both were then built under
`os-verify-lock.sh` (VERDICT command-exit 0), and the gate ran green at
both commits.
- **The two checklist-reading package tests** run on the final head.
These are the only tests declared as reading these files:
`scripts/cross-package-test-inputs.mjs` names
`docs/qa/platform-checklist/areas/*.json` for plugin-auth and `docs/**`
for rest, and nothing else.
- `pnpm --filter @objectstack/plugin-auth exec vitest run --maxWorkers=2
src/checklist-refusal-envelope-consistency.test.ts`: exit 0, 1 file / 5
tests passed.
- `pnpm --filter @objectstack/rest exec vitest run --maxWorkers=2
src/meta-state-route-doc-spelling.test.ts`: exit 0, 1 file / 8 tests
passed.
- **Lint:** `eslint --no-inline-config --format json` on the changed
file returns 1 file and 0 errors. The one message is "File ignored
because no matching configuration was supplied". No block in
`eslint.config.mjs` covers `.json` under `docs/`, so this diff cannot
move any lint verdict. The full `pnpm lint` run is CI's.
- **Changeset:** 69 published workspace manifests. None has a `files[]`
entry naming `docs/qa` or the checklist (0 hits). Positive control: all
69 name `dist` (69 hits). The path is outside every package directory,
so this diff publishes nothing. Hence `skip-changeset`.
- **Typecheck / package tests:** no package source changed, so neither
is owed locally. CI's Test Core reruns plugin-auth and rest because
their declared inputs moved.

## Acceptance notes

- **The empty-locations probe cannot discriminate on two authored-list
surfaces.** The Task Detail `page:header` renders only the ids it lists
(objectui `containers.tsx`, `resolvedHeaderActions`). The
`record:quick_actions` bar is also an explicit `actionNames` list. So
the step-7 probe action, and `showcase_new_task`, are absent from the
record header, the ⋯ overflow and the quick-actions bar because those
lists leave them out. That absence is decided before any `locations`
predicate runs. The probe's verdict therefore rests on the list toolbar,
the row menu and the related-list rows. The quick-actions half predates
this card. The header half is new, because before PR objectstack-ai#22036 the header
did not exist. Not filed: this is an item-design observation, not a
runtime defect, a contract violation or an authoring trap. Carrier:
none.
- `fixtures.requires[0]` and clause 4 spell mark_done's predicate as
`'!record.done'`. The fixture's actual predicate is `has(record.done) &&
record.done != true`. `requires[0]` also leaves out
`showcase_recalc_estimate` and `showcase_archive_task`, which the header
now carries. Neither points the runner at a wrong surface, so both are
outside this card's edit rule and are left as they are. Carrier: none.
- NOT MEASURED: the browser render of the new hosts, as in PR objectstack-ai#22036. No
console bundle is built in this checkout. The knownGap says so.

## Labels

`skip-changeset`: checklist-only diff, measured above.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01VF48aw8RPG6wzDnMgp6rtw)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate tests

Projects

None yet

2 participants