Skip to content

Commit a918fe7

Browse files
fix(spec): os migrate meta guidance for the remaining migration-entry families states each lesson in words, not tracker numbers (stage 9) (#20630)
Part of #20233 Stage 9: the remaining semantic-entry families, meaning every ADR-0087 semantic entry that still cited a tracker number. Clause-②: no `os migrate meta` prints each ADR-0087 semantic entry's `surface`, `replacement`, `reason` and `acceptanceCriteria` to the author. This stage covers the 44 entries that still cited a tracker, pull-request, decision-batch or cross-repository number: 39 with a four- or five-digit id, and 5 with a decision-batch number only. In each of them, every sentence now says what the cited ruling, measurement or fix decided (form D). ADR ids stay, and so do the contributor-guide rule references, which are not tracker ids. The `address-location-value-unknown-keys-refused` replacement cited `AGENTS.md #0.1`, which names nothing in AGENTS.md today. It now states the rule it copied from the 2026-09-01 ruling: a consumer-side alias for an off-spec key stays forbidden. - **Text only:** a base-vs-head AST comparison over the 45 changed entry files and `registry.ts` finds no change outside `replacement` / `reason` / `acceptanceCriteria`. That is 48 prose fields per copy, and no `surface`, id, comment, import or token-skeleton change. - **Census (AST instrument, whole tree):** base `682873f201` has 76 prose sites in 39 entries (replacement 3 / reason 73 / acceptanceCriteria 0), 0 surface sites and 28 short numbers. Head has 0 / 0 / 10. The 10 short numbers left are contributor-guide rule references. - **Pin:** `packages/cli/test/migrate-meta-engine-guidance.test.ts` now holds every semantic entry instead of a prefix list, so an entry added later in any family is held on arrival. `REWRITTEN` goes from 203 to 247. Ablation: putting one removed id back into the `turso-` entry (a family the pin did not cover before) turned the pin red; restoring it turned the pin green. - **Generated:** `registry.ts`, `spec-changes.json` and `docs/protocol-upgrade-guide.md`, by their generators. `patch` changeset. ## Verification (head `96b994e472`) - **Tests:** - spec `--project local`: 575/575 files (16917 passed, 1 todo). - spec `--project repo`: 42/42 files (745 passed). - CLI `--project unit`: 234/234 files (3342 passed). - CLI `--project integration`, the three migrate-meta files: 3/3 (13 passed, 1 skipped by the default-range file's own `skipIf`). - spec and CLI typecheck: exit 0, test-layer debt held. - **Gates:** `dispatch-gates.mjs --commands` derives 89 families. 88 exit 0, among them `check:doc-authoring`, `check:generated`, `check:migration-registry`, `check:spec-changes`, `check:upgrade-guide`, `check:issue-citations`, `check:nul-bytes`, `check:api-surface` and `check:authorable-surface`. - `check:dual-build-cjs-loads` is NOT MEASURED: its prerequisite is not met, because packages outside the CLI closure have no `dist/`. Build Core runs it. - `--ran` reconciliation: 89 accounted, 88 run, 1 NOT MEASURED. - **Lint:** eslint over the 47 changed source files: 0 errors, 0 warnings. - **Mergeability:** a driver-free `merge-tree` onto `origin/main` `b80ab579d8` is clean. Main's new commits touch none of these paths. The census method and controls, the source of every citation, and the ablation record are in the dev report on #20233. ## Acceptance notes - Four verbatim ruling quotes carried a card or batch number: 「217 同意」, 「9266 同意 A」, 「146 同意」, and one reply that also answered another card. Each now keeps only its operative words, or states the decision instead. - The claim fenced off four entries. Two of them are not on `main`: they arrive with PR #20570 and PR #20587. The two on `main` carry no site. PR #20570's incoming `filter-is-empty-lowers-to-empty-operator` carries six tracker ids in `reason`, and the `filter-` family was already held by this pin before this stage. - `ui-list-view-groupbyfield-padded-refused` and `ui-list-view-grouping-field-padded-refused` still cite `AGENTS.md #0.1` in `reason`. That is the same dangling number the `address-` entry had. Both entries are outside this stage's claim and are untouched. - `ui-notification-action-embed-config-retired` names "its ui/ batch 14" of the strictness sweep, a batch number without `#`. It is outside this stage and untouched. - Comment and docblock lines of the entries still cite tracker ids: 127 lines in `semantic/`, 839 across `entries/`. They are form C's, under #20234, and are untouched. --- _Generated by [Claude Code](https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 542670d commit a918fe7

50 files changed

Lines changed: 403 additions & 249 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: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
fix(spec): `os migrate meta` guidance for the remaining migration-entry families states each lesson in words instead of citing tracker numbers
6+
7+
Clause-②: no
8+
9+
The ADR-0087 semantic entries are printed by `os migrate meta` as the header, `why:` and
10+
`verify:` lines of a manual change. In the families not yet brought to this line — among them
11+
`turso-*`, `auth-*`, `admin-*`, `ai-*`, `assembled-*`, `change-*`, `device-*`, `epoch-*`,
12+
`incident-*`, `logging-*`, `memory-*`, `send-*`, `standard-*`, `training-*`, `websocket-*`,
13+
`structured-*` and `translation-*` — that text sent the reader to issue-tracker, pull-request,
14+
decision-batch and cross-repository numbers, some of which no longer resolve, for what a
15+
ruling, measurement or fix had decided; it now says what was decided, in the sentence being
16+
read. Verbatim rulings that carried a card or batch number keep only their operative words.
17+
ADR ids are kept, and so are the rule numbers of this repository's own contributor guide. With
18+
this change no semantic entry's printed guidance carries a `#`-numbered tracker id.
19+
20+
One replacement also named a contributor-guide rule by a number that no longer exists:
21+
`address-location-value-unknown-keys-refused` now states the rule itself — a consumer never
22+
carries an alias for an off-spec key; the metadata is fixed where it is written.
23+
24+
Text only: no entry id, `surface`, `from` / `to`, conversion or matching logic changes, and the
25+
chain rewrites exactly what it rewrote before. The generated migration registry,
26+
`spec-changes.json` and the protocol upgrade guide carry the same text.

‎docs/protocol-upgrade-guide.md‎

Lines changed: 9 additions & 9 deletions
Large diffs are not rendered by default.

‎packages/cli/test/migrate-meta-engine-guidance.test.ts‎

Lines changed: 64 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,8 @@
11
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
22

33
/**
4-
* `os migrate meta` — the guidance it prints for the ADR-0087 semantic entries
5-
* of the COVERED families (`engine-*`, `ui-*`, `plugin-*`, `driver-*`,
6-
* `kernel-*`, `system-*`, `datasource-*`, `filter-*`, `action-*`, `data-*`,
7-
* `element-*`, `field-*`, `export-*`, `api-*`, `dataset-*`, `hook-*`,
8-
* `metadata-*`, `rest-*`, `analytics-*`, `view-*`, `package-*`, `object-*`,
9-
* `sharing-*`, `audit-*`, `flow-*`, `http-*`, `inline-*`, `actor-*`, `hot-*`,
10-
* `external-*`, `query-*`, `delete-*`, `etl-*`, `storage-*`, `apimethod-*`,
11-
* `dashboard-*`, `notification-*`, `record-*`, `runtime-*`, `rls-*`, `scim-*`,
12-
* `stack-*`, `evaluated-*`, `aggregation-*`, `authoring-*`, `automation-*`,
13-
* `cache-*`, `tenant-*`, `client-*`, `spec-*`, `cli-*`, `identity-*`,
14-
* `import-*`, `tool-*`, `advanced-*`, `cloud-*`, `startup-*`, `sys-*`,
15-
* `declarative-*`, `sort-*`, `address-*`, `packages-*`, `platform-*`,
16-
* `session-*`, `strategy-*`) states each lesson in words and carries no
17-
* tracker number.
4+
* `os migrate meta` — the guidance it prints for EVERY ADR-0087 semantic entry
5+
* states each lesson in words and carries no tracker number.
186
*
197
* ## What this pins
208
*
@@ -24,27 +12,27 @@
2412
* author is shown, so it carries no tracker number: a number sends the reader
2513
* to a page that can be deleted (some cited pages already had been), and the
2614
* lesson the entry exists to teach then sits behind a dead link instead of in
27-
* the sentence being read. The covered families were rewritten, one staged
28-
* family at a time, to say what each cited ruling, measurement or fix decided;
29-
* ADR ids stay, because an ADR lives in this repository. The whole printed
15+
* the sentence being read. The entries were rewritten, one staged family at a
16+
* time, to say what each cited ruling, measurement or fix decided; ADR ids
17+
* stay, because an ADR lives in this repository. The whole printed
3018
* block is held, so `surface` is held as well as the three prose fields.
3119
*
3220
* The chain reports every semantic entry of every hop it crosses, whatever the
3321
* stack authors, so the fixture only has to be a real stack the command loads;
3422
* it keeps the lookup and the virtual `formula` field the `engine-*` entries
3523
* are about. The CLI replays the chain from the support floor to the highest
36-
* major carrying a covered entry. Each covered block is then located VERBATIM
37-
* in what the terminal printed, and that printed block must hold no `#`
38-
* followed by four or five digits. The file keeps the name it was given when
39-
* `engine-*` was the only covered family.
24+
* major carrying a semantic entry. Each block is then located VERBATIM in what
25+
* the terminal printed, and that printed block must hold no `#` followed by
26+
* four or five digits. The file keeps the name it was given when `engine-*`
27+
* was the only covered family; the staged rewrites have since reached every
28+
* family, so the pin holds the whole directory rather than a prefix list.
4029
*
4130
* ## Why it cannot pass by reading nothing
4231
*
43-
* - The covered set is derived from the registry by id prefix, so an entry
44-
* added later to a covered family is held to the same line on arrival — and
45-
* the derived set must still contain every entry the rewrites covered, and
46-
* every covered prefix must still select at least one entry, so an emptied
47-
* prefix cannot turn every assertion below into a loop over nothing.
32+
* - The covered set is every semantic entry in the registry, so an entry added
33+
* later — in any family, a new family included — is held to the same line on
34+
* arrival; and the set must still contain every entry the rewrites covered,
35+
* so it cannot turn every assertion below into a loop over nothing.
4836
* - Each block is asserted PRESENT in stdout before it is asserted clean, so a
4937
* renderer change that stopped printing the prose fails here instead of
5038
* passing on an absent string.
@@ -82,26 +70,10 @@ const TSX = resolve(HERE, '../../../node_modules/.bin/tsx');
8270
/** A tracker id as author-shown prose must not carry it: `#` and four or five digits. */
8371
const TRACKER_ID = /#\d{4,5}\b/;
8472

85-
/** The families this pin holds, selected by entry-id prefix. */
86-
const COVERED_PREFIXES = [
87-
'engine-', 'ui-', 'plugin-', 'driver-', 'kernel-', 'system-',
88-
'datasource-', 'filter-', 'action-', 'data-', 'element-',
89-
'field-', 'export-', 'api-', 'dataset-', 'hook-', 'metadata-',
90-
'rest-', 'analytics-', 'view-', 'package-', 'object-', 'sharing-',
91-
'audit-', 'flow-', 'http-', 'inline-',
92-
'actor-', 'hot-', 'external-', 'query-', 'delete-', 'etl-', 'storage-',
93-
'apimethod-', 'dashboard-', 'notification-', 'record-', 'runtime-', 'rls-',
94-
'scim-',
95-
'stack-', 'evaluated-', 'aggregation-', 'authoring-', 'automation-', 'cache-',
96-
'tenant-', 'client-', 'spec-', 'cli-', 'identity-', 'import-', 'tool-',
97-
'advanced-', 'cloud-', 'startup-', 'sys-', 'declarative-', 'sort-', 'address-',
98-
'packages-', 'platform-', 'session-', 'strategy-',
99-
];
100-
10173
/**
10274
* The entries rewritten when each family was brought to this line — the
103-
* anti-vacuity floor. A covered entry that carried no tracker id to begin with
104-
* is held by its prefix and needs no row here.
75+
* anti-vacuity floor. An entry that carried no tracker id to begin with needs no
76+
* row here: the whole directory is held.
10577
*/
10678
const REWRITTEN = [
10779
'action-bulk-dispatch-contract-undeclared',
@@ -111,8 +83,11 @@ const REWRITTEN = [
11183
'action-session-roles-to-positions',
11284
'actor-user-roles-to-positions',
11385
'address-location-value-unknown-keys-refused',
86+
'admin-export-wildcard-removed',
87+
'admin-scope-business-unit-blank-refused',
11488
'advanced-plugin-lifecycle-config-retired',
11589
'aggregation-node-distinct-retired',
90+
'ai-conversation-analytics-duration-unit-in-key',
11691
'analytics-authorable-unknown-keys-refused',
11792
'analytics-date-range-array-two-bounds-required',
11893
'analytics-query-request-envelope-retired',
@@ -122,16 +97,29 @@ const REWRITTEN = [
12297
'api-runtime-config-durations-unit-in-key',
12398
'api-runtime-create-withdrawn',
12499
'apimethod-enum-shrink',
100+
'approval-escalation-enabled-default-flip',
101+
'assembled-package-body-plugins-envelope',
102+
'audience-posture-default-invite-only',
125103
'audit-log-action-enum-retired',
126104
'audit-log-action-restore-retired',
105+
'auth-config-unadvertised-reserved-features',
127106
'authoring-schemas-strict-unknown-keys',
128107
'automation-flow-list-route-retired',
129108
'automation-runs-cursor-retired',
109+
'autonumber-default-unique-organization',
110+
'batch-row-result-schema-shape',
111+
'branded-identifier-schemas-retired',
130112
'cache-warmup-scheduled-strategy-retired',
113+
'cbp-master-detail-required-forced',
114+
'cel-predicate-one-value-comparand-refused',
115+
'change-management-duration-keys-retired',
116+
'change-management-family-retired',
131117
'cli-command-contribution-retired',
132118
'client-delete-result-success',
133119
'client-meta-reset-result-reset',
134120
'cloud-subpath-retired',
121+
'cluster-driver-dangling-values-removed',
122+
'connector-inline-authentication-publish-refused',
135123
'dashboard-header-modal-target-page-only',
136124
'dashboard-widget-chart-config-structure-refused',
137125
'dashboard-widget-compareto-offset',
@@ -156,6 +144,7 @@ const REWRITTEN = [
156144
'datasource-credentialsref-mongo-url-no-user-refused',
157145
'declarative-apis-endpoints-live',
158146
'delete-by-id-before-hook-repoint-retired',
147+
'device-request-response-interval-unit-in-key',
159148
'driver-aggregate-undeclared-key-aliases-removed',
160149
'driver-capabilities-inert-bits-removed',
161150
'driver-options-timeout-to-timeout-ms',
@@ -171,8 +160,12 @@ const REWRITTEN = [
171160
'engine-find-formula-filter-refused',
172161
'engine-find-formula-order-by-refused',
173162
'engine-update-upsert-retired',
163+
'enhanced-api-error-field-errors-renamed',
164+
'epoch-instant-keys-renamed',
165+
'esignature-config-deadline-keys-retired',
174166
'etl-pipeline-layer-retired',
175167
'evaluated-expression-slots-source-required',
168+
'event-name-schema-retired',
176169
'export-axis-opt-in',
177170
'export-field-meta-constraints-retired',
178171
'export-job-family-retired',
@@ -209,7 +202,10 @@ const REWRITTEN = [
209202
'http-server-runtime-vocabulary-retired',
210203
'identity-api-key-schema-retired',
211204
'import-run-automations-declared-default-corrected',
205+
'incident-response-deadline-keys-retired',
206+
'incident-response-family-retired',
212207
'inline-grid-column-currency-scale-refused',
208+
'job-retry-policy-constraints-tightened',
213209
'kernel-compatibility-matrix-estimated-migration-time-unit-in-key',
214210
'kernel-context-preview-mode-retired',
215211
'kernel-event-bus-retention-unit-in-key',
@@ -219,6 +215,8 @@ const REWRITTEN = [
219215
'kernel-plugin-security-durations-unit-in-key',
220216
'kernel-runtime-config-timeout-unit-in-key',
221217
'kernel-startup-orchestrator-durations-unit-in-key',
218+
'logging-durations-unit-in-key',
219+
'memory-persistence-placeholder-refused',
222220
'metadata-customization-protocol-retired',
223221
'metadata-endpoints-switch-radius-repartitioned',
224222
'metadata-manager-config-cache-ttl-unit-in-key',
@@ -229,6 +227,7 @@ const REWRITTEN = [
229227
'object-grid-data-view-data-converged',
230228
'object-grid-default-filters-rule-array',
231229
'object-index-unknown-keys-refused',
230+
'observability-cel-predicates-retired',
232231
'package-api-contracts-unmounted-entries-retired',
233232
'package-install-request-unknown-keys-refused',
234233
'package-rollback-response-retired',
@@ -245,6 +244,7 @@ const REWRITTEN = [
245244
'plugin-runtime-family-retired',
246245
'plugin-security-scan-result-surface-retired',
247246
'plugin-security-scanner-retired',
247+
'position-permissions-column-retired',
248248
'query-array-string-agg-retired',
249249
'query-cursor-retired',
250250
'query-distinct-retired',
@@ -261,7 +261,10 @@ const REWRITTEN = [
261261
'rls-predicate-cross-class-field-comparison-refused',
262262
'rls-predicate-stored-list-ordering-refused',
263263
'runtime-httpserver-wrapper-retired',
264+
'schedule-flow-acting-organization-required',
264265
'scim-provider-object-retired',
266+
'screen-field-lookup-reference-required',
267+
'send-template-input-org-retired',
265268
'session-payload-positions-security-axis',
266269
'session-user-language-retired',
267270
'sharing-execution-context-retired',
@@ -270,9 +273,12 @@ const REWRITTEN = [
270273
'spec-type-alias-input-suffix-retired',
271274
'stack-themes-carrier-retired',
272275
'stack-top-level-unknown-keys-refused',
276+
'standard-error-code-batch-members-retired',
277+
'standard-error-code-concurrent-limit-exceeded-retired',
273278
'startup-orchestrator-retired',
274279
'storage-service-list-retired',
275280
'strategy-context-aggregation-method-narrowed',
281+
'structured-region-body-pause-and-end-refused',
276282
'sys-account-issuer-retired',
277283
'system-cache-durations-unit-in-key',
278284
'system-collaboration-durations-unit-in-key',
@@ -286,7 +292,13 @@ const REWRITTEN = [
286292
'system-worker-queue-rate-limit-duration-unit-in-key',
287293
'tenant-schema-cache-ttl-unit-in-key',
288294
'tenant-timeouts-unit-in-key',
295+
'time-update-interval-sub-day-retired',
289296
'tool-requires-confirmation-retired',
297+
'training-deadline-keys-retired',
298+
'training-family-retired',
299+
'translation-per-app-settings-platform-only',
300+
'turso-config-transport-mismatch-refused',
301+
'ui-bulk-action-param-unknown-keys-refused',
290302
'ui-cloud-connection-widgets-unknown-keys-refused',
291303
'ui-form-field-length-malformed-refused',
292304
'ui-form-field-precision-scale-integer-refused',
@@ -301,12 +313,16 @@ const REWRITTEN = [
301313
'ui-record-blocks-unknown-keys-refused',
302314
'ui-reference-rail-unknown-keys-refused',
303315
'ui-widget-i18n-family-retired',
316+
'ups-delegated-from-column-retired',
304317
'view-filter-rule-absent-value-refused',
305318
'view-filter-rule-scalar-operator-array-refused',
306319
'view-filter-rule-value-shaped-by-operator',
307320
'view-management-protocol-retired',
308321
'view-overlay-options-bag-judged',
309322
'view-pagination-page-size-default-50',
323+
'wait-node-event-config-required',
324+
'websocket-durations-unit-in-key',
325+
'workflow-service-slot-retired',
310326
];
311327

312328
interface FamilyEntry {
@@ -318,10 +334,9 @@ interface FamilyEntry {
318334
acceptanceCriteria: string;
319335
}
320336

337+
/** Every semantic entry of every major: the pin holds the whole directory. */
321338
const FAMILY: FamilyEntry[] = Object.entries(MIGRATIONS_BY_MAJOR).flatMap(([major, step]) =>
322-
step.semantic
323-
.filter((s) => COVERED_PREFIXES.some((prefix) => s.id.startsWith(prefix)))
324-
.map((s) => ({ ...s, toMajor: Number(major) })),
339+
step.semantic.map((s) => ({ ...s, toMajor: Number(major) })),
325340
);
326341

327342
/** The block the command prints for one semantic TODO, exactly as `meta.ts` lays it out. */
@@ -378,7 +393,7 @@ afterAll(() => {
378393
try { rmSync(dir, { recursive: true, force: true }); } catch { /* ignore */ }
379394
});
380395

381-
describe('os migrate meta — the guidance of the covered families carries no tracker number', () => {
396+
describe('os migrate meta — the guidance of every semantic entry carries no tracker number', () => {
382397
it('the detector fires on a tracker id and stays dark on every other number shape', () => {
383398
expect(TRACKER_ID.test(`see #${'9'.repeat(4)}`)).toBe(true);
384399
expect(TRACKER_ID.test(`see #${'9'.repeat(5)}`)).toBe(true);
@@ -387,11 +402,8 @@ describe('os migrate meta — the guidance of the covered families carries no tr
387402
expect(TRACKER_ID.test('ADR-0112')).toBe(false);
388403
});
389404

390-
it('selects every covered family, including every entry the rewrites covered', () => {
405+
it('holds every semantic entry, including every entry the rewrites covered', () => {
391406
const ids = FAMILY.map((e) => e.id);
392-
for (const prefix of COVERED_PREFIXES) {
393-
expect(ids.some((id) => id.startsWith(prefix)), `no entry selected for ${prefix}`).toBe(true);
394-
}
395407
for (const id of REWRITTEN) expect(ids, `family lost ${id}`).toContain(id);
396408
});
397409

0 commit comments

Comments
 (0)