Skip to content

Commit 6dbff8e

Browse files
feat(spec,ui)!: retire the record page's slots.tabs; a page that needs its own strip is kind: 'full' (#27219)
<!-- claude-projects-attribution: {"github_login":"os-support-ai"} --> _Requested by **Yibo Wang** · [project thread](https://claude.ai/code/project/chan_01BVUyyPie8gHwoPM18iABwB?thread=cmsg_01BVUyyPie8gHwoPM18iABwBMVTpWL3WoqLD4tN69q1BPz)_ Fixes #27053 ## What changes The record page's `slots.tabs` is retired under the `spec-property-retirement` kit. A slotted page now fills six slots (`header`, `actions`, `alerts`, `highlights`, `details`, `discussion`); a page that needs its own tab strip is `kind: 'full'`. **Spec (`@objectstack/spec`)** - `slots.tabs` is a `retiredKey()` tombstone carrying the card's prescription: the record page composes its tabs and columns from the object's roles and relationship flags; set `relatedList` / `relatedList: 'primary'` on the child's reference field to change which related lists appear or their order; author `slots.details` to replace the Details body; a page that needs its own strip is `kind: 'full'`. It ends with the house `os migrate meta --from 17` sentence and carries no issue ids. - The `related` / `relatedLists` aliases leave with the slot. They are now `guidance` entries that refuse by name with the same remedy, instead of being read as `tabs`. - `checkPageSlotPair` (the `details` + `tabs` refusal) and `page-slot-pair.test.ts` are removed, and so is its `@objectstack/spec/ui` export. The check-exports pin now asserts that it has zero declarations and zero attachments. - ADR-0087 D2: the `page-slots-tabs-removed` conversion (`retiredFromLoadPath`, `retiredAfter: '18.2.0'`, order 78) drops `slots.tabs` from a stored page. When `tabs` was the only slot, the page is left as an empty slotted page, which renders the synthesized default. - ADR-0087 D3: the `page-slots-tabs-retired` entry covers the judgement the conversion cannot make, which is whether such a page should become `kind: 'full'`. It absorbs `page-slots-details-beside-tabs-refused`, because the pair no longer exists. The `STEP18_RATIONALE` fragment moves with it (order 112). The retired-key entry is `ui/Page:slots.tabs`. - The JOIN row in `retired-key-removal-version.pin.test.ts`, the liveness `slots` note, and the regenerated api-surface, export-origins and references. **The four in-repo producers, rebuilt as `kind: 'full'`.** Each rebuild is faithful: the same nodes in the order the synthesizer drew them (header, then the actions/alerts slot, highlights, the strip, then discussion). Nothing is redesigned. Header actions are the object's `record_header` / `record_more` actions in declaration order, which is exactly what the synthesized header drew; the host still appends Edit/Share/Delete. | Page | Package | Body | |:--|:--|:--| | `sys_organization_detail` | `@objectstack/platform-objects` | `page:header` (4 actions) · `record:highlights` (`slug`) · Members / Invitations / Teams strip (unchanged) · `record:discussion` | | `sys_user_detail` | `@objectstack/platform-objects` | `page:header` (11 actions) · the `record:alert` from its `alerts` slot · highlights · its curated strip · no discussion (its `discussion: []`) | | `sys_approval_request_detail` | `@objectstack/plugin-approvals` | `page:header` · `record:approval_decision` · highlights · Details / Timeline strip · no discussion (its `discussion: []`) | | `showcase_project_detail` | `examples/app-showcase` (private) | `page:header` · highlights · Details / Tasks grid / Related strip · `record:discussion` | **Console (`@object-ui/*`)** - `buildDefaultPageSchema` composes from the six slots only, and its tabs branch is removed. - `RecordDetailView`'s `pageColumnsEligible` drops the `slots.tabs` arm, so the one gate line is now `!assignedPage && !namedPageBlocked`. - `@object-ui/types` removes both `.superRefine(checkPageSlotPair)` attachments and types `PageSlotMap.tabs` as `never`. - The Studio slot canvas (`SLOT_ORDER`) lists six slots. **Docs** - `ui/content/docs/guide/slotted-pages.md`: the slot menu no longer has `tabs`, and the guide says how to shape the strip instead. - `content/docs/ui/pages.mdx` (merged from main during this branch): the same change. - The generated references. - `skills/objectstack-ui/rules/pages.md`: one bullet. It is paid for by a deletion in the same file (−23 tokens net). **ADR-0133 and QA** - ADR-0133's open-question correction gains the inline one-line amendment: the mechanism moves from slotted to full, and the strip does not. - The `identity-auth.json` source pin moves with it (revision 6). - `studio-authoring.json` follows the retired pair: the step now PUTs `slots.tabs`, which is refused with `invalid_type`. The pin file, the source anchor and revision 4 are updated to match. ## Why An authored `tabs` slot replaced the whole synthesized strip. A page that used it lost every related list, approval, attachment and history entry the platform composes from the object, and stayed a tab strip at every width instead of taking the record page's columns. The maintainer ruled the slot out on epic #26676; the card carries that ruling. ## Acceptance notes - **Correction to the card's premise.** The card assumed no producer used `slots.tabs` ("HotCRM never uses tabs"). Re-measured on main at `0f1045bc`, four in-repo pages authored it: the table above. Seat 1 confirmed moving all four to `kind: 'full'` with identical behaviour. - HotCRM's own account page could not be measured from this session: the hotcrm repo is outside this session's scope. If it authors no `tabs`, it parses unchanged. If it does, its stored row is converted on read by the D2 conversion, and its source is listed by `os migrate meta --from 17`. - **The tombstone.** `safeParse` of `tabs` / `related` / `relatedLists` fails with the prescription, and the six live slots parse. `page-slots-tabs-retirement.test.ts` has 18 cases: - tombstone refusal for one component, an array and empty; - the alias guidance; - the six live slots; - a full page with `page:tabs` parses; - no `#` and the migrate sentence; - `@ts-expect-error` on `definePage`; - the barrel no longer exports `checkPageSlotPair`. - **Stored rows.** A stored slotted page with `tabs` replays through the conversion: `applyConversionsToStoredItem('page', …)`, the tabs-only page becomes `slots: {}`, the conversion is idempotent, and it is retired from the load path. The registry fixture has three pages and expects two notices. - **Absence pin.** A tree-scoped absence pin (`packages`, `examples`, `skills`, `content`, `scripts`) refuses a `slots` frame holding `tabs`, `related` or `relatedLists` in `.ts`, `.json`, `.yaml` and `.md` fences. It excludes the conversion fixtures, the json-schema output, release notes and changelogs. - **Fixtures and tests moved.** Each one now uses a live slot (`details`) or reads the full page's region: - **spec:** `i18n-resolver.test.ts` (two fixtures), `page-component-filter-record-to-rule-array.test.ts`, `object-refinement-check-exports.test.ts`, `retired-key-removal-version.pin.test.ts` (new JOIN row). `page-slot-pair.test.ts` is deleted. - **lint:** `page-walk`, `validate-page-field-bindings`, `validate-visibility-predicates` and `page-envelope-audit` tests. - **cli:** `i18n-section-coverage` (its fixture is now a `kind: 'full'` page whose region authors its own `page:tabs`, keeping the nesting under test), `platform-page-i18n-parity` (the boundary pin now expects `kind: 'full'`, no `slots`, every component reached, and none addressed). - **platform-objects:** `sys-user-details-tab`, `sys-user-security-tab-own-record`, `sys-user-two-factor-display`, `setup-nav-per-user-stores` and `canonical-expression-envelopes` (the control's path is now `regions[0].components[N].visibleWhen`); new `record-page-header-actions.test.ts` (round 2, review should-fix ④.1: for `sys_organization_detail` and `sys_user_detail`, the `page:header` action ids equal the object's `record_header` ∪ `record_more` declarations in declaration order, with a non-vacuous guard); comments in `invite-entry-toolbar.test.ts` and `sys-member.object.ts`. - **plugin-approvals:** `sys-approval-request-page.test.ts`, which pins the page body in order. - **app-showcase:** comments in `task-detail.page.ts` and `record-action-location-hosts.test.ts`. - **ui:** `buildDefaultPageSchema.test.ts` (a stray `tabs` key is ignored and `details` applies), `recordPageColumns.test.ts`, `slots-canvas.test.ts`, `PagePreview.test.tsx`, `spec-object-refinements-7715.test.ts` (the retired slot is refused at both objectui doors), `imported-defaults-8317.test.ts`, and the ten console served-approvals-page copies (`approvalRequestsDataSource.servedPage*.test.tsx`, `ApprovalsInboxPage.opensServedPage-12484.test.tsx`), which now copy the full page whole. ## Verification Head `ca5556e06`, with main merged at `b322b5da` (11 commits, no conflicts). Runs below are local, under `os-verify-lock`. - **spec:** `--project local` has 22219 passed. The 3 reds from the first run were fixed and their files re-run green (166 passed): a conversion fixture moved to `details`, the new JOIN row, and the export-origins regeneration. `test:repo` has 82 files / 1343 passed, including the tree-scoped absence pin, which is now listed in `vitest.repo-tests.json`. - **Package suites:** - platform-objects: 96 files / 1377 passed. - plugin-approvals: 105 files / 1331 passed. - lint: 7522 passed. - cli: the three touched files, 59 passed. - plugin-auth: `organization-record-page-option`, 3 passed. - **ui:** - `plugin-detail`: 2941 passed. - Every ui test that authors a slot map or the served approvals page (19 files): 319 passed. - `RecordDetailView` record-page tiers and named-page tests: 13 passed. - `PagePreview`: 8 passed. - **Typecheck:** `turbo run typecheck` over spec, platform-objects, plugin-approvals, lint, cli, example-showcase, `@object-ui/types`, `plugin-detail`, `app-shell` and console: 104 of 104 tasks pass. This includes the test-layer typecheck ledgers. - **Derived gates:** `dispatch-gates --commands` lists 126 commands, and 121 exit 0. Of the other five: - `check:role-word` and `check:cross-package-test-inputs` were real findings. Both were fixed and now exit 0. - `check-plugin-teardown-shape --self-test` exited 3 because the clone is shallow. After fetching its pinned fixture commit it passes all 48 cases. - `check:skill-examples` and `check:dual-build-cjs-loads` exit 3 because they need a full repo build. That is a prerequisite miss, and nothing was measured. - **Spec artifacts:** `check:generated --fix` regenerated three artifacts (api-surface, export-origins and references), and each re-checks green. `check-platform-checklist`, `check-skills-token-ratchet` (`pages.md` 5669/5692) and `check-adr-0087-registration --base origin/main` are green. Clause-②: no (narrowing) Metadata-protocol: narrowed Skills-docs: follow-up #27059 _Keep one value on each of the two lines above, each line starting with its key. [Human Minimum](https://github.com/objectstack-ai/objectstack/blob/main/CONTRIBUTING.md#human-minimum) item 5 says what they mean._ The changeset carries `Clause-②: no (narrowing)`, `Metadata-protocol: narrowed`, the BREAKING banner, a **FROM → TO.** line and `<!-- adr-0087: registered page-slots-tabs-removed, page-slots-tabs-retired -->`. The packages bump minor: spec, platform-objects, plugin-approvals, `@object-ui/types`, `@object-ui/plugin-detail`, `@object-ui/app-shell` and skills. Governed surfaces (`docs/adr/**`, `skills/**`, Tier H): this PR is a draft and is not queued or armed. It needs a governed approver's review before the seat lands it. ## Human Minimum Each item is one rule from [CONTRIBUTING.md › Human Minimum](https://github.com/objectstack-ai/objectstack/blob/main/CONTRIBUTING.md#human-minimum), which links the AGENTS.md text that states it. - [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic. - [x] 2. Every name I added uses the casing for its kind. - [x] 3. No formatter ran; `pnpm lint` passes. - [x] 4. One issue, one branch off an up-to-date `main`, one pull request. - [x] 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset. - [x] 6. `packages/spec` touched: its generated artifacts are regenerated and committed. - [x] 7. No hand edits to `content/docs/references/`, `packages/*/CHANGELOG.md` or `content/docs/releases/`. - [x] 8. Title, body and comments are in English. - [x] The *Before Submitting* list in CONTRIBUTING.md is done. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01WGufzff3QiP6kmtajVnKnq --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 5b5cff1 commit 6dbff8e

72 files changed

Lines changed: 2366 additions & 1717 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
'@objectstack/spec': minor
3+
'@objectstack/platform-objects': minor
4+
'@objectstack/plugin-approvals': minor
5+
'@object-ui/types': minor
6+
'@object-ui/plugin-detail': minor
7+
'@object-ui/app-shell': minor
8+
'@objectstack/skills': minor
9+
---
10+
11+
feat(spec,ui)!: the record page's `slots.tabs` is retired — a slotted page fills six slots, and a page that needs its own tab strip is `kind: 'full'`
12+
13+
Clause-②: no (narrowing)
14+
15+
Metadata-protocol: narrowed (`page.slots.tabs` is a tombstone that refuses every value; `slots.related` / `slots.relatedLists` are refused by name instead of being read as `tabs`)
16+
17+
<!-- adr-0087: registered page-slots-tabs-removed, page-slots-tabs-retired -->
18+
19+
**BREAKING** accept-set narrowing on the record page (`@objectstack/spec`), with the console following it in the same release.
20+
21+
The record page composes its tabs and columns from the object's roles and relationship flags. An authored `tabs` slot replaced that whole strip, so a page that used it lost every synthesized related list and its wide-screen columns. The slot is gone; the six live slots (`header`, `actions`, `alerts`, `highlights`, `details`, `discussion`) are unchanged.
22+
23+
- **FROM → TO.** `slots: { tabs: … }` on a `kind: 'slotted'` page → delete the key. To change which related lists appear or their order, set `relatedList` / `relatedList: 'primary'` on the child's reference field; to replace the Details body, author `slots.details`; a page that needs its own strip is `kind: 'full'` and authors its header, highlights, `page:tabs` strip and `record:discussion` in `regions`, drawn as authored.
24+
- **The one-line fix.** `os migrate meta --from 17` lists the edit for existing sources, and `--write` applies it. A stored `sys_metadata` page is converted when it is read (the `page-slots-tabs-removed` conversion): the slot is dropped, and a page whose only slot it was becomes an empty slotted page, which renders the synthesized default. Whether such a page should become `kind: 'full'` is a judgement the conversion cannot make; the upgrade guide entry `page-slots-tabs-retired` lists it.
25+
- **What the parse says.** `slots.tabs` is refused with its prescription ("`page.slots.tabs` was removed in @objectstack/spec 18.3.0 (ADR-0085) — …") at `defineStack` as `STACK_SCHEMA_INVALID` / 422, and at the metadata save door. `slots.related` and `slots.relatedLists` are refused by name with the same remedy. TypeScript rejects `tabs` in a `definePage` slot map.
26+
- **The pair check.** `checkPageSlotPair` (the `details` + `tabs` refusal) is removed from `@objectstack/spec/ui` with the slot it paired; the `page-slots-details-beside-tabs-refused` upgrade entry is folded into `page-slots-tabs-retired`.
27+
28+
The four in-repo pages that authored `slots.tabs` are rebuilt as `kind: 'full'` pages that render the same nodes in the same order: `sys_user_detail` and `sys_organization_detail` (`@objectstack/platform-objects`), `sys_approval_request_detail` (`@objectstack/plugin-approvals`) and the showcase `showcase_project_detail`.
29+
30+
The console (`@object-ui/*`): `buildDefaultPageSchema` composes from the six slots only, and `RecordDetailView` lays every slotted page out by width (three columns from 1280 px), since none can opt out with an authored strip any more. `@object-ui/types` drops the pair check from its page schemas and types `PageSlotMap.tabs` as `never`. The Studio slot canvas lists six slots.
31+
32+
`@objectstack/skills`: the UI skill says a slotted page has no `tabs` slot and how to shape the strip instead.

‎content/docs/references/ui/page.mdx‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,18 @@ View filter rule
294294
| **repeatTableHeaders** | `boolean` | optional | Page-break hint: repeat a table's column headings at the top of every sheet the table runs onto (`thead { display: table-header-group }`). On when omitted. Applied by the console's browser print rendering. |
295295
| **avoidBreakInside** | `boolean` | optional | Page-break hint: keep each block whole on one sheet when it fits, moving it to the next sheet instead of splitting it (`break-inside: avoid` on every block). Off when omitted. Applied by the console's browser print rendering. |
296296

297+
### Nested Shape: `Page.slots`
298+
299+
| Property | Type | Required | Description |
300+
| :--- | :--- | :--- | :--- |
301+
| **header** | `{ type: Enum<'page:header' \| 'page:footer' \| 'page:sidebar' \| 'page:tabs' \| 'page:accordion' \| …> \| string; id?: string; label?: string \| Record<string, string>; properties?: Record<string, any>; … } \| { type: Enum<'page:header' \| 'page:footer' \| 'page:sidebar' \| 'page:tabs' \| 'page:accordion' \| …> \| string; id?: string; label?: string \| Record<string, string>; properties?: Record<string, any>; … }[]` | optional | |
302+
| **actions** | `{ type: Enum<'page:header' \| 'page:footer' \| 'page:sidebar' \| 'page:tabs' \| 'page:accordion' \| …> \| string; id?: string; label?: string \| Record<string, string>; properties?: Record<string, any>; … } \| { type: Enum<'page:header' \| 'page:footer' \| 'page:sidebar' \| 'page:tabs' \| 'page:accordion' \| …> \| string; id?: string; label?: string \| Record<string, string>; properties?: Record<string, any>; … }[]` | optional | |
303+
| **alerts** | `{ type: Enum<'page:header' \| 'page:footer' \| 'page:sidebar' \| 'page:tabs' \| 'page:accordion' \| …> \| string; id?: string; label?: string \| Record<string, string>; properties?: Record<string, any>; … } \| { type: Enum<'page:header' \| 'page:footer' \| 'page:sidebar' \| 'page:tabs' \| 'page:accordion' \| …> \| string; id?: string; label?: string \| Record<string, string>; properties?: Record<string, any>; … }[]` | optional | |
304+
| **highlights** | `{ type: Enum<'page:header' \| 'page:footer' \| 'page:sidebar' \| 'page:tabs' \| 'page:accordion' \| …> \| string; id?: string; label?: string \| Record<string, string>; properties?: Record<string, any>; … } \| { type: Enum<'page:header' \| 'page:footer' \| 'page:sidebar' \| 'page:tabs' \| 'page:accordion' \| …> \| string; id?: string; label?: string \| Record<string, string>; properties?: Record<string, any>; … }[]` | optional | |
305+
| **details** | `{ type: Enum<'page:header' \| 'page:footer' \| 'page:sidebar' \| 'page:tabs' \| 'page:accordion' \| …> \| string; id?: string; label?: string \| Record<string, string>; properties?: Record<string, any>; … } \| { type: Enum<'page:header' \| 'page:footer' \| 'page:sidebar' \| 'page:tabs' \| 'page:accordion' \| …> \| string; id?: string; label?: string \| Record<string, string>; properties?: Record<string, any>; … }[]` | optional | |
306+
| **tabs** | `never` | optional | [REMOVED] `page.slots.tabs` was removed in @objectstack/spec 18.3.0 (ADR-0085) — an authored strip replaced the whole synthesized one, so the related lists, approvals, attachments and history the platform composes from the object were dropped unless re-added by hand, and the page stayed tabbed at every width instead of taking the record page's columns. Delete the key. The record page composes its tabs and columns from the object's roles and relationship flags: to change which related lists appear or their order, set `relatedList` / `relatedList: 'primary'` on the child's reference field; to replace the Details body, author `slots.details`; a page that needs its own strip is `kind: 'full'`, drawn as authored. Run `os migrate meta --from 17` to list the mechanical edits for existing sources; `--write` applies the ones it can prove, and you apply the rest by hand. |
307+
| **discussion** | `{ type: Enum<'page:header' \| 'page:footer' \| 'page:sidebar' \| 'page:tabs' \| 'page:accordion' \| …> \| string; id?: string; label?: string \| Record<string, string>; properties?: Record<string, any>; … } \| { type: Enum<'page:header' \| 'page:footer' \| 'page:sidebar' \| 'page:tabs' \| 'page:accordion' \| …> \| string; id?: string; label?: string \| Record<string, string>; properties?: Record<string, any>; … }[]` | optional | |
308+
297309

298310
---
299311

‎content/docs/ui/pages.mdx‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -207,13 +207,14 @@ synthesizer fills in the rest. A slotted page authors `slots`, not `regions`:
207207
| `alerts` | The alert banners above the body |
208208
| `highlights` | The highlights strip |
209209
| `details` | The body of the Details tab; the other tabs stay synthesized |
210-
| `tabs` | The whole tab strip, to add, remove or reorder tabs |
211210
| `discussion` | The discussion panel |
212211

213212
Each slot takes one component or an array of them, and replaces its part whole: there is no
214-
deep merge. `details` and `tabs` are refused together, because the Details tab lives inside
215-
the strip `tabs` replaces; to replace both, put `record:details` in the first `tabs` item
216-
and author no `details` slot. A slotted `tabs` strip stays a strip at every width.
213+
deep merge. There is no `tabs` slot: the tab strip is composed from the object's fields and
214+
relationship flags. To change which related lists appear or their order, set `relatedList` /
215+
`relatedList: 'primary'` on the child's reference field; a page that needs its own strip is
216+
`kind: 'full'`, drawn as authored. `slots.tabs`, `slots.related` and `slots.relatedLists` are
217+
refused by name, and `os migrate meta --from 17` drops a stored `slots.tabs`.
217218

218219
{/* os:check */}
219220
```typescript

‎docs/adr/0133-org-management-open-basics.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,12 @@ resolved here.
260260
(`packages/plugins/plugin-auth/src/auth-plugin.ts#pages`). That file was
261261
already in the tree at `77781151d`, the very commit cited above.
262262

263+
> **Amended (2026-10-28) — the mechanism moves from slotted to full; the
264+
> strip does not.** The `tabs` slot retired (#27053), so
265+
> `SysOrganizationDetailPage` is now a `kind: 'full'` page that authors the
266+
> same Members, Invitations, Teams strip in its own `page:tabs` node, with
267+
> the header, highlights and discussion the slotted page rendered.
268+
263269
⭐ **What the measurement missed, stated so it transfers.** The search was
264270
exhaustive over the wrong space: scoped to `src/identity/` and to the
265271
`relatedList` key, while the declaration lives one directory over, in

‎docs/qa/platform-checklist/areas/identity-auth.json‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -846,7 +846,7 @@
846846
"title": "Setup Organization page resolves the active org and drives member/invitation/team management through the better-auth org endpoints — non-admins refused server-side",
847847
"since": "v17",
848848
"status": "active",
849-
"revision": 5,
849+
"revision": 6,
850850
"priority": "P1",
851851
"surface": "mixed",
852852
"personas": ["tenant admin / org owner", "a target org member", "a non-admin org member (forger)"],
@@ -921,7 +921,7 @@
921921
"traps": ["wrong-persona", "dispatcher-vs-hono-route", "hydration-race"],
922922
"source": [
923923
"packages/platform-objects/src/apps/setup-nav.contributions.ts#nav_organization (nav_organization recordId {current_org_id}, cloud ADR-0081 D3; Teams/Invitations always mounted per cloud ADR-0081 D1)",
924-
"packages/platform-objects/src/pages/sys-organization.page.ts#SysOrganizationDetailPage (WHERE THE THREE TABS COME FROM: a kind: 'slotted' record page whose slots.tabs override replaces the synthesized tab strip outright, giving exactly Members / Invitations / Teams with Members at index 0. Handed to the runtime by plugin-auth's pages: [...]. NOT the Field.relatedList: 'primary' prominence key — no identity object declares that key, and it would not move this page while the tabs slot is present. A grader who finds a Details + stacked Related strip instead is looking at the synthesized default, i.e. this page failed to load)",
924+
"packages/platform-objects/src/pages/sys-organization.page.ts#SysOrganizationDetailPage (WHERE THE THREE TABS COME FROM: a kind: 'full' record page that authors its own page:tabs strip, drawn as authored with no synthesized strip, giving exactly Members / Invitations / Teams with Members at index 0 (a kind: 'slotted' page whose slots.tabs override replaced the synthesized strip until that slot retired, #27053). Handed to the runtime by plugin-auth's pages: [...]. NOT the Field.relatedList: 'primary' prominence key — no identity object declares that key, and it would not move this full page. A grader who finds a Details + stacked Related strip instead is looking at the synthesized default, i.e. this page failed to load)",
925925
"packages/plugins/plugin-auth/src/auth-route-ledger.ts (organization family: update-member-role, remove-member, update, create-team, add-team-member, list-members/teams/invitations, get-active-member, get-full-organization)",
926926
"packages/spec/src/identity/membership-role.ts#BUILTIN_MEMBERSHIP_ROLES (BUILTIN_MEMBERSHIP_ROLES / BUILTIN_MEMBERSHIP_ROLE_OPTIONS — THE role vocabulary: owner/admin/delegated_admin/member, ADR-0108; 'nothing widens these at boot any more')",
927927
"docs/adr/0108-membership-grade-is-not-a-capability-channel.md (why the list is closed: a grade decides what you can REACH, never a bundle of what you may do)",
@@ -939,7 +939,8 @@
939939
"date": "2026-10-19",
940940
"change": "steps[5] and acceptance[3].clause corrected — a checklist-accuracy finding of the 18.0 QA sweep (QA run #24538, identity-auth.org-membership-team-management; wave anchor #24579 §3): after a rename the step expected the nav label to follow, but nav_organization's label is the static 'Organization' (setup-nav.contributions.ts); what follows is the organization record page's title, which the run observed. Not a loosening: the persisted name and the page title must both show the rename",
941941
"ref": "#24632"
942-
}
942+
},
943+
{ "revision": 6, "date": "2026-10-28", "change": "PROVENANCE, no clause change: the record page's tabs slot retired (#27053), so SysOrganizationDetailPage moved from kind: 'slotted' with a slots.tabs override to kind: 'full', rebuilt as the header, highlights, the same Members / Invitations / Teams strip and the discussion the slotted page rendered. source[1] now names the full page; the tab order, steps, acceptance clauses, oracles and negatives are unchanged (ADR-0133 carries the matching amendment)", "ref": "#27053" }
943944
]
944945
},
945946
{

0 commit comments

Comments
 (0)