Skip to content

Commit 288611e

Browse files
fix(spec): os migrate meta guidance for the rest-*, analytics-*, view-*, package-*, object-*, sharing-*, audit-*, flow-* and http-* migration entries states each lesson in words, not tracker numbers (stage 6) (#20536)
Part of #20233 Stage 6: the rest-, analytics-, view-, package-, object-, sharing-, audit-, flow- and http- families. Clause-②: no **Stage 6 of a staged card.** The card stays open for later stages; this PR carries no closing keyword. Text only: no entry id, `from` / `to`, conversion or matching logic moves, and the chain rewrites exactly what it rewrote before. One `surface` moves, under ruling A of the stage-1 ACCEPT (`5858839916`): it carried two tracker numbers. ## What this does `os migrate meta` prints every ADR-0087 semantic entry it crosses as one block: `⚠ [protocol N] SURFACE → REPLACEMENT`, then `why:` (the entry's `reason`) and `verify:` (its `acceptanceCriteria`). AGENTS.md's runtime-string rule applies to all of it: 「Runtime strings — refusal prose, prescriptions, anything an author is shown — carry no tracker number (`pnpm check:doc-authoring`): the lesson goes into the text.」 Form **D** of ruling C+D on card `19123` (`5749154545`) sets the shape: the lesson in words, and no number, dead or alive; a cross-repo number is still a tracker number. This stage covers the nine families `rest-`, `analytics-`, `view-`, `package-`, `object-`, `sharing-`, `audit-`, `flow-` and `http-`: **122 sites → 0** in the three prose fields and **2 → 0** in `surface`, across 33 entry files. It also takes the two carry-overs the stage-5 record (`5880299859`) named: - `18.api-error-retry-after-unit-in-key`: the clause on the ~16 runtime-emitted measurements and `ApiError.retryAfter` now dates the ruling that decided it — "its 2026-09-05 population ruling" — instead of reading under ruling B's 2026-09-02 date alone. - `18.inline-grid-column-currency-scale-refused`: the two ruling-record ids and the batch / item numbers are replaced by the rulings' dates and options, the same rewrite stage 5 gave its `field-` sibling. Each site now says what the cited ruling, measurement or fix decided. ADR ids stay, and so does `Prime Directive #10` in `18.package-manifest-version-grammar-enforced` (a rule in AGENTS.md, as stages 2–5 kept `#10` / `#12`). `registry.ts`, `spec-changes.json` and `docs/protocol-upgrade-guide.md` are regenerated from the entries (`gen:migration-registry`, `gen:spec-changes`, `gen:upgrade-guide`), never hand-edited. The pin now holds twenty-seven families. ## Census — tracker ids in the author-shown fields **Instrument.** The stage-4 / stage-5 TypeScript-AST census, the same script: for each entry object literal under `packages/spec/src/migrations/entries/**` it evaluates `replacement`, `reason`, `acceptanceCriteria` and (separately) `surface`, joining string literals with `+`, and counts `#` followed by 4 or 5 digits at a word boundary. On base `fb386074` it reads the whole tree at **461** sites / 5 `surface` / 50 short, which is the stage-5 record's after-count. Unevaluable fields: 0. 313 semantic entries. **Controls, same run.** - **Lit:** `17.aggregation-node-distinct-retired.ts` reads 7 sites (replacement 1, reason 6), before and after. - **Dark (comment lines):** 832 `//` / docblock lines in entry files carry a tracker id, and none is counted; 832 before and after. Comment lines are the sibling card's surface (the one that owns every comment and docblock line), and this PR touches none (proved below). **Base `fb386074`:** `rest-` 6 entries, **17** sites (0 / 17 / 0); `view-` 10, **15** (0 / 13 / 2); `analytics-` 6, **14** (2 / 12 / 0); `audit-` 2, **14** (2 / 12 / 0); `sharing-` 2, **14** (1 / 13 / 0); `http-` 2, **13** (0 / 13 / 0); `object-` 7, **13** (1 / 11 / 1); `flow-` 6, **11** (0 / 11 / 0) plus **2** in `surface`; `package-` 6, **11** (0 / 10 / 1). **122** sites (6 / 112 / 4) in 31 of the 47 entries; 91 distinct ids read (82 in this repository, 8 in objectui, 1 `hotcrm#`), plus five ruling-record comment ids. Short numbers in the nine families: 9 (one kept, the Prime Directive). **After this PR:** all nine families **0**, `surface` 0; the seventeen earlier families still 0; whole tree **461 → 339**, `surface` **5 → 3**, short **50 → 40** (the two `inline-` batch numbers included). The PM's rough line count (about 133 sites, about 30 files) is a wider line instrument; the AST reading is 122 in 31 files. | entry | sites (replacement / reason / acceptanceCriteria) | surface | short numbers | record ids | |---|---|---|---|---| | `17.analytics-query-request-envelope-retired` | 1 (0 / 1 / 0) | | | | | `17.audit-log-action-enum-retired` | 4 (0 / 4 / 0) | | | | | `17.audit-log-action-restore-retired` | 10 (2 / 8 / 0) | | | | | `17.flow-retry-max-retries-required` | 1 (0 / 1 / 0) | | | | | `17.http-request-errors-total-retired` | 7 (0 / 7 / 0) | | | | | `17.http-server-runtime-vocabulary-retired` | 6 (0 / 6 / 0) | | | | | `17.package-uninstall-explicit-all-tenants` | 3 (0 / 2 / 1) | | 1 | | | `17.rest-server-openapi31-block-removed` | 2 (0 / 2 / 0) | | | | | `17.sharing-execution-context-retired` | 11 (1 / 10 / 0) | | | | | `17.sharing-rule-recipient-reconcile` | 3 (0 / 3 / 0) | | | | | `17.view-filter-rule-value-shaped-by-operator` | 4 (0 / 3 / 1) | | | | | `17.view-management-protocol-retired` | 3 (0 / 2 / 1) | | | | | `18.analytics-authorable-unknown-keys-refused` | 3 (0 / 3 / 0) | | | | | `18.analytics-date-range-array-two-bounds-required` | 7 (2 / 5 / 0) | | 1 | | | `18.analytics-time-dimension-date-range-vocabulary-closed` | 3 (0 / 3 / 0) | | 1 | | | `18.api-error-retry-after-unit-in-key` | 0 | | | | | `18.flow-decision-branch-expression-absent-refused` | 1 (0 / 1 / 0) | | | | | `18.flow-decision-edge-branching-first-match` | 1 (0 / 1 / 0) | | | | | `18.flow-edge-condition-evaluated-slot-source-required` | 4 (0 / 4 / 0) | 2 | | 1 | | `18.flow-predicate-slot-blank-string-refused` | 4 (0 / 4 / 0) | | | 1 | | `18.inline-grid-column-currency-scale-refused` | 0 | | 2 | 2 | | `18.object-block-sort-item-array` | 3 (0 / 3 / 0) | | 1 | | | `18.object-grid-data-view-data-converged` | 4 (0 / 3 / 1) | | | | | `18.object-grid-default-filters-rule-array` | 2 (0 / 2 / 0) | | | | | `18.object-index-unknown-keys-refused` | 4 (1 / 3 / 0) | | | | | `18.package-api-contracts-unmounted-entries-retired` | 3 (0 / 3 / 0) | | 1 | | | `18.package-install-request-unknown-keys-refused` | 0 | | 1 | 1 | | `18.package-rollback-response-retired` | 5 (0 / 5 / 0) | | | | | `18.rest-api-endpoint-handler-status-retired` | 7 (0 / 7 / 0) | | 1 | | | `18.rest-api-plugin-durations-unit-in-key` | 3 (0 / 3 / 0) | | 1 | | | `18.rest-server-config-dead-keys-retired` | 5 (0 / 5 / 0) | | | | | `18.view-filter-rule-absent-value-refused` | 2 (0 / 2 / 0) | | | | | `18.view-filter-rule-scalar-operator-array-refused` | 4 (0 / 4 / 0) | | | | | `18.view-overlay-options-bag-judged` | 0 | | | | | `18.view-pagination-page-size-default-50` | 2 (0 / 2 / 0) | | | | | **total, 35 entries** | **122 (6 / 112 / 4)** | **2** | **10 (0 kept)** | **5** | The fourteen entries of these families that carried no number are untouched: `analytics-cube-public-default-visible-enforced`, `analytics-query-request-format-retired`, `flow-node-config-required-keys-refused`, `object-grid-default-sort-retired`, `object-kanban-quick-add-retired`, `object-tenancy-organization-field-retired`, `package-manifest-version-grammar-enforced` (its one short number is the kept Prime Directive), `package-version-row-semver-2-0-0`, `rest-api-config-dead-keys-retired`, `rest-api-documentation-version-retired` (the new entry from the landed `rest-` change: its prose was read and is clean), `view-filter-rule-operator-input-canonical`, `view-item-owner-hidden-retired`, `view-overlay-judged-by-viewkind-arm`, `view-overlay-owner-hidden-retired`. Four of the 35 changed entries carried no four- or five-digit id: `view-overlay-options-bag-judged` (a provenance-only acknowledgement quote), `package-install-request-unknown-keys-refused` (a batch number and a ruling-record id) and the two carry-overs. ## Text only — proved by a base-vs-head AST comparison For every entry file this PR changes, both versions (`fb386074` and the head) are parsed and compared: every import declaration; every property other than the three prose fields, by evaluated value (so `id`, `from` / `to` and any matcher); every comment token in the file; and the code skeleton, token by token with each run of joined string literals collapsed to one. `surface` is allowed to differ only where the base value carried a tracker id and the head value carries none. **35 files compared, 0 with a non-prose change**; one note, the ruling-A `surface` of `18.flow-edge-condition-evaluated-slot-source-required`. The instrument is shown able to fail first: on an in-memory copy it reports DETECTED for a mutated `id`, a mutated comment, a mutated `surface` whose base carried no tracker id, a mutated code token and a mutated import, and stays dark on a prose-only mutation. So none of the sibling card's comment lines moved, and no entry's identity or matching moved. `registry.ts`, compared the same way: comment tokens, imports and code skeleton identical; all 1,572 non-prose properties identical by value (container literals compared through their children); exactly the 35 changed ids differ; and all 313 head registry entries equal the head entry files on `surface` / `replacement` / `reason` / `acceptanceCriteria`. ## Every citation read, and what the text now says Each cited id was read with a single-card REST read (body plus the ruling, measurement or landing comments), resolved against the repository its sentence names: 82 in this repository and 8 in `objectstack-ai/objectui` (one bare id resolves there: the sort ruling's consumer change `8758` is "objectui PR" in its sentence). The five ruling-record comment ids were read by id. `hotcrm#1555` answers 403 to this session and is rewritten from what `main` records. Ids are in code spans so this body posts no cross-references. **8 of this repository's ids answer 404** on the issues endpoint and again on the pulls endpoint (`6206`, `6239`, `6511`, `6523`, `10004`, `14369`, `14691`, `17124`; control `6209` answers 200); their sentences are rewritten from what `main` records, listed in Acceptance notes. **`rest-` (14 ids)** | cited | what it decided (read) | how the text now carries it | |---|---|---| | `3197` | An audit: several event / subscription / connector webhook enums are schema-only, declared with no runtime consumer. | "the declared-but-unconsumed shape an earlier audit found in the connector webhook and event enums one layer up" | | `4579` | This retirement's own card (`openApi31` declared, never enforced). | trailing id dropped; "(the `openApi31` precedent)" | | `13823` | Maintainer, 2026-09-01: remove `handlerStatus` with a tombstone; enforce excluded; the class direction recorded for two sibling cards. | "maintainer ruling of 2026-09-01 on this key: remove it with a tombstone; enforce excluded"; trailing id dropped | | `5384` | `ApiEndpointSchema` was still an open object after `api` became a registered metadata type; closed strictly. | "the same endpoint vocabulary whose ApiEndpointSchema had already been closed strictly once `api` became a registered metadata type" | | `13808` (PR) | A factual sweep of the automation skill; one corrected sentence taught `handlerStatus` as working machinery. | "this finding came out of correcting that skill sentence, in a factual sweep of the automation skill" | | `3950` (PR) | The precedent that an exported value schema with no consumer reads as a capability, so it leaves with its key. | "an exported value schema with no consumer reads as a capability, so it leaves with its key" | | `13612`, `13613` | The unbound branded identifier schemas; `EventNameSchema`'s binding schemas with no runtime consumer. | "two sibling ADR-0049 findings (the unbound branded identifier schemas and the event-name schema no runtime reads; not ruled by it)" | | `14478`, `15677` | Maintainer ruling B on duration units (2026-09-02), its population widened 2026-09-05; the `api/` stack of that ruling. | the stage-3 wording, naming both dates; trailing ids dropped | | `14369` | **404** — see Acceptance notes. | "The liveness census that enrolled the four `RestServerConfig` sub-objects" | | `11984` | `normalizeConfig` cast the four sub-objects instead of parsing them; it parses them since. | "(which parses them, rather than casting them, since an earlier fix)" | | `14796` | A closed-set sweep of the cloud repository for the 15 dead keys: zero hits. | "A closed-set sweep of the cloud repository at 9b6abe0f2fd5: zero hits" | | `14691` | **404** — this retirement's own card. | trailing id dropped | **`analytics-` (10 ids)** | cited | what it decided (read) | how the text now carries it | |---|---|---| | `3891` | The degraded analytics shim (the fallback serving `/analytics/query` with no analytics service installed) dropped the caller's identity and the contract's `where` filter at its door. | "the retired degraded analytics shim (the fallback that answered /analytics/query when no analytics service was installed, and dropped the caller's identity and its `where` filter at the door)" | | `4001` | The unknown-key strictness campaign: silent stripping of undeclared keys ends as the default, schema family by schema family. | "The unknown-key strictness campaign (the sweep that ended silent stripping of undeclared keys as the default, one schema family at a time), its data/ batch" | | `3878` | One URL, two request bodies (the shim's envelope vs the bare query); the envelope dialect was retired. | "strict since the degraded shim's envelope dialect was retired (one URL, one request body)" | | `10414` | `MetricSchema.filters` was authorable with zero consumers; removed. | "removed later in this major, because nothing ever read it: `metric-filters-removed`" | | `17598` | Maintainer ruling A, 2026-09-12, re-affirmed 2026-09-13: the array arm refuses anything but exactly two string bounds. | "Maintainer ruling A of 2026-09-12, re-affirmed 2026-09-13, which tightened the array arm to exactly two string bounds" | | `16322` | The driver half of the closed preset vocabulary; its migration table is the vocabulary entry's, unchanged (single day as the same date twice). | "the shipped migration table for the closed preset vocabulary"; "in a driver change of their own" | | `17593` (PR) | All four analytics faces read the array arm through one rule and refuse the rest with the ADR-0112 envelope. | "since the fix that made them read the array arm one way"; "The fix that followed made all four faces refuse it"; "Since that fix" | | `17124` | **404** — see Acceptance notes. | "a measurement of one authored document on each face found what that bought" | | `16041` | Maintainer, 2026-09-06, option A (contract first): the string arm closes to the declared preset vocabulary. | "Maintainer ruling of 2026-09-06 on the analytics date-range string (option A — contract first)" | | `4614` | The preset list, once three copies (spec, objectui, docs), became one source of truth. | "since the dashboard date filter's three copies of the list were folded into it" | **`view-` (10 ids)** | cited | what it decided (read) | how the text now carries it | |---|---|---| | `5869`, `6209` (PR) | A scalar comparand on `in` / `not_in` answered 500; now a named 400 INVALID_FILTER. | "An earlier fix closed the RUNTIME half"; the trailing `(5869)` in `acceptanceCriteria` dropped | | `5685` | The ordering operators' comparand was widened to the strings the platform itself produces (a schema stricter than the runtime was the wrong side). | "an earlier fix already settled the opposite error (the ordering operators' comparand widened to the strings the platform itself produces)" | | `5948` | The issue asking what `GET /ui/view/:object/:type` answers, and its 2026-08-07 ruling, both read `GetViewResponseSchema`. | "The issue asking what `GET /ui/view/:object/:type` answers AND its 2026-08-07 maintainer ruling"; "the shapes that ruling meant" | | `6239` | **404** — this removal's own change (recorded in the client and spec CHANGELOGs). | trailing id dropped | | `19751`, `19514` | The absent-value and scalar-array findings (this and its sibling entry's own cards). | leading ids dropped | | `6227` | `ViewFilterRuleSchema.value` shaped by its operator (the `view-filter-rule-value-shaped-by-operator` entry). | "since the value was first shaped by its operator"; "from then until this change" | | `objectui#9050` | Maintainer ruling C′, 2026-09-20: the protocol is the only refusal set; render time never throws on a protocol-valid document. | "the maintainer's ruling C-prime of 2026-09-20 on objectui's render-time filter converter — the protocol is the only refusal set, so a document it accepts never throws at render time"; the lesson quote 「the differences are the protocol's to close」 kept verbatim | | `objectui#9853` | Maintainer, 2026-09-24: the display default page size is 50, declared once in the protocol; objectui reads the spec default and never hardcodes it. | "the maintainer's ruling of 2026-09-24 set the platform display page size to 50, declared once in the protocol"; "(an earlier ruling on the grid's page size, which the page-size ruling restated)" | | (no id) `view-overlay-options-bag-judged` | Maintainer, 2026-09-24 (objectui's overlay-options card), option A: judge each `options.KIND` at the door. | 「其他同意」 replaced by "the maintainer's ruling of 2026-09-24" | **`package-` (8 ids and one ruling record)** | cited | what it decided (read) | how the text now carries it | |---|---|---| | `7705`, `7780` | Uninstall left orphaned rows (repaired); measured there: an org-less uninstall deleted every organization's rows. | "measured at 5 of 5 deleted, including a foreign org's, while uninstall's orphaned-row defect was being repaired"; "exactly as the orphaned-row repair left it" | | `#12` (short) | Not a tracker id: `rest-requireauth-default-flip` lived in protocol 12. | "(protocol 12)", the spelling four sibling entries use | | `19116` | Maintainer, 2026-09-23, option A: retire the three contract-map entries naming paths nothing mounts. | "Maintainer ruling of 2026-09-23 (option A: retire the three contract-map entries that name paths nothing mounts)" | | `18604` | The measurement on one `HttpDispatcher` over a real `SchemaRegistry`. | the measurement was already in the sentence; the id is dropped | | `18058` | `installPackage` rebound onto the serving `POST /api/v1/packages`. | "rebound by an earlier fix onto the serving POST /api/v1/packages" | | `5856869656` (record) | Maintainer, 2026-09-27, option A: the wrapped install form refuses an unknown top-level key by name. | "the maintainer's ruling of 2026-09-27, option A: the wrapped form refuses an unknown top-level key by name" | | `12038` | Maintainer, 2026-08-27, sub-question 3A: retire the false rollback declaration first, then author the true one. | "Maintainer ruling of 2026-08-27 on the client SDK's unbound response contracts, sub-question 3A: retire this false declaration first, then author the true one"; "the ruling's own survey" | | `11925` | Typed the SDK's un-annotated return values, keeping compile-time guards against a wrong-contract substitution. | "the change that typed the SDK's un-annotated return values left a compile-time guard"; "that negative guard" | | `3877` | Response bodies are never checked against the schemas that declare them. | "the hazard of response bodies never checked against the schemas that declare them, realised in the opposite direction" | **`object-` (11 ids)** | cited | what it decided (read) | how the text now carries it | |---|---|---| | `objectui#8221` | Maintainer, 2026-09-07, option B: the legacy string `sort` clause retired, one spelling (the array); its fourth item routes the `ComponentPropsMap` pull-back here. | "the maintainer's ruling of 2026-09-07 (option B) retired the legacy string `sort` clause"; "One item of that ruling"; the item's quote kept verbatim | | `8758` (objectui PR) | The consumer half: the string arm dropped from `convertSortToQueryParams`. | "the objectui change that drops the string arm from `convertSortToQueryParams`" | | `7751` | Maintainer, 2026-08-12, direction A: the `object-*` block family's props schemas enter `ComponentPropsMap`. | "a read-point record from the change that brought the `object-*` blocks into `ComponentPropsMap` (the maintainer's ruling of 2026-08-12)" | | `objectui#6207` | Found by objectui's declared-arm parity gate: two spec authorities disagreed on `data`'s kind. Ruled 2026-08-25, option A. | "(contract-vs-contract, found by objectui's declared-arm parity gate)"; "The maintainer's ruling of 2026-08-25 (option A)"; "closes the objectui finding that the two authorities disagreed" | | `objectui#5090` | The grid's registry declaration of `data` was aligned to `ViewData`. | "the authority objectui aligned the grid's registry declaration to" | | `objectui#4648` | Its deprecated-alias carve-out: object-grid's deprecated spellings (`staticData` among them) are not published as authoring surface. | "the deprecated `staticData` shortcut that objectui's deprecated-alias carve-out already refuses to publish as authoring surface" | | `19514`, `objectui#9050` | As in `view-`. | as in `view-` | | `objectui#4772` | The console's index fallback editor converged onto `IndexSchema`, dropping `where`. | "removed when objectui converged that editor onto `IndexSchema`"; "objectui then converged that editor" | | `4001` | As in `analytics-`. | "The unknown-key strictness campaign held this site open" (its internal batch and site numbers dropped) | | `5114` | The console's filter save answered 422: a strict schema refused a key the console itself writes. | "a measured risk, the kind that had already made a console save answer 422 (a strict schema refusing a key the console itself writes)" | **`sharing-` (11 ids)** | cited | what it decided (read) | how the text now carries it | |---|---|---| | `6206` | **404** — see Acceptance notes. | "completing the maintainer's ruling of 2026-08-07 on the share-link context (enforcement adjudicates on the WHOLE envelope, never a per-site subset)" | | `6430`, `6511` | The share-link enforcement path moved onto the full context under that ruling (`6511` answers 404). | "the share-link twin, which that same ruling moved onto the whole context" | | `6523`, `7068` (PR) | The sharing, approval and report contracts converged onto the full envelope (`6523` answers 404). | "the envelope the sharing, approval and report contracts have declared since they converged onto it"; "One change converged the contracts" | | `7140` (PR), `7206` (PR), `7070` | The four implementations re-annotated (sharing and audit, then approvals and reports); the split that deferred the type's deletion. | "two more re-annotated the four implementations (sharing and audit, then approvals and reports)"; "the deletion that split had deferred" | | `7218` | This retirement's own card. | trailing ids dropped | | `1878` | The metadata property liveness audit: security properties parsed but never enforced. | "The change came out of the metadata property liveness audit, which found security properties parsed but never enforced" | | `6350` | The stock reconciliation of the v17 train's breaking changesets, which backfilled this entry. | "registered late, by the stock reconciliation that compared the breaking changesets already on the v17 release train against this ledger" | **`audit-` (8 ids)** | cited | what it decided (read) | how the text now carries it | |---|---|---| | `7675` | Maintainer, 2026-08-12: two halves — build the cheap writers, retire the enum values with no feature; principle recorded 「空 widget + 永远查不到东西的过滤器是可见产品缺陷;审计面宁窄勿谎」 (kept verbatim, it is the lesson). | "Maintainer ruling 2026-08-12 on the audit log's writerless actions"; "that ruling's own survey" | | `8144`, `8145` | The `login` / `logout` writers on the auth session hooks; `config_change` from the settings service. | "(`login` / `logout` on the auth session hooks, `config_change` from the settings service)" | | `8147`, `8315` | The two retirements' own cards. | trailing ids dropped; "(triage 2026-08-13)" kept | | `1883`, `3146` | The undelete / purge permission lifecycle and the soft-delete recycle bin: both open, held, not declined. | "(an undelete / purge permission lifecycle and a soft-delete recycle bin, neither built yet)"; "both held open, not declined" | | `8011` | A credential-storage audit had to re-measure two "hashed at rest" comments; resolved by making the declaration cite its mechanism. | "(the shape a credential-storage audit had to settle by re-measuring two "hashed at rest" comments)" | **`flow-` (11 ids and two ruling records)** | cited | what it decided (read) | how the text now carries it | |---|---|---| | `4247` | `maxRetries` had two defaults (schema 0, engine 3). | the measurement was already in the sentence; the id is dropped | | `19961` | This refusal's own card. | leading id dropped | | `hotcrm#1555` | **403** — see Acceptance notes. | "a CRM application's lead-conversion flow rendered a refusal screen AND ran the conversion in one execution" | | `17322`, `17495` (in `surface`) | The node slot rebound to the edge door's rule at `registerFlow`, then at `objectstack validate`. | "The node slot joined this entry with the two later changes that rebound AutomationEngine.registerFlow and objectstack validate to the edge door's own rule" | | `15807`, `15430`, `15662` | The evaluated-slot rule: an `ast`-only envelope no engine can evaluate refused; a non-string node predicate refused at registration rather than answered a silent `false`; carried to the edge. | "The evaluated-slot rule, carried to the edge condition — the line that first refused an `ast`-only envelope no engine can evaluate, and refused a non-string node predicate at registration instead of letting the evaluator answer it a silent `false`" | | `5550509137` (record) | 2026-09-05: an `ast`-only envelope driven through `AutomationEngine.evaluateCondition` answers `false`. | "(measured on 2026-09-05 by driving an `ast`-only envelope through `AutomationEngine.evaluateCondition` directly)" | | `17493`, `5651023407` (record) | Maintainer ruling A, 2026-09-13: the two sibling predicate slots refuse a blank string at authoring. | "Maintainer ruling A of 2026-09-13: the two sibling predicate slots refuse a blank string at authoring" | | `15572` | Its pin had held the blank admission correct because parser and evaluator agreed. | "An earlier fix had pinned that admission as correct" | | `17322`, `15811` | The structural `config.condition` and every evaluated `source` refuse a blank. | "after the structural `config.condition` and a blank evaluated `source`" | **`http-` (11 ids)** | cited | what it decided (read) | how the text now carries it | |---|---|---| | `9650`, `9835`, `9834`, `10004` | The request counter, then the latency histogram, moved to the transport through the response-observing hook (`10004` answers 404). | "(the request counter, then the latency histogram, both through the response-observing hook the transport was given)" | | `5122`, `3977` | The origin cards of the two named sibling entries. | named by those entries' ids, which the sentence already carried | | `9834`, `5295` | This retirement's own cards. | trailing ids dropped | | `4938` | The CONFIG half of `system/http-server.zod.ts` removed. | "The first removed the CONFIG half"; "the config half's removal in this very file" | | `4834`, `4988`, `5055` | The dynamic plugin-loading family, the `ui/` interaction configs, the widget / i18n shapes — each removed by route 3. | "the earlier removals of the dynamic plugin-loading family, the `ui/` interaction configs and the widget / i18n shapes" | **Beyond the four- and five-digit regex.** Nine decision-batch numbers (`#27`, `#43`, `#57`, `#77`, `#117`, `#215`, `#217`, `#218`, `#227`), their item numbers, the campaign's internal batch and site numbers, `batch adjudication batch 4`, 「五问一批」 and "C′ item 1" were dropped; each sentence now carries the ruling's date and content. The provenance-only acknowledgements 「同意」 (×2), 「其他同意」 (×2), 「217 同意」, 「其他接受」 and 「9853 默认页大小改为50」 (the ruling's content, 50, is stated in words; the quote itself carried a tracker number) were replaced by the ruling's date and content. The lesson-bearing quotes are kept verbatim: 「the differences are the protocol's to close」 (×2), the sort ruling's fourth item, 「every other operator takes a scalar」, 「lowers to a deep-equality comparand」, 「`persistViewPatch` 只存 patch,不存 merged base」 and the audit ruling's 原则记录. ## Pin — widened, not weakened `packages/cli/test/migrate-meta-engine-guidance.test.ts`: `COVERED_PREFIXES` 17 → **27** (`rest-`, `analytics-`, `view-`, `package-`, `object-`, `sharing-`, `audit-`, `flow-`, `http-`, and `inline-` for the carry-over entry, the `inline-` family's only entry, now tracker-free); `package-` selects no `packages-` entry. `REWRITTEN` 113 → **147**: the 34 ids this stage rewrote for the first time (the 33 nine-family entries plus `inline-grid-column-currency-scale-refused`; `api-error-retry-after-unit-in-key` was already listed). The header comment names the new families; the three `it` blocks and every `expect` are textually unchanged. **Ablation, from committed HEAD `472ee29b82`, one lock turn** (`scripts/ablation-replace.mjs` wrap mode, a restore trap by absolute path with a blob check, `scripts/ablation-dist-preflight.mjs`): `registry.ts`, `http-server-runtime-vocabulary-retired`'s `reason`, anchor `behind it, vocabulary second. ADR-0049.` → `behind it, vocabulary second. ADR-0049, #5295.` (anchor 1 → 0, replacement 0 → 1, blob `06c06741` → `702e7370`). Mutate leg: spec build exit 0; the marker in 4 `dist` files; the pin **RED**, 1 failed | 2 passed: `http-server-runtime-vocabulary-retired: the printed guidance cites a tracker id: expected '#5295' to be undefined`. Restore proven by the tool (blob == HEAD `06c06741`, `git diff HEAD` empty). Restore leg: spec build exit 0; the marker absent from all 224 `dist` files with the tree clean; the pin **GREEN**, 3 passed; whole tree 0 dirty paths. No other test pins a removed number: on `main`, the tests naming any of the 35 entry ids (`sys-audit-log-retired-actions`, `plugin-rest-api.handler-status-retirement`, `rest-api-config-dead-keys-retirement`, `inline-grid-column-currency-scale-refused`, `component-object-grid-default-filters.pin`, `view-overlay-owner-hidden-retirement`, and two that name them in passing) assert ids, surfaces and prescriptions this PR does not move, not the prose it rewrites. ## Generated artifacts - `registry.ts`: 313 semantic, 232 retired-key, 206 retired-def; exactly the 35 ids differ (see the AST comparison above). - `spec-changes.json` and `docs/protocol-upgrade-guide.md`: only the protocol-17 entries appear in them, as the generators project; every changed line is a fragment of a changed entry's field. - `.changeset/20233-rest-analytics-view-package-object-sharing-audit-flow-http-migration-guidance-tracker-free.md`: `'@objectstack/spec': patch`, `Clause-②: no`. ## Verification (head `472ee29b82`) Every heavy run through `scripts/pm/os-verify-lock.sh`, each exit code written to disk before it was read. - **Build:** `turbo run build --concurrency=2 --filter='@objectstack/cli^...'` → Tasks: 58 successful, 58 total (VERDICT command-exit 0); plus the 10 packages outside that closure for the dual-build gate → Tasks: 68 successful, 68 total. - **Pin + neighbour:** `pnpm --filter @objectstack/cli exec vitest run --project integration --maxWorkers=2 test/migrate-meta-engine-guidance.test.ts test/migrate-meta-default-range.test.ts` → Test Files 2 passed (2), Tests 10 passed | 1 skipped (the default-range file's own `skipIf`). - **CLI unit (the tests that read the registry or `spec-changes.json`):** `spec-release-changes`, `meta.stored-flags`, `doctor-deprecation-hint-commands`, `vitest-tiers-partition` → Test Files 4 passed (4), Tests 48 passed (48). - **Spec:** `--project local` → Test Files 573 passed (573), Tests 16835 passed | 1 todo; `--project repo` → Test Files 39 passed (39), Tests 701 passed (701); `src/migrations/migrations.test.ts` alone → Tests 150 passed (150). - **Typecheck:** `pnpm --filter @objectstack/spec typecheck` exit 0 (test layer 53 files / 251 errors held); `pnpm --filter @objectstack/cli typecheck` exit 0 (3 files / 28 errors held). - **Gates:** `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derives 89 families over this diff; all 89 run, all exit 0; `--ran` → "89 derived, 89 run, 0 NOT-MEASURED, 0 UNRUN". Among them: `check:doc-authoring` ("16759 customer-facing string(s) across 1172 spec sources clean"), `check:generated` ("All 15 generated artifacts are up to date"), `check:migration-registry` ("registry.ts is current (313 semantic, 232 retired-key, 206 retired-def)"), `check:spec-changes`, `check:upgrade-guide`, `check:issue-citations` ("no issue citations added"), `check:org-identifier`, `check:nul-bytes`, `check:api-surface`, `check:authorable-surface`, `check:dual-build-cjs-loads` (104 require entry points across 66 packages load), `check:type-check-debt`, `check:adr-0087-registration`, `check:changeset-no-major`, `check:empty-changeset`. - **Lint, a proven narrowing:** `eslint --no-inline-config --format json` over the 37 changed `.ts` files → 37 files, 0 errors, 0 warnings, no file-ignored notice. Population read from `eslint.config.mjs` (`**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}` minus `NEVER_LINTED`); invariance: the config enables no type-aware linting (no `parserOptions.project`, no typed rules), so this diff cannot move any untouched file's verdict. The full `pnpm lint` is CI's. - **Mergeability:** `origin/main` is `1378ec7c`, three commits past the base, none touching the migration ledger or its projections; a driver-free bare-clone `merge-tree --write-tree` of the head against it exits 0 with no conflicted path. `registry.ts` is shared with open PRs `20504`, `20460` and `20458`, ordinary concurrency; no open PR touches any of the 35 entry files or the pin. ## Acceptance notes **404 and 403 ids, rewritten from what `main` records.** - `6206` (the share-link ruling of 2026-08-07): `packages/core/src/security/assemble-execution-context.ts` (the share-link copies omitted `accessible_org_ids`; both surfaces converted to pass the whole envelope) and `packages/plugins/plugin-approvals/CHANGELOG.md` ("applying the ... ruling — enforcement adjudicates on the whole envelope, never a per-site subset"). - `6523` / `6511`: the same CHANGELOG ("converged 36 contract signatures onto the complete `resolveAuthzContext` envelope"); `6430` (200) names the share-link half. - `6239`: `packages/client/CHANGELOG.md` records it as this removal itself (retire `ViewProtocol`'s five viewId-addressed methods); dropped. - `8758` as a bare id: its sentence names objectui, and `objectui#8758` answers 200 (the string `sort` clause retired). - `10004`: `packages/observability/src/semconv.ts` and the observability CHANGELOG pair it with `9834` as the histogram's move to the transport seam. - `14369`: `docs/qa/platform-checklist/areas/api-backend.json` ("the liveness census that found the block read by nothing") and the `14640` changeset (it enrolled four of the five sub-objects). `14691`: the same file records it as this retirement; dropped. - `17124`: `.changeset/17124-daterange-array-arm-arity.md` records the measurement (one authored document, four faces, three readings). - `hotcrm#1555` (403 cross-repo): `.changeset/15429-decision-edge-branching-first-match.md` records the case and the node (`lead_conversion.decision_duplicate`). **Observations, not filed.** - Carrier: this card's later stages · 339 prose-field sites, 40 short numbers and 3 `surface` sites remain in the other families (the largest: `actor-` 13, `hot-` 12, `external-` 11, `query-` 11, `delete-` 10, `stack-` 10); `stack-` and `turso-` have entries in open PRs. The pin file keeps its stage-1 name while holding twenty-seven families. - Carrier: the sibling card for comment and docblock lines · 832 comment lines in entry files still cite tracker ids (unchanged), including the header comments of `18.view-pagination-page-size-default-50.ts`, `18.view-overlay-options-bag-judged.ts`, `18.flow-decision-edge-branching-first-match.ts` (`hotcrm#1555`) and `18.analytics-authorable-unknown-keys-refused.ts`. Implemented-by: `claude/issue-20233-migrate-meta-tracker-free-stage-6` · `domain:spec` seat 4 dispatch, session `session_01ARcDurZ5j34RdqsGgc4jgH`. --- _Generated by [Claude Code](https://claude.ai/code/session_01ARcDurZ5j34RdqsGgc4jgH)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 05077d4 commit 288611e

40 files changed

Lines changed: 498 additions & 310 deletions

File tree

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
fix(spec): `os migrate meta` guidance for the `rest-*`, `analytics-*`, `view-*`, `package-*`, `object-*`, `sharing-*`, `audit-*`, `flow-*` and `http-*` migration entries states each lesson in words instead of citing tracker numbers
6+
7+
Clause-②: no
8+
9+
The ADR-0087 semantic entries of the `rest-*` family (the retired OpenAPI 3.1 block, the
10+
endpoint `handlerStatus` marker, the REST server's dead config keys and the REST plugin
11+
durations renamed with their unit), the `analytics-*` family (the retired query envelope,
12+
the unknown-key refusals on cubes, and the closed date-range vocabulary and two-bound
13+
window), the `view-*` family (the filter value shaped by its operator and its array and
14+
absent-value refusals, the retired view-management protocol, the page-size default and the
15+
judged overlay `options` bag), the `package-*` family (the explicit all-tenants uninstall,
16+
the retired unmounted contract-map entries and rollback response, and the strict wrapped
17+
install body), the `object-*` family (the array `sort` on object blocks, the converged grid
18+
`data`, the rule-array `defaultFilters` and the index unknown-key refusal), the `sharing-*`
19+
family (the retired `SharingExecutionContext` type and the reconciled rule recipients), the
20+
`audit-*` family (the audit-log action values no writer produced), the `flow-*` family (the
21+
retry count, the decision-branch and edge refusals, first-match edge branching and blank
22+
predicate slots) and the `http-*` family (the retired error counter and server runtime
23+
vocabulary) are printed by `os migrate meta` as the header, `why:` and `verify:` lines of a
24+
manual change. Their text sent the reader to issue-tracker, decision-batch and ruling-record
25+
numbers — some of which no longer resolve, and some in another repository — for what a
26+
ruling, measurement or fix had decided; it now says what was decided, in the sentence being
27+
read. ADR ids are kept. Two entries of other families are corrected the same way:
28+
`api-error-retry-after-unit-in-key` now dates the population ruling its clause describes,
29+
and `inline-grid-column-currency-scale-refused` names its two currency rulings by date
30+
instead of by record number.
31+
32+
Text only: no entry id, `from` / `to`, conversion or matching logic changes, and the chain
33+
rewrites exactly what it rewrote before. One entry's `surface` (the header line of
34+
`flow-edge-condition-evaluated-slot-source-required`) drops the two tracker numbers it
35+
carried and names nothing else differently. The generated migration registry,
36+
`spec-changes.json` and the protocol upgrade guide carry the same text.

