Skip to content

Commit ae0ca66

Browse files
feat(spec)!: retire page:header showStar — the follow star the reference console no longer draws (#23781)
Fixes #23650 Clause-②: no (narrowing) Regen-provenance: merge of origin/main at 46ca793 plus check:generated regeneration; no source change since the reviewed head 75ddc30. ## What Retires `page:header` `showStar` (`PageHeaderProps`) through the spec-property-retirement route, per the maintainer's ruling on the card (record `6203775759`, letter A: 「A 退役 showStar (Recommended)」). Since objectstack-ai/objectui#12427 the record chip draws the follow star only where the host wires `onToggleFavorite` into the record context, and no reference host does: the record's favorites entry is the header's ⋯ menu. So `showStar: true` (the default) drew nothing and `showStar: false` hid nothing. | surface | change | | --- | --- | | `packages/spec/src/ui/component.zod.ts` | `showStar` becomes a `retiredKey()` tombstone. The prescription names the header's ⋯ menu as the favorites entry and says metadata does not control it. `recordChrome`'s describe stops promising a star. | | `packages/spec/src/conversions/registry.ts` | D2 conversion `page-header-show-star-removed` (toMajor 18, `retiredFromLoadPath`, `retiredAfter: '17.7.0'`, order 66). It strips both values from every `page:header` (region, nested `children`, slotted page slot) and is scoped by component type. | | `migrations/entries/retired-keys/18.ui__PageHeaderProps__showStar.ts` | `RETIRED_KEYS_BY_MAJOR[18]` entry | | `migrations/entries/semantic/18.page-header-show-star-retired.ts` + `STEP18_RATIONALE` fragment (order 98) | ADR-0087 D3 entry | | `packages/spec/liveness/page.json` | `[1] page:header` read-point list: `showStar` is now marked refused. `recordChrome` and `showCopyId` stay. | | generated | `authorable-surface/ui.json` (`[RETIRED]`), `authorable-defaults/ui.json` (`showStar = true` removed), `content/docs/references/ui/component.mdx` | | `.changeset/23650-page-header-show-star-retired.md` | `@objectstack/spec` minor, BREAKING, FROM → TO, ADR-0087 `registered` marker | ### Pins - **New:** `packages/spec/src/ui/page-header-show-star-retirement.test.ts` (repo project, listed in `vitest.repo-tests.json`). It covers: - the tombstone refuses `true`, `false` and `'no'` at the key with the prescription; - a not-unrecognized-keys control; - the type-level `never` (`@ts-expect-error`); - the page parse still accepts a stored header; - the D2 strip for stored and built rows, plus its type scope, idempotence and load-path retirement; - the ledger rows; - a tree-scoped absence pin over the radius the package already declares. - **Replaced pins:** `component.test.ts`'s `#6776` block pinned `showStar` default `true`, accepted `false`, and refused `'no'`. Those pins are deleted together with the key. The tombstone refusal pin `refuses \`true\` AND \`false\` at the key, with the prescription` and `a non-boolean is refused by the same tombstone, at the key` replace them, and the block keeps pinning `recordChrome` / `showCopyId`. - `packages/lint/src/validate-component-props.test.ts`: the `#6776` acceptance fixture drops `showStar`. A new case pins `component-props-invalid` + `error` + path + prescription for `true` and `false`. - `retired-key-removal-version.pin.test.ts` JOIN row added. `page-header-breadcrumb-retirement.test.ts`'s exact `PageHeaderProps:*` ledger list now includes `showStar`. ## Measurements - **Producers:** none. The search was a `git grep showStar` at `140238a47` over `examples/`, `packages/*/src`, `skills/` and `content/` (outside the spec's schema/tests, generated references and the historical `registry.ts.template` rationale). objectui at the pinned `f96f771140f1` has no producer in apps or examples either. - **objectui at the pinned `.objectui-sha` (`f96f771140f1`), for the Console Pin Gate:** - No TYPE reference: no import of `PageHeaderProps` and no `z.infer` of it. `app-shell`'s `ConsolePageHeaderProps` is its own interface. - The reads are untyped `schema?.showStar` / `schema?.properties?.showStar` in `packages/components/src/renderers/layout/containers.tsx` (around `:1581`-`:1584`), plus `RecordTitleChip`'s own React prop. - The Console Pin Gate runs objectui's `tsc` against objectui's installed `@objectstack/spec` (the `OBJECTSTACK_SPEC_DIST` hook only reaches vite). So no compile break is expected from this removal. The gate itself was not run locally. - **objectui read points for the follow-up card (the seat files it):** - `packages/components/src/renderers/layout/containers.tsx` `:1580`-`:1584` (the read), `:2616` (passes `showStar` to the chip), `:2681` (the registration's `{ name: 'showStar', type: 'boolean' }` input); - `packages/components/src/custom/RecordTitleChip.tsx` `:45`/`:63`/`:134`; - the CONTROL pin `packages/components/src/__tests__/page-header-star-host-toggle-12427.test.tsx` `:117`-`:120`; - ⚠️ `apps/console/src/__tests__/registry-inputs-spec-parity.test.ts` `:5787` lists `['page:header', 'showStar']` in `settledUpstream`. It asserts `specTopLevelKeys(type)` contains the key, and that set subtracts tombstones. That test reads objectui's installed spec, so it goes red there on the first spec bump that carries this retirement, not here. The forward direction also reds while the registration still publishes the input. - docs: `content/docs/layout/page-header.mdx:49` and `content/docs/guide/layout.md:373`. - **`sdui.manifest.json`:** still lists `showStar` as a renderer input. It is not edited by hand. `check-sdui-manifest` checks shape, tamper, staleness and pin correspondence, not spec parity, and it is green. No gate here compares manifest inputs against `ComponentPropsMap`. It updates on the pin bump after objectui drops the input. ## Verification (head `75ddc305`) - `pnpm --filter @objectstack/spec typecheck && test && test:repo`: 680 files / 20787 tests, then 63 files / 1070 tests, VERDICT command-exit 0. - `pnpm --filter @objectstack/lint typecheck` exit 0. `pnpm --filter @objectstack/lint test`: 139 files / 6772 tests, exit 0. - `pnpm --filter @objectstack/spec check:generated`: all 14 artifacts up to date. - `node scripts/pm/dispatch-gates.mjs --commands`: 111 derived families, each run with its exit code captured before any pipe, all 0. `--ran` reports 111 run, 0 NOT-MEASURED (a derived zero). - Ablation, one-time: `node scripts/ablation-replace.mjs` swapped `showStar: retiredKey(` for `showStar: z.boolean().default(true).describe(`. The new pin file went red (6 tombstone cases), then was restored byte-identically to HEAD (blob `d3db276b1322`). A first attempt was a syntax-broken mutation that ran no tests; it was discarded as void. - `main` was merged once (merge commit `75ddc305`) after the retirement commit. The one conflict was in `retired-key-removal-version.pin.test.ts`, where both sides added JOIN rows, and both were kept. Nothing needed regenerating afterwards. `scripts/pm/os-regen-merge.sh` was refused by this container's command classifier, so a plain `git merge origin/main` was used, followed by build + `check:generated`. ## Acceptance notes - `@objectstack/lint` gets no changeset: only a test file changed there, and test files do not publish. - The historical step-17 rationale in `registry.ts.template` still names `showStar` among the keys declared at 17. That text records a past step and is left as is. --- _Generated by [Claude Code](https://claude.ai/code/session_0127kfChiciCXiqVX5g7E5rJ)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 74b4362 commit ae0ca66

16 files changed

Lines changed: 738 additions & 26 deletions
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
---
2+
'@objectstack/spec': minor
3+
---
4+
5+
feat(spec)!: retire `page:header` `showStar` — the follow-star switch the reference console no longer draws (#23650)
6+
7+
**BREAKING** — `showStar` on a `page:header` component (`PageHeaderProps`) is now refused. Since objectui#12427 the header's record chip draws the follow star only where the host wires a favorites toggle into the record context, and the reference console wires none: the record's favorites entry is the header's ⋯ menu. So `showStar: true` (the default) drew nothing and `showStar: false` hid nothing. Delete the key; the ⋯ menu's favorites entry is unchanged, and metadata does not control it.
8+
9+
Clause-②: no (narrowing)
10+
11+
Measured before removal, at `140238a47`: no producer. No example app, skill, doc or package source in this repository authors the key outside the spec's own schema and tests, and neither does any app or example in objectui at the pinned `f96f771140f1`.
12+
13+
## FROM → TO
14+
15+
| you wrote (17.7 and earlier) | write instead |
16+
| --- | --- |
17+
| `showStar: true` or `showStar: false` in a `page:header`'s `properties` | nothing — delete the key; the record's favorites entry is the header's ⋯ menu |
18+
19+
**The one-line fix:** delete `showStar` from every `page:header`.
20+
21+
**What an author who still writes it sees.** `tsc` fails at a typed authoring site (`PageHeaderProps` types the key as retired), and `os validate`, `os build` and `os lint` refuse it as an `error` (`component-props-invalid`) at its own path with the prescription:
22+
23+
> `page:header` property `showStar` was removed in @objectstack/spec 18 (ADR-0087 D2) — the reference console draws no star beside the record title, so the key switched nothing: the record's favorites entry is the header's ⋯ menu, and metadata does not control it. Delete the key, whether it was `true` or `false`; the ⋯ menu's favorites entry is unchanged. 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.
24+
25+
A page's own parse keeps its component `properties` as an open bag, so an existing page carrying the key is never refused at load.
26+
27+
## The retirement kit
28+
29+
- **A `retiredKey()` tombstone** on `PageHeaderProps`, a `strictObject`, beside the `icon` and `breadcrumb` this row lost before it. `RETIRED_KEYS_BY_MAJOR[18]`: `ui/PageHeaderProps:showStar`. The authorable-surface row reads `[RETIRED]`, and the `showStar = true` default leaves the defaults baseline.
30+
- **The D2 conversion `page-header-show-star-removed`** (protocol 18, retired from the load path) deletes the key from every `page:header` — region-level, nested in a container's `children`, and in a slotted page's named slot — `true` and `false` alike, one notice per header, as a lossless delete. It is scoped by component `type`: the same key name on another component is left alone.
31+
- **The D3 entry `page-header-show-star-retired`** records the one case the conversion cannot decide: a host of your own that wires the favorites toggle now draws its star on every record header, a header that said `false` included.
32+
- **No deprecation window**, per the project's startup-stage posture.
33+
34+
**Not part of it:** the record context's favorites hook in the published objectui component library (`onToggleFavorite`) stays — this removes the metadata switch, not the host hook. The renderer's read of the key leaves on the objectui side.
35+
36+
⚠️ **The out-of-repo consumer population is NOT MEASURED.** `@objectstack/spec` is published, so this is breaking for consumers no telemetry was consulted for.
37+
38+
<!-- adr-0087: registered page-header-show-star-removed, page-header-show-star-retired -->

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1506,8 +1506,8 @@ View filter rule
15061506
| **icon** | `never` | optional | [REMOVED] `page:header` property `icon` was removed in @objectstack/spec 17.0.0 (ADR-0087 D2) — no renderer ever read it: objectui resolves `icon` only per header action (`action.icon`), never off the header's own props bag, and the component registry never published it as an input, so an authored value was accepted and dropped. Delete the key. The header's own identity is drawn by the record chrome (`recordChrome`, on by default) and each action carries its own `icon`. Run `os migrate meta --from 16` to list the mechanical edits for existing sources; `--write` applies the ones it can prove, and you apply the rest by hand. |
15071507
| **breadcrumb** | `never` | optional | [REMOVED] `page:header` property `breadcrumb` was removed in @objectstack/spec 17 (ADR-0087 D2) — no renderer ever drew a trail for it: objectui drew an empty slot and nothing filled it, and the navigation trail is drawn once, by the app shell's header. Delete the key, whether it was `true` or `false`; the shell's trail is unchanged. 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. |
15081508
| **actions** | `string[]` | optional | Action IDs the header draws, resolved against the actions declared on the page's object. An authored list is the whole set: an action that declares `record_header` (or `record_more`) in its `locations` but is not named here is not drawn, and omitting the key draws none of the object's actions. A named action that declares neither location renders nowhere. The host's own Edit / Share / Delete chrome is separate from this list. |
1509-
| **recordChrome** | `boolean` | optional (default: `true`) | Render the record chrome — the title as a record chip with its follow star and copy-id button. Set false on a non-record page (dashboard, landing) to fall back to the bare heading layout. |
1510-
| **showStar** | `boolean` | optional (default: `true`) | Show the follow (favourite) star beside the record title. Part of the record chrome — no effect when `recordChrome` is false. |
1509+
| **recordChrome** | `boolean` | optional (default: `true`) | Render the record chrome — the title as a record chip with its copy-id button. Set false on a non-record page (dashboard, landing) to fall back to the bare heading layout. |
1510+
| **showStar** | `never` | optional | [REMOVED] `page:header` property `showStar` was removed in @objectstack/spec 18 (ADR-0087 D2) — the reference console draws no star beside the record title, so the key switched nothing: the record's favorites entry is the header's ⋯ menu, and metadata does not control it. Delete the key, whether it was `true` or `false`; the ⋯ menu's favorites entry is unchanged. 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. |
15111511
| **showCopyId** | `boolean` | optional (default: `true`) | Show the copy-record-id button beside the record title. Part of the record chrome — no effect when `recordChrome` is false. |
15121512
| **maxVisible** | `integer` | optional | How many header actions render as inline buttons before the rest fold into the overflow menu (renderer default 3). |
15131513
| **mobileMaxVisible** | `integer` | optional | The `maxVisible` budget on mobile viewports (renderer default 1). |

‎packages/lint/src/validate-component-props.test.ts‎

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,25 @@ describe('validateComponentProps — undeclared keys', () => {
9393
}
9494
});
9595

96+
// #23650 — `PageHeaderProps.showStar` is a retiredKey tombstone, the
97+
// `breadcrumb` route one key over: the reference console draws no star, and
98+
// the record's favorites entry is the header's ⋯ menu. It replaces the
99+
// acceptance this file used to pin for `showStar: false` (#6776, below).
100+
it('refuses a retired page-header `showStar` as an error carrying the prescription, for `true` and `false`', () => {
101+
for (const showStar of [true, false]) {
102+
const findings = validateComponentProps(
103+
stackWith([{ type: 'page:header', properties: { title: 'T', showStar, showCopyId: false } }]),
104+
);
105+
expect(findings, `showStar: ${showStar}`).toHaveLength(1);
106+
const [f] = findings;
107+
expect(f.rule).toBe('component-props-invalid');
108+
expect(f.severity).toBe('error');
109+
expect(f.path).toBe('pages[0].regions[0].components[0].properties.showStar');
110+
expect(f.message).toContain('`page:header` property `showStar` was removed in @objectstack/spec 18');
111+
expect(f.message).toContain('the record\'s favorites entry is the header\'s ⋯ menu, and metadata does not control it');
112+
}
113+
});
114+
96115
// #21589 — an `object-master-detail-form` detail entry's `sortField` is a
97116
// retiredKey tombstone, one array level down. The same door and, since
98117
// #23026, the same refusal: an error that names the entry's key.
@@ -535,7 +554,9 @@ describe('validateComponentProps — value verdicts', () => {
535554
// objectui `apps/console/src/preview-samples.ts:68` verbatim — the
536555
// measured warning site, on a non-record page.
537556
{ type: 'page:header', properties: { title: 'Welcome to the CRM', recordChrome: false } },
538-
{ type: 'page:header', properties: { title: 'Lead', showStar: false, showCopyId: false } },
557+
// `showStar` left this shape at 18 (#23650, a tombstone now refused
558+
// below); its live sibling stays.
559+
{ type: 'page:header', properties: { title: 'Lead', showCopyId: false } },
539560
{ type: 'page:accordion', properties: { items: [], variant: 'card' } },
540561
{ type: 'page:tabs', properties: { tabStyle: 'card', items: [] } },
541562
]),

‎packages/spec/authorable-defaults/ui.json‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,6 @@
7575
"ui/PageComponent:properties = {}",
7676
"ui/PageHeaderProps:recordChrome = true",
7777
"ui/PageHeaderProps:showCopyId = true",
78-
"ui/PageHeaderProps:showStar = true",
7978
"ui/PageTabsProps:position = \"top\"",
8079
"ui/PageTabsProps:tabStyle = \"line\"",
8180
"ui/PageVariable:type = \"string\"",

‎packages/spec/authorable-surface/ui.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1108,7 +1108,7 @@
11081108
"ui/PageHeaderProps:mobileMaxVisible",
11091109
"ui/PageHeaderProps:recordChrome",
11101110
"ui/PageHeaderProps:showCopyId",
1111-
"ui/PageHeaderProps:showStar",
1111+
"ui/PageHeaderProps:showStar [RETIRED]",
11121112
"ui/PageHeaderProps:subtitle",
11131113
"ui/PageHeaderProps:title",
11141114
"ui/PageNavItem:badge",

‎packages/spec/liveness/page.json‎

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

‎packages/spec/src/conversions/registry.ts‎

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11031,6 +11031,138 @@ const pageHeaderBreadcrumbRemoved: MetadataConversion = {
1103111031
},
1103211032
};
1103311033

