Commit 36ad321
Fixes #21015
Clause-②: no (narrowing)
## What this does
Release 2 of objectui#7450's ruling B. `ElementTextPropsSchema.variant`
(an `element:text` page component's `properties.variant`,
`packages/spec/src/ui/component.zod.ts`) is now exactly the nine values
`ui:text` publishes: `h1`-`h6`, `body`, `caption`, `overline`. The two
pre-convergence spellings `heading` and `subheading` are **named
refusals** through the generic value-level mechanism
`enumWithRetiredValues` (`shared/retired-key.ts`, the helper #17109
added; no one-off refinement on this enum). Each refusal carries the
ruled hint: `heading` → `h2`, `subheading` → `h3`, or the level the page
means. `.optional().default('body')` is kept byte for byte, so an absent
`variant` still parses to `body`.
The rulings this executes, quoted on the card body (objectui#7450 ruling
B, `5565743592`, maintainer 「其他同意」, routed at `5599600566`):
> "**B — split across two releases.** spec widens to the nine **first**
(additive, nothing refused), objectui converges on that released pin,
and `heading`/`subheading` are retired in a **later** spec release once
out-of-repo authors have had a window."
> "`heading` and `subheading` become **named refusals** carrying the
migration hint (`heading` → `h2`, `subheading` → `h3`, or pick the level
you mean)."
Triage set the window at `5923829846`: 17.6.0 is the one full release in
which both vocabularies parse. The PM's unlock `5969419957` measured
17.6.0 published at 2026-10-02T03:03Z.
### The ADR-0087 disposition: stored rows convert, authors are refused
This came out of the playbook and its precedents. It was not a guess,
and they left no real fork:
- **D2 conversion `element-text-variant-heading-levels`**
(`conversions/registry.ts`, step 18, `retiredFromLoadPath: true`,
`retiredAfter: '17.6.0'`) rewrites `heading` → `h2` and `subheading` →
`h3` on every `element:text` page component it reaches through
`mapPageComponents` (regions, named slots, container nesting). This is
the shape of the two value-level page-prop retirements already in step
18, `record-chatter-position-vocabulary` and
`form-layout-inline-grid-to-vertical`: an enum refuses the old value at
parse, and a load-path-retired rewrite replays it over stored rows. The
analytics precedent (`cube-metric-expression-types-retired`) has no D2
only because "no rewrite can say which aggregate the author meant". Here
the ruling names the rewrite.
- **What the rewrite keeps.** I measured the `element:text` renderer at
the `.objectui-sha` pin `89cad75d55` (`renderers/basic/elements.tsx`,
`VARIANT_TAG` / `VARIANT_CLASS`). `heading` drew an h2 element and
`subheading` an h3 element, so the rewrite keeps the heading element and
the document outline. It does not keep the size: `heading` drew `h3`'s
class and `subheading` a medium-weight `text-lg`, while `h2` and `h3`
draw their own, larger classes. The prescriptions, the D3 entry and the
changeset all say this.
- **D3 semantic entry
`element-text-variant-heading-subheading-retired`** (`conversionIds:
['element-text-variant-heading-levels']`) holds the judgement the chain
cannot make: whether the rewritten level is the one the page means.
- **A `STEP18_RATIONALE` fragment** at the id's sort position, order 68.
There is no `RETIRED_KEYS_BY_MAJOR` row, because no key retired.
- **Changeset**
`.changeset/21015-element-text-variant-heading-retired.md`:
`@objectstack/spec` minor, `@objectstack/platform-objects` patch, a
BREAKING banner, the FROM → TO table, this PR's `Clause-②` line and the
ADR-0087 `registered` marker.
### Measured at the doors
These come from a one-off probe on this branch. No probe file is
committed.
- **Authoring door.** `validateComponentProps` is the component-props
rule that `os validate` / `os build` / `os lint` run. It is advisory,
and its tier is unchanged. On a page with one `subheading`, one
`heading` and one `h3` it returns 2 findings, both
`component-props-invalid` at
`pages[0].regions[0].components[N].properties.variant`. Each carries the
full prescription, for example: "`subheading` was removed from
`element:text` `variant` (`ElementTextPropsSchema.variant`) in
@objectstack/spec 17.7.0 — … Write `h3` — the heading element
`subheading` always rendered, now drawn in the `h3` style — or the level
the page outline means. Run `os migrate meta --from 17` to list the
mechanical edits for existing sources; apply them by hand. (received
"subheading")". The `h3` node draws no finding.
- **Stored-row seam.** `applyConversionsToStoredItem('page', …)` on the
same page gives `h3,h2,h3`.
- **Authoring funnel.** `normalizeStackInput` on the same page gives
`subheading,heading,h3`. The conversion is retired from the load path,
so authors are refused, never silently rewritten.
- **`tsc`.** Both members are gone from `z.input` of the schema. The two
`@ts-expect-error` lines in `component.test.ts` are live: the file is in
`tsconfig.test.json`'s program (`tsc --listFilesOnly`), and
`check:test-typecheck` is OK with `component.test.ts` absent from the
debt ledger.
### Producers moved
The tree-wide sweep was run with a control word. On `main` at the base
`6c5697dff`, `variant:` with `heading`/`subheading` in authoring shape
gave 5 sites outside `packages/spec`. The control `variant:` with
`h3|body|caption` gave 9 in the same files. In objectui `main`
`6f5719e1c` the count was 0, against a control of 8 for `h2|h3`.
- `packages/platform-objects/src/pages/sys-user.page.ts`: the four
Security-tab section headings (`:368`, `:401`, `:434`, `:467`) move
`subheading` → `h3`. Only those four values changed. This is the
cross-lane `domain:engine` package, and the PM posts the declaration.
- `examples/app-showcase/src/ui/pages/page-variables.page.ts:90`:
`detail_heading` moves `subheading` → `h3`.
- Fixtures were triaged as the playbook says. Two were respelled:
`component.test.ts` "should accept full text props" and `page.test.ts`
"Page end-to-end" now use `h2`. One was replaced whole: the release-1
pin "release 1 refuses nothing — %s is still accepted" pinned exactly
the branch this PR removes, and is now the refusal pins below.
### Pins
- `component.test.ts`: the nine are accepted. Each retired spelling is
refused with exactly one issue, `code` `invalid_value`, `path`
`['variant']`, the prescription's first sentence (FROM, version) and the
`Write \`h2\`` / `Write \`h3\`` hint, ending in the pinned `os migrate
meta` sentence. The `ComponentPropsMap['element:text']` row refuses the
same way. A never-legal value keeps zod's own message. Schema refusals
carry no ADR-0112 `status`, which belongs to the API error surface, so
the agent-retirement pins' `code` + `path` set is followed.
- `element-text-variant-heading-retirement.test.ts` is a new
**tree-scoped absence pin** in the `repo` project, registered in
`vitest.repo-tests.json`, inside the radius `@objectstack/spec` already
declares. It has an anti-vacuity battery and three structural
exclusions, each with its reason.
### Reverse verification
All three runs used `scripts/ablation-replace.mjs` from the committed
state. Each mutation landed by anchor count and blob, and each restore
was proven `blob == HEAD` with `git diff HEAD` empty. In every case the
direction was **turned red**, as expected.
1. The enum was reverted to a plain `z.enum` of eleven
(`component.zod.ts` blob `c0882cf1f3c7` → `b31a88f86812`).
`component.test.ts` failed 4 and passed 363: both refusals, the
`ComponentPropsMap` row and the parse half of the tsc case.
2. The conversion's `subheading` arm was dropped (`registry.ts`
`2d89e2846e32` → `14b1aa524be6`). The conversion suite failed 2 and
passed 443: "fixture.before → fixture.after via the chain" and "emits 4
notice(s)".
3. The showcase producer was put back to `subheading`. The absence pin
failed 1 and passed 1, naming
`examples/app-showcase/src/ui/pages/page-variables.page.ts`.
### Tests
Post-merge readings are at `442d5a8625`, after merging `origin/main`
`9a4182a752` through `scripts/pm/os-regen-merge.sh`. That brought in
#21565's own step-18 entries; both sides' entries are present, and the
spec was rebuilt before `check:generated` came back "All 15 generated
artifacts are up to date".
| package | command | reading (all at `442d5a8625`) |
| --- | --- | --- |
| `@objectstack/spec` | `vitest run --project local` | 608 files, 18017
passed, 1 todo |
| `@objectstack/spec` | `vitest run --project repo`, in chunks | 52
files, 881 passed |
| `@objectstack/lint` | `vitest run` | 119 files, 5620 passed |
| `@objectstack/platform-objects` | `vitest run` | 59 files, 949 passed
|
| `@objectstack/example-showcase` | `vitest run` | 31 files, 394 passed
|
| all four | `typecheck` | exit 0 (spec: `check:test-typecheck` OK, 52
ledgered files, `component.test.ts` not among them) |
Generated artifacts: only `content/docs/references/ui/component.mdx`
moved (the enum drops the two values). `authorable-surface/`,
`api-surface/`, `json-schema.manifest/` and `api-surface-signatures` are
byte-identical, as `spec-property-retirement` §2 predicts for a
value-level narrowing. Major 18 is not yet projected into
`spec-changes.json` or the upgrade guide, and
`form-layout-inline-grid-to-vertical` is absent there too.
Gates: `dispatch-gates.mjs --commands --repo objectstack-ai/objectstack`
derives 118 commands at `442d5a8625`. All 118 ran with `exit 0`. The
`--ran` reconciliation reads "118 derived, 118 run, 0 NOT-MEASURED, 0
UNRUN", a derived zero with every exit code recorded.
### Narrowings declared
- **Lint.** `eslint --no-inline-config --format json` ran on the 9
changed `.ts` files: 9 files, 0 errors, 0 warnings, 0 ignored.
Population: those are all the `.ts` paths in `git diff --name-only`
against the merge base, and eslint's own config lints each one (no
file-ignored message). Invariance: `eslint.config.mjs` never enables
type-aware linting (no `parserOptions.project`, no typed rules, as its
own comment states), so this diff cannot move a verdict on an untouched
file. The repo-wide `pnpm lint` is left to CI.
- **`packages/cli` integration tier** was not run locally. This diff
touches no CLI file, and that tier is left to CI.
## Acceptance notes
- **The interim seam the card already carries.** objectui's
`element:text` renderer, its registry `inputs` enum, the html tier
compiled from those inputs and the published `sdui.manifest.json`
(committed here at the root) still accept `heading` / `subheading`.
Until objectui installs this release, a source-authored html/jsx page
can write them past the manifest-driven JSX gate, and the
component-props rule does not walk source-authored pages. The stored
region cache is still rewritten by the conversion at rehydration. The
card body names the carrier: the one-line objectui card, filed when this
release is installable, after which `registry-inputs-spec-parity` holds
the two sides together. That card is not filed here.
- **The authoring refusal is advisory at the CLI door.**
`component-props-invalid` is a warning tier (`validateComponentProps`,
`tier: 'advisory'`), and page component `properties` are not parsed on
the save path. Both are pre-existing and unchanged here, and the D3
entry states them.
- **Out-of-repo author population is NOT MEASURED.** `@objectstack/spec`
is published, and tenant-authored pages were not measured.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01T9u38rswFp5Rw8DswRUReJ)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 5c9138b commit 36ad321
12 files changed
Lines changed: 597 additions & 53 deletions
File tree
- .changeset
- content/docs/references/ui
- examples/app-showcase/src/ui/pages
- packages
- platform-objects/src/pages
- spec
- src
- conversions
- migrations
- entries/semantic
- ui
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
436 | 436 | | |
437 | 437 | | |
438 | 438 | | |
439 | | - | |
| 439 | + | |
440 | 440 | | |
441 | 441 | | |
442 | 442 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
87 | 87 | | |
88 | 88 | | |
89 | 89 | | |
90 | | - | |
| 90 | + | |
91 | 91 | | |
92 | 92 | | |
93 | 93 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
365 | 365 | | |
366 | 366 | | |
367 | 367 | | |
368 | | - | |
| 368 | + | |
369 | 369 | | |
370 | 370 | | |
371 | 371 | | |
| |||
398 | 398 | | |
399 | 399 | | |
400 | 400 | | |
401 | | - | |
| 401 | + | |
402 | 402 | | |
403 | 403 | | |
404 | 404 | | |
| |||
431 | 431 | | |
432 | 432 | | |
433 | 433 | | |
434 | | - | |
| 434 | + | |
435 | 435 | | |
436 | 436 | | |
437 | 437 | | |
| |||
464 | 464 | | |
465 | 465 | | |
466 | 466 | | |
467 | | - | |
| 467 | + | |
468 | 468 | | |
469 | 469 | | |
470 | 470 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
7718 | 7718 | | |
7719 | 7719 | | |
7720 | 7720 | | |
| 7721 | + | |
| 7722 | + | |
| 7723 | + | |
| 7724 | + | |
| 7725 | + | |
| 7726 | + | |
| 7727 | + | |
| 7728 | + | |
| 7729 | + | |
| 7730 | + | |
| 7731 | + | |
| 7732 | + | |
| 7733 | + | |
| 7734 | + | |
| 7735 | + | |
| 7736 | + | |
| 7737 | + | |
| 7738 | + | |
| 7739 | + | |
| 7740 | + | |
| 7741 | + | |
| 7742 | + | |
| 7743 | + | |
| 7744 | + | |
| 7745 | + | |
| 7746 | + | |
| 7747 | + | |
| 7748 | + | |
| 7749 | + | |
| 7750 | + | |
| 7751 | + | |
| 7752 | + | |
| 7753 | + | |
| 7754 | + | |
| 7755 | + | |
| 7756 | + | |
| 7757 | + | |
| 7758 | + | |
| 7759 | + | |
| 7760 | + | |
| 7761 | + | |
| 7762 | + | |
| 7763 | + | |
| 7764 | + | |
| 7765 | + | |
| 7766 | + | |
| 7767 | + | |
| 7768 | + | |
| 7769 | + | |
| 7770 | + | |
| 7771 | + | |
| 7772 | + | |
| 7773 | + | |
| 7774 | + | |
| 7775 | + | |
| 7776 | + | |
| 7777 | + | |
| 7778 | + | |
| 7779 | + | |
| 7780 | + | |
| 7781 | + | |
| 7782 | + | |
| 7783 | + | |
| 7784 | + | |
| 7785 | + | |
| 7786 | + | |
| 7787 | + | |
| 7788 | + | |
| 7789 | + | |
| 7790 | + | |
| 7791 | + | |
| 7792 | + | |
| 7793 | + | |
| 7794 | + | |
| 7795 | + | |
| 7796 | + | |
| 7797 | + | |
| 7798 | + | |
| 7799 | + | |
| 7800 | + | |
| 7801 | + | |
| 7802 | + | |
| 7803 | + | |
| 7804 | + | |
| 7805 | + | |
| 7806 | + | |
| 7807 | + | |
| 7808 | + | |
| 7809 | + | |
| 7810 | + | |
| 7811 | + | |
| 7812 | + | |
| 7813 | + | |
| 7814 | + | |
| 7815 | + | |
| 7816 | + | |
| 7817 | + | |
| 7818 | + | |
| 7819 | + | |
| 7820 | + | |
| 7821 | + | |
| 7822 | + | |
| 7823 | + | |
| 7824 | + | |
| 7825 | + | |
| 7826 | + | |
| 7827 | + | |
| 7828 | + | |
| 7829 | + | |
| 7830 | + | |
| 7831 | + | |
| 7832 | + | |
| 7833 | + | |
| 7834 | + | |
| 7835 | + | |
| 7836 | + | |
| 7837 | + | |
| 7838 | + | |
| 7839 | + | |
| 7840 | + | |
| 7841 | + | |
| 7842 | + | |
| 7843 | + | |
| 7844 | + | |
| 7845 | + | |
| 7846 | + | |
| 7847 | + | |
| 7848 | + | |
| 7849 | + | |
| 7850 | + | |
| 7851 | + | |
| 7852 | + | |
| 7853 | + | |
| 7854 | + | |
| 7855 | + | |
| 7856 | + | |
| 7857 | + | |
| 7858 | + | |
| 7859 | + | |
| 7860 | + | |
| 7861 | + | |
7721 | 7862 | | |
7722 | 7863 | | |
7723 | 7864 | | |
| |||
14439 | 14580 | | |
14440 | 14581 | | |
14441 | 14582 | | |
| 14583 | + | |
14442 | 14584 | | |
14443 | 14585 | | |
14444 | 14586 | | |
| |||
Lines changed: 47 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
0 commit comments