‎docs/protocol-upgrade-guide.md‎

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

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

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
* of the COVERED families (`engine-*`, `ui-*`, `plugin-*`, `driver-*`,
66
* `kernel-*`, `system-*`, `datasource-*`, `filter-*`, `action-*`, `data-*`,
77
* `element-*`, `field-*`, `export-*`, `api-*`, `dataset-*`, `hook-*`,
8-
* `metadata-*`) states each lesson in words and carries no tracker number.
8+
* `metadata-*`, `rest-*`, `analytics-*`, `view-*`, `package-*`, `object-*`,
9+
* `sharing-*`, `audit-*`, `flow-*`, `http-*`, `inline-*`) states each lesson in
10+
* words and carries no tracker number.
911
*
1012
* ## What this pins
1113
*
@@ -78,6 +80,8 @@ const COVERED_PREFIXES = [
7880
'engine-', 'ui-', 'plugin-', 'driver-', 'kernel-', 'system-',
7981
'datasource-', 'filter-', 'action-', 'data-', 'element-',
8082
'field-', 'export-', 'api-', 'dataset-', 'hook-', 'metadata-',
83+
'rest-', 'analytics-', 'view-', 'package-', 'object-', 'sharing-',
84+
'audit-', 'flow-', 'http-', 'inline-',
8185
];
8286

