Skip to content

Commit ba27f6b

Browse files
committed
feat(spec): the two retired spellings' refusals name the release that refuses them, joined in the removal-version pin
Claude-Session: https://claude.ai/code/session_01D6F9axfoPzR7t8G1J7Ytzz Co-authored-by: Claude <noreply@anthropic.com>
1 parent afb0202 commit ba27f6b

3 files changed

Lines changed: 33 additions & 8 deletions

File tree

‎packages/spec/src/shared/retired-key-removal-version.pin.test.ts‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ import {
8888
ActionMenuPropsSchema,
8989
ElementNumberPropsSchema,
9090
ElementRecordPickerPropsSchema,
91+
ElementButtonPropsSchema,
9192
ElementRepeaterPropsSchema,
9293
ObjectGridPropsSchema,
9394
ObjectKanbanPropsSchema,
@@ -239,6 +240,19 @@ const JOIN: Readonly<Record<string, readonly Renderer[]>> = {
239240
'sharingModel',
240241
),
241242
],
243+
// Two protocol-17 live-window conversions retired from the load path at 18
244+
// (`toMajor: 17`, unlike every other row): the prescription is the refusal
245+
// the surface answers once the loader stops rewriting the old spelling.
246+
'page-header-subtitle-alias': [
247+
refusal("page:header property 'description'", () => PageHeaderProps.safeParse({ title: 'Leads', description: 'All open leads' })),
248+
],
249+
'inline-action-api-params-to-body-extra': [
250+
refusal(
251+
"an inline type:'api' action's object-form params",
252+
() => ElementButtonPropsSchema.safeParse({ label: 'Go', action: { type: 'api', target: '/x', params: { a: 1 } } }),
253+
'action',
254+
),
255+
],
242256
// Shipped by a 17.x minor; `toMajor: 18` like the rows above.
243257
'page-header-breadcrumb-removed': [tombstone(PageHeaderProps, 'page:header', 'breadcrumb')],
244258
'object-grid-default-sort-removed': [tombstone(ObjectGridPropsSchema, 'object-grid', 'defaultSort')],

‎packages/spec/src/ui/action.zod.ts‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1295,11 +1295,13 @@ const actionObject = () => strictObject({
12951295
&& typeof iss.input === 'object'
12961296
&& !Array.isArray(iss.input)
12971297
? "`params` is the parameter DEFINITION array (fields collected from the user before the action runs), not a values map. "
1298-
+ "For a `type:'api'` action's static request body — including `{{page.<var>}}` tokens — use `bodyExtra: { … }` instead. "
1298+
+ "For a `type:'api'` action's static request body — including `{{page.<var>}}` tokens — use `bodyExtra: { … }` instead; "
1299+
+ "the loader rewrote that object form to `bodyExtra` on an inline page button until @objectstack/spec 18, which refuses it (ADR-0087 D2). "
12991300
+ "For a `type:'url'` action there is nowhere to move it to, by decision: put static values straight into the `target` string "
13001301
+ "(`${param.X}` interpolates a value collected by the params dialog, `${ctx.X}` one from the action context), and open a new tab with "
13011302
+ "`openIn: 'new-tab'`. The url-side readings of an object `params` — a static `${param.X}` scope, and `params.newTab` — are RETIRED, not renamed. "
1302-
+ 'Expected an array of ActionParam, received an object.'
1303+
+ 'Expected an array of ActionParam, received an object. '
1304+
+ 'Run `os migrate meta --from 16` to list the mechanical edits for the inline `element:button` action case; `--write` applies the ones it can prove, and a registered action keeps its object form until you move it by hand.'
13031305
: undefined
13041306
),
13051307
}).optional().describe('Input parameters required from user — an ActionParam[] DEFINITION array, never a payload map (a static request body goes in `bodyExtra`).'),

‎packages/spec/src/ui/component.zod.ts‎

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -588,7 +588,7 @@ export const PageHeaderProps = strictObject({
588588
surface: 'this `page:header`',
589589
history: PROPS_HISTORY,
590590
guidanceSets: COMPONENT_LEVEL_GUIDANCE,
591-
aliases: {
591+
guidance: {
592592
/**
593593
* The ADR-0087 D2 conversion `page-header-subtitle-alias` (#4827,
594594
* objectui#3226) renamed this on load for protocol 17's window; retired
@@ -601,12 +601,21 @@ export const PageHeaderProps = strictObject({
601601
* (`description` is a live declared prop on other components), got no
602602
* diagnostic at a nested site from any layer".
603603
*
604-
* An alias rather than a `retiredKey` tombstone precisely because of that
605-
* parenthesis: `description` is a live prop elsewhere in this file
606-
* (`element:text_input`), so the answer is a rename on THIS surface, not a
607-
* removal notice. Grounded in the conversion registry rather than guessed.
604+
* A guidance row rather than a `retiredKey` tombstone: `PageHeaderProps`
605+
* never declared `description`, so there is no property for a tombstone to
606+
* replace, and `description` is a live prop elsewhere in this file
607+
* (`element:text_input`), so the answer is a rename on THIS surface. It was
608+
* an `aliases` row (a bare "did you mean") while the conversion was live;
609+
* retired, it is the upgrade prescription, naming the release that refuses
610+
* the spelling (#23421). Grounded in the conversion registry rather than
611+
* guessed.
608612
*/
609-
description: 'subtitle',
613+
description:
614+
'`page:header` property `description` was removed in @objectstack/spec 18 (ADR-0087 D2) — it was an '
615+
+ 'off-spec second spelling of the header\'s second line, which the loader rewrote to `subtitle` until '
616+
+ 'this release; `subtitle` is the declared key. Use `subtitle`. Rename the key; the value '
617+
+ '(the second line\'s text) is unchanged. '
618+
+ '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.',
610619
},
611620
}, {
612621
/**

0 commit comments

Comments
 (0)