You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 8693110
Browse filesBrowse the repository at this point in the historyBrowse files
feat(spec)!: retire page-header-subtitle-alias and inline-action-api-params-to-body-extra from the load path at 18 (#23485)
Fixes#23421
Clause-②: yes (narrowing)
## What
The two ADR-0087 D2 conversions `page-header-subtitle-alias`
(`page:header` `properties.description` to `subtitle`) and
`inline-action-api-params-to-body-extra` (an inline `type: 'api'`
action's object-form `params` to `bodyExtra`) were declared `toMajor:
17` for protocol 17's one-major window. On the `18.0.0-next` line they
were still load-active, so `os validate` and the load path converted
silently what the metadata save door (once PR #23418 moves the
component-props rule onto it) refuses. This PR retires both from the
load path, the way every other retired entry is marked:
`retiredFromLoadPath: true` and `retiredAfter: '17.7.0'`, the newest
published stable release under the `18.0.0-next.0` label.
After this change every door that judges page props answers the same
way: `os validate`, `os build`, `os lint`, and the save door after
#23418 all refuse the old spelling, and the refusal names the rename.
Stored rows keep healing. `applyConversionsToStoredItem` replays the
full chain with `includeRetired: true`, so does the artifact-ingestion
door inside its window, and `os migrate meta` replays the chain too.
## Premise check (done before any edit)
- ADR-0087 D2, "Window": "each conversion is applied by the loader for
one major (N accepts N-1 shapes at load), then retired from the load
path in N+1". `toMajor: 17` means 18 is N+1. The section "The
load-window's second half is now mechanical" names
`MetadataConversion.retiredFromLoadPath` as the field that implements
that retirement.
- `conversions/types.ts`: `RetiredConversionState` is
`retiredFromLoadPath: true` plus a required `retiredAfter`. `apply.ts`
skips such an entry unless `includeRetired` is set. `stored.ts` pins
`includeRetired: true`.
- Precedent: at `61b12584f`, 45 `toMajor: 17` entries carry
`retiredFromLoadPath` (`retiredAfter` 16.1.0). Among the `toMajor: 18`
entries, 69 are retired, 7 of them with `retiredAfter: '17.7.0'` (for
example `declared-index-unique-scope` and
`email-template-category-is-system-removed`).
- The premise holds, so this PR takes the retirement route. It does not
touch the other route (the save door applying live-window conversions).
## Changes
- `packages/spec/src/conversions/registry.ts`: the two entries get the
two fields, and their docblocks now record the retirement in place of
"Live window; retires at 18".
- `packages/spec/src/ui/component.zod.ts`: `PageHeaderProps`
`description` moves from an `aliases` row (a bare "Did you mean
`description` to `subtitle`?") to a `guidance` row. It is the upgrade
prescription in house form: it names `@objectstack/spec 18` and
`subtitle`, followed by the `os migrate meta --from 16` house sentence.
- `packages/spec/src/ui/action.zod.ts`: the object-form `params` refusal
keeps its `bodyExtra` instruction. It now also says the loader rewrote
that form on an inline page button until `@objectstack/spec 18`, and it
ends in the MIXED two-clause `os migrate meta --from 16` sentence. The
partial form is used because the conversion only covers the inline
`element:button` case.
- Why these two text edits are here although the claim's file surface
does not name `ui/*.zod.ts`:
`shared/retired-key-removal-version.pin.test.ts` requires every entry
retired after the newest stable release to be joined to the
prescriptions an author meets, and requires each of those prescriptions
to name the derived release (18). Neither refusal named a release, so
the pin went red on exactly these two ids. The landing spot is the
producer of the refusal text, in the same package.
- Pin flips (load-bearing, not just deletions):
- `conversions/conversions.test.ts`: the two entries' rewrite cases run
the data-at-rest posture (`includeRetired: true`). New cases assert that
the authoring posture rewrites nothing on both `page:header` and the
kebab `page-header`, and on the object-form `params`. They also assert
the entry fields (`toMajor: 17`, `retiredFromLoadPath: true`,
`retiredAfter: '17.7.0'`) and that the `bodyExtra` refusal is what an
author meets. The premise pin on `PageHeaderProps` (refuses
`description`, names `subtitle`) stays verbatim.
- `lint/src/validate-component-props.test.ts`: the "normalized tier"
block asserted the normalized tier says nothing about `description`. It
now asserts that the normalized tier refuses it as
`component-props-unknown-key`, `error`, at the exact path, naming
`subtitle`, and that the raw tier gives the same answer.
- `shared/retired-key-removal-version.pin.test.ts`: two JOIN rows.
- Vehicle swaps: `spec/src/stack-conversions-record.test.ts`,
`cli/test/stack-conversion-record-door.test.ts` and
`cli/src/commands/validate-json-strict-exit.e2e.test.ts` used
`page-header-subtitle-alias` as their live conversion. Their own
docblocks said to re-point them at a live entry when it retired. They
now ride `page-component-visibility-to-visibleWhen` (`visibility` to
`visibleWhen` on the same `page:header` node, `retiresIn: 16`), and
every assertion is unchanged in substance.
- `.changeset/23421-retire-page-aliases-at-18.md`: `minor`, with the
**BREAKING** banner, `Clause-②: yes (narrowing)`, the FROM to TO
mapping, and the ADR-0087 disposition. In words, the disposition marker
is "adr-0087: not-required (already-registered
page-header-subtitle-alias, inline-action-api-params-to-body-extra)".
Both conversions are already in the step-17 chain and are the whole
mechanical migration, so there is no new D2 conversion and no D3
semantic entry. `check-adr-0087-registration` accepts it.
## For the seat: one sentence in PR #23418 becomes false
PR #23418's changeset
`.changeset/23388-page-component-rules-runtime-door.md` carries the
paragraph that begins "Two live-window ADR-0087 D2 aliases ... are
converted at `objectstack validate` and at load but not at this door".
Once this PR lands, that paragraph is false: neither alias is converted
at `objectstack validate` or at load any more. This PR does not edit
#23418's files. The same goes for the comment block in
`packages/lint/src/authoring-rules.ts` (the `validateComponentProps`
entry, "`normalized` for a reason worth stating ..."), which still cites
`page-header-subtitle-alias` as the converted alias that makes the tier
matter. That file is in #23418's diff, so it is left for that landing.
#23418 is not addressed here.
## Verification (at `6f0fcb21f`, after merging `origin/main` at
`3d231ba78`)
- `pnpm --filter @objectstack/spec test`: 676 files, 20576 tests passed
(after the merge).
- `pnpm --filter @objectstack/spec test:repo`: 59 files, 1011 tests
passed (before the merge, which touched no tree-scoped pin input of this
diff).
- `pnpm --filter @objectstack/spec check:generated`: all 14 generated
artifacts up to date.
- Before the merge: `typecheck`: `@objectstack/spec`,
`@objectstack/lint` and `@objectstack/cli` each exit 0.
- `@objectstack/lint` full vitest, `@objectstack/cli` `--project unit`
(287 files, 4307 tests), and the two touched CLI integration and e2e
files: `test/stack-conversion-record-door.test.ts` (21/21) and
`src/commands/validate-json-strict-exit.e2e.test.ts` under
`OS_TEST_TIERS=nightly` (7/7).
- Red before the flip: with only the registry edit applied,
`conversions.test.ts` had 5 failing cases and
`stack-conversions-record.test.ts` had 30, all asserting the two
conversions ran on the authoring path. The lint tier case and the
removal-version pin each went red the same way.
- The gate families came from `node scripts/pm/dispatch-gates.mjs
--commands --repo objectstack-ai/objectstack` (93 families, the same set
before and after the merge). All 93 were run at `6f0fcb21f` with exit 0,
and the list was reconciled with `--ran`.
## Acceptance notes
- `page-header` (kebab): the conversion's walk still lists it, for the
data-at-rest replays. Retired, a source `page-header` node's
`description` is neither converted nor judged, because
`ComponentPropsMap` has no `page-header` row. This is harmless: objectui
at the pinned `.objectui-sha` no longer registers `page-header` (its
components changeset "drop `page-header` from the opt-in protocol
placeholders"), so such a node renders the unknown-component panel
whatever its props say.
- The boot path loading source through `defineStack` does not run the
props gate (that gate runs at `os validate` / `os build` / `os lint`
and, after #23418, at the save door). This is the same posture every
other retired page prop already has (`page:header` `icon` and
`breadcrumb`).
- In-repo authors of either old spelling: zero (`examples/`,
`packages/platform-objects`, `packages/plugins`, `packages/services`).
The control grep on `page:header` finds the canonical `subtitle` in use.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01D6F9axfoPzR7t8G1J7Ytzz)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
feat(spec)!: two page-property conversions leave the load path at 18 — a `page:header`'s `description` and an inline `api` action's object-form `params` are refused with their rename at every door that judges page props
6
+
7
+
Clause-②: yes (narrowing)
8
+
9
+
**BREAKING (authoring):** the ADR-0087 D2 conversions `page-header-subtitle-alias` and `inline-action-api-params-to-body-extra` were declared for protocol 17's one-major window and are now retired from the load path. The authoring funnel (`defineStack`, `os validate`, `os build`, `os lint`) no longer rewrites either spelling, so an author who still writes one meets the page-props refusal, which names the rename. That refusal is the same answer the metadata save door gives, so `os validate` no longer passes a page that the save door then refuses. Both refusals now also name the release that refuses the old spelling and the `os migrate meta` command: the `page:header``description` refusal is an upgrade prescription naming `subtitle` (it was a bare "did you mean"), and the object-form `params` refusal adds the release and the command after its `bodyExtra` instruction.
10
+
11
+
Migration:
12
+
13
+
-`page:header` (and the legacy `page-header` node) `properties.description` → `properties.subtitle`. Rename the key; the value is unchanged. If both keys are present with different text, keep the one you mean under `subtitle` and delete `description`.
14
+
- An inline `type: 'api'` action's object-form `params` (a request payload map such as `params: { name: '{{page.inquiryName}}' }`) → `bodyExtra`. Rename the key; the value is unchanged. `params` stays the `ActionParam[]` definition array (the fields a dialog collects).
15
+
16
+
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.
17
+
18
+
Stored metadata needs no edit. Stored-row rehydration still replays the whole conversion chain, these two entries included, so `sys_metadata` page rows written under either old spelling keep loading in canonical form. A built artifact whose declared protocol floor is at or below `17.7.0` is also still converted at the artifact-ingestion door.
19
+
20
+
<!-- adr-0087: not-required (already-registered page-header-subtitle-alias, inline-action-api-params-to-body-extra) Both D2 conversions are registered in the step-17 chain before this change, and they are the whole mechanical migration: this diff marks them retiredFromLoadPath with retiredAfter 17.7.0 and makes the two refusals an author now meets name that release, so the authoring funnel stops replaying them while os migrate meta, stored-row rehydration and the artifact door keep replaying them. No key, value or shape is added or removed, so there is no new D2 conversion and no D3 semantic judgment to register: both rewrites are lossless renames. -->
0 commit comments