8387
/**
@@ -91,10 +95,16 @@ const REWRITTEN = [
9195
'action-descriptor-resume-authority-default-flip',
9296
'action-engine-facade-find-query-envelope',
9397
'action-session-roles-to-positions',
98+
'analytics-authorable-unknown-keys-refused',
99+
'analytics-date-range-array-two-bounds-required',
100+
'analytics-query-request-envelope-retired',
101+
'analytics-time-dimension-date-range-vocabulary-closed',
94102
'api-assembled-entry-split',
95103
'api-error-retry-after-unit-in-key',
96104
'api-runtime-config-durations-unit-in-key',
97105
'api-runtime-create-withdrawn',
106+
'audit-log-action-enum-retired',
107+
'audit-log-action-restore-retired',
98108
'data-driver-find-stream-retired',
99109
'data-driver-query-omit-object',
100110
'data-engine-batch-retired',
@@ -148,9 +158,17 @@ const REWRITTEN = [
148158
'filter-query-face-comparands-refused-at-save',
149159
'filter-regex-options-retired',
150160
'filter-text-operator-declared-type-refused',
161+
'flow-decision-branch-expression-absent-refused',
162+
'flow-decision-edge-branching-first-match',
163+
'flow-edge-condition-evaluated-slot-source-required',
164+
'flow-predicate-slot-blank-string-refused',
165+
'flow-retry-max-retries-required',
151166
'hook-context-session-roles-retired',
152167
'hook-register-empty-object-target-refused',
153168
'hook-register-undispatched-lifecycle-event-refused',
169+
'http-request-errors-total-retired',
170+
'http-server-runtime-vocabulary-retired',
171+
'inline-grid-column-currency-scale-refused',
154172
'kernel-compatibility-matrix-estimated-migration-time-unit-in-key',
155173
'kernel-context-preview-mode-retired',
156174
'kernel-event-bus-retention-unit-in-key',
@@ -165,6 +183,14 @@ const REWRITTEN = [
165183
'metadata-manager-config-cache-ttl-unit-in-key',
166184
'metadata-manager-config-inert-cache-keys-retired',
167185
'metadata-plugin-additional-types-retired',
186+
'object-block-sort-item-array',
187+
'object-grid-data-view-data-converged',
188+
'object-grid-default-filters-rule-array',
189+
'object-index-unknown-keys-refused',
190+
'package-api-contracts-unmounted-entries-retired',
191+
'package-install-request-unknown-keys-refused',
192+
'package-rollback-response-retired',
193+
'package-uninstall-explicit-all-tenants',
168194
'plugin-activation-events-retired',
169195
'plugin-auto-restart-never-reinitialised',
170196
'plugin-manifest-contributes-dead-members-retired',
@@ -175,6 +201,12 @@ const REWRITTEN = [
175201
'plugin-runtime-family-retired',
176202
'plugin-security-scan-result-surface-retired',
177203
'plugin-security-scanner-retired',
204+
'rest-api-endpoint-handler-status-retired',
205+
'rest-api-plugin-durations-unit-in-key',
206+
'rest-server-config-dead-keys-retired',
207+
'rest-server-openapi31-block-removed',
208+
'sharing-execution-context-retired',
209+
'sharing-rule-recipient-reconcile',
178210
'system-cache-durations-unit-in-key',
179211
'system-collaboration-durations-unit-in-key',
180212
'system-failover-health-check-interval-unit-in-key',
@@ -199,6 +231,12 @@ const REWRITTEN = [
199231
'ui-record-blocks-unknown-keys-refused',
200232
'ui-reference-rail-unknown-keys-refused',
201233
'ui-widget-i18n-family-retired',
234+
'view-filter-rule-absent-value-refused',
235+
'view-filter-rule-scalar-operator-array-refused',
236+
'view-filter-rule-value-shaped-by-operator',
237+
'view-management-protocol-retired',
238+
'view-overlay-options-bag-judged',
239+
'view-pagination-page-size-default-50',
202240
];
203241

204242
interface FamilyEntry {

0 commit comments

Comments
 (0)