11034+
/**
11035+
* `page:header.showStar` leaves the contract (protocol 18, #23650 — ADR-0049
11036+
* enforce-or-remove; ruling record 6203775759, letter A: 「A 退役 showStar
11037+
* (Recommended)」).
11038+
*
11039+
* The key switched a star the reference console no longer draws. Since
11040+
* objectui#12427 the `page:header` record chip draws the follow star only
11041+
* where the host wires a favorites toggle into the record context
11042+
* (`onToggleFavorite`), and no reference host does: the record's favorites
11043+
* entry is the header's ⋯ overflow menu. `true` drew nothing and `false` hid
11044+
* nothing. The tombstone on `PageHeaderProps` refuses it for a live author:
11045+
* `os validate`, `os build` and `os lint` refuse it as an `error` with the
11046+
* prescription (the component-props gate, `@objectstack/lint`; the load path
11047+
* keeps the open `properties` bag as written).
11048+
*
11049+
* **A delete of both values.** Neither changes what the reference console
11050+
* draws, so there is no value to preserve and no rewrite target. The host
11051+
* hook is not the key: a third-party host of the published component library
11052+
* that wires `onToggleFavorite` keeps its star, and after the strip a header
11053+
* that said `false` shows it there as an absent key would. The D3 entry
11054+
* `page-header-show-star-retired` records that.
11055+
*
11056+
* Who writes the key: nobody measured. No example app, skill or doc in this
11057+
* repo, and no objectui app or example, authors it; the entry is for stored
11058+
* `sys_metadata` pages and built artifacts written by hand.
11059+
*
11060+
* ⚠️ Scoped by component `type`, never by key name, exactly as
11061+
* {@link pageHeaderBreadcrumbRemoved}: `showStar` is an ordinary name for an
11062+
* open-namespace component's own prop.
11063+
*/
11064+
const pageHeaderShowStarRemoved: MetadataConversion = {
11065+
id: 'page-header-show-star-removed',
11066+
toMajor: 18,
11067+
retiredFromLoadPath: true,
11068+
retiredAfter: '17.7.0',
11069+
surface: 'page.component.page:header.showStar',
11070+
summary:
11071+
"page:header prop 'showStar' removed, whether 'true' or 'false' (the reference console draws no star "
11072+
+ 'beside the record title: the record\'s favorites entry is the header\'s ⋯ menu, and metadata does '
11073+
+ 'not control it)',
11074+
apply(stack, emit) {
11075+
return mapPageComponents(stack, (component, path) => {
11076+
if (component.type !== 'page:header') return component;
11077+
const properties = component.properties;
11078+
if (!isDict(properties) || !('showStar' in properties)) return component;
11079+
const stripped = stripKeys(properties, ['showStar'], emit, `${path}.properties`);
11080+
return { ...component, properties: stripped };
11081+
});
11082+
},
11083+
fixture: {
11084+
before: {
11085+
pages: [
11086+
{
11087+
name: 'account_record',
11088+
regions: [
11089+
{
11090+
name: 'header',
11091+
components: [
11092+
// The default value, written out.
11093+
{ type: 'page:header', properties: { title: 'Account', showStar: true } },
11094+
// The switch turned off, beside the live copy-id switch.
11095+
{ type: 'page:header', properties: { title: 'Contract', showStar: false, showCopyId: false } },
11096+
// A header WITHOUT the key rides through untouched — the strip
11097+
// dispatches on key presence, and copy-on-write keeps the reference.
11098+
{ type: 'page:header', properties: { title: 'Overview', recordChrome: false } },
11099+
// ⚠️ The same key name on an open-namespace component that is
11100+
// NOT a page header — its own prop, not this entry's key.
11101+
{ type: 'acme:rating_badge', properties: { showStar: true } },
11102+
// The nested position (#6775): a header inside a card's
11103+
// `children` is still a page header.
11104+
{
11105+
type: 'page:card',
11106+
properties: {
11107+
title: 'Summary',
11108+
children: [{ type: 'page:header', properties: { title: 'Inner', showStar: true } }],
11109+
},
11110+
},
11111+
],
11112+
},
11113+
],
11114+
},
11115+
// The named-slot shape (#6776): a header authored into a slotted page.
11116+
{
11117+
name: 'account_record_detail',
11118+
kind: 'slotted',
11119+
regions: [],
11120+
slots: {
11121+
details: { type: 'page:header', properties: { title: 'Account', showStar: false } },
11122+
},
11123+
},
11124+
],
11125+
},
11126+
after: {
11127+
pages: [
11128+
{
11129+
name: 'account_record',
11130+
regions: [
11131+
{
11132+
name: 'header',
11133+
components: [
11134+
{ type: 'page:header', properties: { title: 'Account' } },
11135+
{ type: 'page:header', properties: { title: 'Contract', showCopyId: false } },
11136+
{ type: 'page:header', properties: { title: 'Overview', recordChrome: false } },
11137+
{ type: 'acme:rating_badge', properties: { showStar: true } },
11138+
{
11139+
type: 'page:card',
11140+
properties: {
11141+
title: 'Summary',
11142+
children: [{ type: 'page:header', properties: { title: 'Inner' } }],
11143+
},
11144+
},
11145+
],
11146+
},
11147+
],
11148+
},
11149+
{
11150+
name: 'account_record_detail',
11151+
kind: 'slotted',
11152+
regions: [],
11153+
slots: {
11154+
details: { type: 'page:header', properties: { title: 'Account' } },
11155+
},
11156+
},
11157+
],
11158+
},
11159+
// Four notices, one per stripped key: the two region-level headers, the
11160+
// nested one and the slotted one. The header without the key and the
11161+
// open-namespace component emit none.
11162+
expectedNotices: 4,
11163+
},
11164+
};
11165+
1103411166
/**
1103511167
* `page.requires` removed from the page kinds whose source is never compiled at
1103611168
* save — `react`, `full` and `slotted`, a page that omits `kind` included
@@ -16670,6 +16802,7 @@ const MAJOR_18_CONVERSIONS: readonly OrderedConversion[] = [
1667016802
{ conversion: pageComponentFilterRecordToRuleArray, order: 36 },
1667116803
{ conversion: pageComponentResponsiveRemoved, order: 13 },
1667216804
{ conversion: pageHeaderBreadcrumbRemoved, order: 49 },
16805+
{ conversion: pageHeaderShowStarRemoved, order: 66 },
1667316806
{ conversion: pageRequiresNonCompiledKindRemoved, order: 58 },
1667416807
{ conversion: permissionAdminScopeBindingAuthoringRemoved, order: 65 },
1667516808
{ conversion: permissionAllowRestorePurgeRemoved, order: 16 },
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2+
3+
// #23650 — ADR-0049 enforce-or-remove through the ADR-0087 D2 route (ruling
4+
// record 6203775759, letter A). `PageHeaderProps.showStar` switched a follow
5+
// star the reference console no longer draws: since objectui#12427 the record
6+
// chip draws it only where the host wires `onToggleFavorite`, and no reference
7+
// host does — the record's favorites entry is the header's ⋯ menu. Tombstoned
8+
// with `retiredKey()` in the `strictObject`, beside the `icon` and `breadcrumb`
9+
// this row lost before it (the surface baseline line carries `[RETIRED]`);
10+
// stored and built pages are stripped of both values by the D2 conversion
11+
// `page-header-show-star-removed`, whose D3 record is
12+
// `page-header-show-star-retired`.
13+
//
14+
// ⭐ RETIRED-DEFAULT RESIDUE: not owed, although the key carried
15+
// `.default(true)` — the `breadcrumb` reasoning, unchanged: a page parses its
16+
// component `properties` as an open bag, and this row is parsed only by the
17+
// props lint, which writes nothing back, so no released toolchain emitted
18+
// `showStar: true` into an artifact nobody authored.
19+
export const entry = 'ui/PageHeaderProps:showStar';
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2+
3+
import type { SemanticMigration } from '../../types.js';
4+
5+
// #23650 (ADR-0049 enforce-or-remove) — the D3 entry of the
6+
// `page-header-show-star-removed` family (one D3 entry per retirement family,
7+
// even when D2 is lossless). Registered key: `ui/PageHeaderProps:showStar`.
8+
// The strip changes nothing the reference console draws; what it can change
9+
// is a third-party host that wires its own favorites toggle.
10+
export const entry: SemanticMigration = {
11+
id: 'page-header-show-star-retired',
12+
// No backticks in `surface` — build-upgrade-guide.ts renders it inside a code
13+
// span AND a table cell.
14+
surface: 'page.component.page:header.showStar — the follow star beside the record title',
15+
replacement:
16+
'Nothing: delete the key, whether it was `true` or `false`. The record\'s favorites entry is the '
17+
+ 'header\'s ⋯ menu, and metadata does not control it.',
18+
reason:
19+
'The D2 conversion `page-header-show-star-removed` deletes `showStar` from every page header, and '
20+
+ 'nothing the reference console draws changes: its record chip draws the follow star only where '
21+
+ 'the host wires a favorites toggle into the record context, and the console wires none, so '
22+
+ '`true` drew nothing and `false` hid nothing. What the conversion cannot decide is a host of '
23+
+ 'your own: if it wires that favorites toggle, the star now draws on every record header, a '
24+
+ 'header that said `false` included, and hiding it there is the host\'s call, not the page\'s.',
25+
acceptanceCriteria:
26+
'No page header carries `showStar`, and the props lint reports one with the prescription. Every '
27+
+ 'record page shows the same header in the console as before the upgrade — title, copy-id '
28+
+ 'button and actions — and the record\'s favorites entry is still in the header\'s ⋯ menu.',
29+
};

0 commit comments

Comments
 (0)