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
objectstack-fleet[bot] merged 3 commits into
Conversation
…t tracker numbers The reason / replacement / acceptanceCriteria text (and one surface) of the rest-, analytics-, view-, package-, object-, sharing-, audit-, flow- and http- ADR-0087 semantic entries, plus the two carry-overs from stage 5 (api-error-retry-after-unit-in-key names its 2026-09-05 population ruling; inline-grid-column-currency-scale-refused drops the ruling-record ids and batch numbers its field sibling already dropped). Text only. Claude-Session: https://claude.ai/code/session_01ARcDurZ5j34RdqsGgc4jgH Co-authored-by: Claude <noreply@anthropic.com>
… holds the stage-6 families registry.ts, spec-changes.json and docs/protocol-upgrade-guide.md are regenerated by their generators from the rewritten entries. The os migrate meta guidance pin covers rest-, analytics-, view-, package-, object-, sharing-, audit-, flow-, http- and inline-, and its REWRITTEN floor lists the 34 entries this stage rewrote for the first time. Claude-Session: https://claude.ai/code/session_01ARcDurZ5j34RdqsGgc4jgH Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARcDurZ5j34RdqsGgc4jgH Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 6 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 1 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 137 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin f67b7d8098ead2d4094c185262977c70d4d179b3 && git checkout f67b7d8098ead2d4094c185262977c70d4d179b3
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 31d281d3b2b855a7c7d00e48d2ac2fb22aa5c84b 472ee29b8293041df4664be19fd38ab1a068c136 && git checkout -B drift-repro 31d281d3b2b855a7c7d00e48d2ac2fb22aa5c84b && git merge --no-ff 472ee29b8293041df4664be19fd38ab1a068c136
node scripts/docs-audit/affected-docs.mjs --json 31d281d3b2b855a7c7d00e48d2ac2fb22aa5c84b
|
Contract reviewServed-tier: Head unmoved across the review (re-read at the end: same sha, ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS |
… commits that decided them (stage 4) (objectstack-ai#20548) Part of objectstack-ai#20234 Clause-②: no ## What changed This is stage 4 of the staged sweep: the `data/` remainder. It covers the six `packages/spec/src/data/` files stage 3 (PR objectstack-ai#20533, landed `03b19d9cfd`) left out because an open PR held them, and nothing else. They are `object.zod.ts`, `filter-logic-conformance.ts`, `object.form.ts`, `data-engine.zod.ts`, `data-engine.test.ts` and `hook.form.ts`. Later stages cover the other areas, so this PR says `Part of`. The census below measured all six. Three of them carry comment or docblock sites that cite a tracker number answering 404. `data-engine.zod.ts`, `data-engine.test.ts` and `hook.form.ts` carry none, so they are not in the diff. Every such site has been rewritten in ruling C+D's form C (comment 5749154545 on objectstack-ai#19123). That is **19 sites on 19 lines in 3 files, covering 9 numbers**. Each rewritten line now cites the commit in `origin/main` history that decided what the line describes, and it says in its own words what that commit decided. Where a PR number was already on the line (`PR objectstack-ai#13529`), it stays beside the commit as the link. No ADR or ruling-record file in `docs/adr/` or `scripts/adr-anchors/` records the decision behind any of the 9 numbers: a search for each number, with and without `#`, finds nothing there. So every anchor is a commit: **9 distinct shas**. Stage 3 had already read these commits and recorded them in PR objectstack-ai#20533's body. They were not copied from there. Each one was re-read against the current line it anchors: its own message or diff names the number it replaces, and it made the change the line describes. `object.zod.ts` and `filter-logic-conformance.ts` moved on `main` after stage 3 read them (PRs objectstack-ai#20521 and objectstack-ai#20523). Each site was therefore re-read at this base, `03b19d9cfd`. Only comments changed. Every source file keeps its line count (20 lines out, 20 in, over 3 files), so no line citation into these files moves. One of the 20 lines held no dead citation: `filter-logic-conformance.ts:249`, the first half of a sentence reflowed onto `:250`. No code token moves (see the guard below). **No tracker number is added.** Every tracker number on an added line was already in the hunk it replaces. `PR objectstack-ai#13529` stands on three added lines, and on the three removed lines of the same hunks. It is the link beside commit `9dac1ae01`, which stage 3 recorded the same way. No reference page under `content/docs/references/` moved: none of the rewritten docblocks projects into one (`check:docs` at the head: `226 generated files in sync`). The PR adds one `patch` changeset for `@objectstack/spec` (see Changeset below). ## Census: the six files, before and after **Instrument.** This is the instrument of stages 1 to 3. It sends REST `GET /repos/objectstack-ai/objectstack/issues/N` without following redirects, for every distinct number cited in `packages/spec/src/data`. The population is: - the citation gate's own exported `CITATION_RE` and `NON_CITATION_HEADS`, kept when the qualifier is none, `objectstack`, `objectstack-ai/objectstack`, `framework`, `pre-` or `post-`; - widened case-insensitively to `Pre-`, `POST-` and `Framework`, as in stage 3; - N of 100 or more, excluding `summon` heads. Each site is classified by the TypeScript parser as a line comment, a docblock, a block comment or a string. Two cross-checks close the population. First, a raw `#N` count in each of the six files equals the census rows plus the cross-repo rows in five files. In the other two it is one higher, and the extra is a second number after a slash inside a string (`objectstack-ai#5322/objectstack-ai#5134` in a `note`, `objectstack-ai#6262/objectstack-ai#6433` in a test title). Both answer 200. Second, no spelled citation (`issue N`, `PR N`, `card N`) occurs in any of the six. **Controls.** The lit controls were `objectstack-ai#16862`, `objectstack-ai#16847` and `objectstack-ai#17698`. The dead controls were `objectstack-ai#16714`, `objectstack-ai#16715` and `objectstack-ai#16697`. They were probed at the start, after every 100 numbers and at the end: 24 of 24 lit (200) and 24 of 24 dead (404) over 8 checkpoints in the base run, and 21 of 21 lit and 21 of 21 dead over 7 checkpoints in the head run. | reading | tree | numbers probed | 200 | 404 | 301 or other | dead sites, all of `data/` | dead sites, the six files | lines | files | numbers | |---|---|---|---|---|---|---|---|---|---|---| | before | base `03b19d9cfd`, probed 2026-09-29T01:11:59Z to 01:15:49Z | 601 | 572 | 29 | 0 | **77** | 19 | 19 | 3 | 9 | | after | head `53c9070dfd`, probed 2026-09-29T01:25:55Z to 01:29:35Z | 597 | 572 | 25 | 0 | **58** | 0 | 0 | 0 | 0 | The head probe found no number newly dead since the base probe: the same 572 numbers answer 200. The base reading of 77 equals stage 3's after reading at `96fd49caa2`. **Per file.** Cited sites here are every in-repo citation the population reads, live or dead. | file | cited sites (base) | dead sites before | by class | dead sites after | |---|---|---|---|---| | `object.zod.ts` | 120 | 15 | 8 docblock, 7 line comment | 0 | | `filter-logic-conformance.ts` | 97 | 3 | 2 docblock, 1 line comment | 0 | | `object.form.ts` | 31 | 1 | 1 line comment | 0 | | `data-engine.zod.ts` | 48 | 0 | | 0 | | `data-engine.test.ts` | 29 | 0 | | 0 | | `hook.form.ts` | 0 | 0 | | 0 | None of the 19 sites is a string, so this stage leaves no string token behind. ## Per-number table | number | sites / lines | anchor: what it decided | |---|---|---| | `objectstack-ai#8772` | 4 / 4, `object.zod.ts:2718`, `:2731`, `:2744`, `:2910` | `75b7c240a`: Direction 2 of the 2026-08-16 maintainer ruling. `ObjectSchema.create()` forces `required: true` on a `master_detail` reference under `controlled_by_parent` and refuses an explicit `required: false`. Raw parse stays tolerant, and runtime tolerance is the ruling's other half. Its changeset records the measurement that only the security gate closed that shape while the declaration surface accepted it (`:2731`). ADR-0055 stays cited beside it. It is the same anchor stage 3 gave `object.test.ts` | | `objectstack-ai#10165` | 2 / 2, `object.zod.ts:818`, `:1036` | `801296050`: `ttl.onlyWhen` with the canonical null predicate (maintainer ruling 2026-08-20, option A). One shared `onlyWhen` union, and both of `retention.onlyWhen`'s conflicts mirrored. Its diff wrote both `[objectstack-ai#10165]` blocks | | `objectstack-ai#10347` | 3 / 3, `object.zod.ts:1006`, `:1042`, `:1049` | `530c1df65`: the Archiver honours a declared `ttl`. It selects by the ttl cutoff on `ttl.field` when `ttl` is declared, and by `created_at` / `archive.after` otherwise (maintainer ruling 2026-08-20) | | `objectstack-ai#10527` | 1 / 1, `object.zod.ts:1005` | `5649efbf9`: refuses a diverging retention + ttl + archive triple at parse time. Its diff wrote this very paragraph | | `objectstack-ai#11195` | 1 / 1, `object.zod.ts:1791` | `b37231883`: `UserActionsConfigSchema` adopts `group` / `hideFields` / `rowColor` (the "last three" the line names) | | `objectstack-ai#11408` | 1 / 1, `object.zod.ts:2189` | `f11fc61c5`: declares `editMode` on the object document (maintainer ruling 2026-08-24, the `objectstack-ai#10144` declare-or-rule-out family, which stays cited) | | `objectstack-ai#13608` | 3 / 3, `object.zod.ts:2317`, `:2354`, `:2366` | `fc9ba76a5`: `publicSharing.eligibility` is held at redemption, not only at mint, fail-closed, with the undifferentiated `null` refusal. Its changeset heads with objectstack-ai#13608. It is the same anchor stage 1 gave `contracts/share-link-service.ts` | | `objectstack-ai#13195` | 3 / 3, `filter-logic-conformance.ts:190`, `:250`, `:525` | `9dac1ae01`, PR objectstack-ai#13529's squash commit, which stays as the link: `$exists` means has-a-value on driver-memory's live mingo path, its analytics face and driver-mongodb's `translateFilter` (the "last three key-presence exits") | | `objectstack-ai#12868` | 1 / 1, `object.form.ts:256` | `c459da6bc`: narrows the per-option `default` key out of the form-view options vocabulary, which offered a key nothing on that surface read. Commit `e808890958`, which wrote this line, names objectstack-ai#12868 as the same offer-vs-door class | The shas were checked at the base and again at `origin/main` `288611e3e5`. Every one matches exactly one commit (`git rev-parse --disambiguate`, count 1). Every one is an ancestor (`git merge-base --is-ancestor`, exit 0 for 9 of 9). The control leg `e9584681a4` also exits 0, and the repository is not shallow. For each commit, a grep of its own message or diff finds the number it replaces. Seven of the nine name it in the message. `fc9ba76a5` names it in its diff (20 lines, including its changeset heading), and so does `c459da6bc` (8 lines, including its changeset heading). Wordings to check, each true of its commit: - `object.zod.ts:2731` now reads 「closes that shape, and commit 75b7c24 records that the declaration and the enforcement disagree」. The measurement was the card's. The commit's changeset records it: "only the security gate closed that shape while the declaration surface accepted it". - `object.zod.ts:2189` reads 「Declared here by commit f11fc61's maintainer ruling」, and `:2744` reads 「the other half of commit 75b7c24's ruling」. This is stage 3's wording for the same relation (`object.test.ts`, 「the other half of commit 75b7c24's ruling」): the commit that landed the ruling and quotes it. - `object.zod.ts:1049` reads 「That is the whole of what [commit 530c1df] changed here」. Commit `52db1d1f2a` wrote the paragraph. `530c1df65` is the change it describes. ## Mechanical guard: no code token moves The check compares leaf tokens with comments stripped, base `03b19d9cfd` against head `53c9070dfd`. It uses the TypeScript parser's leaf tokens (TypeScript from the head's lockfile), so template literals are scanned in context, and it excludes JSDoc nodes. It ran over all 3 touched `.ts` files. It is the stage-3 instrument, unchanged. - Real run: 13,624 base tokens (object.zod.ts 8,774, object.form.ts 3,226, filter-logic-conformance.ts 1,624), **0 files with a token change** (exit 0). - Comment-insertion control (`object.form.ts`): 0 files changed, as expected (exit 0). - Positive control (a declaration inserted into `object.zod.ts`): 1 file reads DIFFER at token 1629 (exit 1). - Positive control (one digit changed inside the `objectstack-ai#5322/objectstack-ai#5134` `note` string in `filter-logic-conformance.ts`): 1 file reads DIFFER at token 889 (exit 1). Line balance: `object.zod.ts` +15 / -15, `filter-logic-conformance.ts` +4 / -4, `object.form.ts` +1 / -1. Line counts are equal at base and head: 3,240, 621 and 751. ## Changeset This change ships bytes, so a `patch` changeset for `@objectstack/spec` is included. It says only that the provenance comments were re-anchored. `Clause-②: no`: no export, key, value or type moves (the guard above). Measured on the head's built package: `object.zod.ts` is `src/**/*.zod.ts`, which `files[]` ships verbatim. The rewritten comments also reach `dist`: - `9dac1ae01` appears in `dist/data/index.d.ts` (the `filter-logic-conformance.ts` docblock) and in 4 bundled `.js` files; - `fc9ba76a5`, `f11fc61c5` and `b37231883` each appear in 22 bundled `.js` files, and `c459da6bc` in 12; - the positive control, the pre-existing `object.zod.ts` sentence 「Fail-CLOSED at both points」, appears in 11 bundled `.js` files. ## Gates (head `53c9070dfd`) - **Citation judging pass, run as CI runs it:** `pnpm check:issue-citations && node scripts/check-issue-citations.mjs` exits 0. The self-test passes 73 cases in 7 batteries. The live run judged 6 citations across 3 files: 3 resolve (`objectstack-ai#9138` twice, `objectstack-ai#11410`) and 3 resolve as a pull request (`objectstack-ai#13529`, the link). - **Doc authoring:** `pnpm check:doc-authoring` exits 0. - **Derived gates:** `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` at the head derived 79 families, and all 79 exit 0. `--ran` reports 79 run, 0 NOT MEASURED, 0 unrun, and exits 0. A full `turbo run build` of `./packages/*` ran first, under the shared verify lock: 71 of 71 tasks, VERDICT command-exit 0. So no gate met an unbuilt prerequisite. - `pnpm --filter @objectstack/spec run check:generated`: under the lock against that build, `All 15 generated artifacts are up to date`, VERDICT command-exit 0. - **Tests and typecheck:** - `pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 src/data` under the lock: Test Files 107 passed (107), Tests 3527 passed, 1 todo (3528), VERDICT command-exit 0. It covers every test in `data/`, among them `object.test.ts`, which reads these schemas. - The 13 spec suites outside `src/data` that read the touched files' source text or pin their line numbers, under the lock: Test Files 13 passed (13), Tests 544 passed (544). They are stage 3's 12 (`scripts/{file-description,root-index,skill-map-guards,strictness-ledger}.test.ts`, `src/api/api-entry-graph.pin.test.ts`, `src/contracts/scoped-context.test.ts`, `src/shared/{alias-integrity,evaluated-slot-population,retired-key-migrate-sentence}.test.ts`, `src/system/constants/platform-object-names.test.ts`, `src/type-alias-convention.pin.test.ts`, `src/ui/dashboard.test.ts`) plus `src/shared/union-author-message-pins.test.ts`, which pins `data/object.zod.ts:855`. - `pnpm --filter @objectstack/spec typecheck` under the lock exits 0, including `check:test-typecheck` (53 files, 251 errors, 138 pinned signatures held). - **Lint, as a proven narrowing at the head:** `eslint --no-inline-config --format json` over the 3 touched `.ts` files gives 3 files, 0 errors and 0 warnings. All 3 are in eslint's own population (`isPathIgnored` is false for each). `eslint.config.mjs` never enables type-aware linting (no `parserOptions.project`, which its own line 328 states), so a comment edit here cannot move the verdict on any untouched file. The repo-wide `pnpm lint` is CI's run. ## Acceptance notes - **Base.** The branch forked from `03b19d9cfd`, stage 3's landing. `origin/main` then moved two commits (`05077d4c26`, PR objectstack-ai#20532, and `288611e3e5`, PR objectstack-ai#20536), and neither touches `data/`. `dispatch-gates` flagged its derivation as stale because `scripts/regen-artifacts.mjs` had moved, so `origin/main` was merged in (`53c9070dfd`, a clean merge with no driver-deferred path) before the gates ran. The PR's delta against `origin/main` is exactly its 4 files. `origin/main` has since moved two more commits: `7e36a3cd7c` (PR objectstack-ai#20531) and `ba5927f714` (PR objectstack-ai#20460). Neither touches `data/` or anything the gate derivation reads, and a re-derivation prints the same 79 commands. A no-driver `merge-tree` of the head onto `ba5927f714`, from a bare shared clone, exits 0. So there is no second merge. - **Open PRs, re-read at 2026-09-29T02:01Z:** 9 open PRs, and none touches any of the six files. The `data/` files open PRs touch are objectstack-ai#20458's `analytics*` files, objectstack-ai#20504's `driver/turso.*`, and objectstack-ai#20545's `filter-number-comparand-declared-type.*`, which is disjoint. Since the claim, PR objectstack-ai#20460 has landed (`ba5927f714`) without touching `filter-subtree-provenance.ts`. That file's 3 dead sites are outside this claim's fence, so they are left for a later stage. - **The rung.** Two anchored changes also have ADR-0087 entries in `packages/spec/src/migrations`: `cbp-master-detail-required-forced` for objectstack-ai#8772, and `form-view-option-default-retired` for objectstack-ai#12868. The second entry's own header names commit `c459da6bc`. This PR takes the commit rung, as stages 1 to 3 did. The D3 id is the more durable in-repo record, if the ruling's first rung is later read to include those entries. - **What stays in `data/` after this stage: 58 dead sites.** - **12 comment sites in files other open work still holds.** `analytics.zod.ts`, `analytics-strictness-batchd.test.ts` and `analytics-date-range-two-bound-window.test.ts` hold 5 (objectstack-ai#20300, PR objectstack-ai#20458). `driver/turso.zod.ts` and `driver/turso.test.ts` hold 4 (objectstack-ai#20437, PR objectstack-ai#20504). `filter-subtree-provenance.ts` holds 3. It was held by objectstack-ai#20367 and is now free (see above). - **3 comment sites stage 3 left on purpose.** They are the test-read `[objectstack-ai#6259]` marker at `api-derivation.ts:163`, the test comment at `api-derivation.test.ts:232` that names it, and `field.zod.ts:370`, whose `objectstack-ai#6111` is objectui's number. - **43 string sites**, left as tokens: 41 test strings (2 of them in the held analytics and turso test files) and the 2 exported `AGGREGATION_CASES` note strings in `aggregation-conformance.ts` (`:398`, `:407`, objectstack-ai#11065), which objectstack-ai#20489's claim holds. - **Outside `data/`,** the card's other remaining items are unchanged: the migrations and ui areas, the `liveness/**` notes, the `why` strings, the `PROVENANCE_WAIVERS` reason, and `rest-server.zod.ts`. - **The citation gate's reach.** It defers `packages/**/*.test.ts`. No test file is touched here, so all 3 touched files are in its judging population. --- _Generated by [Claude Code](https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
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. Onesurfacemoves, under ruling A of the stage-1 ACCEPT (5858839916): it carried two tracker numbers.What this does
os migrate metaprints every ADR-0087 semantic entry it crosses as one block:⚠ [protocol N] SURFACE → REPLACEMENT, thenwhy:(the entry'sreason) andverify:(itsacceptanceCriteria). 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 card19123(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-andhttp-: 122 sites → 0 in the three prose fields and 2 → 0 insurface, 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 andApiError.retryAfternow 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 itsfield-sibling.Each site now says what the cited ruling, measurement or fix decided. ADR ids stay, and so does
Prime Directive #10in18.package-manifest-version-grammar-enforced(a rule in AGENTS.md, as stages 2–5 kept#10/#12).registry.ts,spec-changes.jsonanddocs/protocol-upgrade-guide.mdare 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 evaluatesreplacement,reason,acceptanceCriteriaand (separately)surface, joining string literals with+, and counts#followed by 4 or 5 digits at a word boundary. On basefb386074it reads the whole tree at 461 sites / 5surface/ 50 short, which is the stage-5 record's after-count. Unevaluable fields: 0. 313 semantic entries.Controls, same run.
17.aggregation-node-distinct-retired.tsreads 7 sites (replacement 1, reason 6), before and after./// 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 insurface;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, 1hotcrm#), 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,
surface0; the seventeen earlier families still 0; whole tree 461 → 339,surface5 → 3, short 50 → 40 (the twoinline-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.17.analytics-query-request-envelope-retired17.audit-log-action-enum-retired17.audit-log-action-restore-retired17.flow-retry-max-retries-required17.http-request-errors-total-retired17.http-server-runtime-vocabulary-retired17.package-uninstall-explicit-all-tenants17.rest-server-openapi31-block-removed17.sharing-execution-context-retired17.sharing-rule-recipient-reconcile17.view-filter-rule-value-shaped-by-operator17.view-management-protocol-retired18.analytics-authorable-unknown-keys-refused18.analytics-date-range-array-two-bounds-required18.analytics-time-dimension-date-range-vocabulary-closed18.api-error-retry-after-unit-in-key18.flow-decision-branch-expression-absent-refused18.flow-decision-edge-branching-first-match18.flow-edge-condition-evaluated-slot-source-required18.flow-predicate-slot-blank-string-refused18.inline-grid-column-currency-scale-refused18.object-block-sort-item-array18.object-grid-data-view-data-converged18.object-grid-default-filters-rule-array18.object-index-unknown-keys-refused18.package-api-contracts-unmounted-entries-retired18.package-install-request-unknown-keys-refused18.package-rollback-response-retired18.rest-api-endpoint-handler-status-retired18.rest-api-plugin-durations-unit-in-key18.rest-server-config-dead-keys-retired18.view-filter-rule-absent-value-refused18.view-filter-rule-scalar-operator-array-refused18.view-overlay-options-bag-judged18.view-pagination-page-size-default-50The 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 landedrest-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 (
fb386074and the head) are parsed and compared: every import declaration; every property other than the three prose fields, by evaluated value (soid,from/toand 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.surfaceis 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-Asurfaceof18.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 mutatedid, a mutated comment, a mutatedsurfacewhose 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 onsurface/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 change8758is "objectui PR" in its sentence). The five ruling-record comment ids were read by id.hotcrm#1555answers 403 to this session and is rewritten from whatmainrecords. 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; control6209answers 200); their sentences are rewritten from whatmainrecords, listed in Acceptance notes.rest-(14 ids)31974579openApi31declared, never enforced).openApi31precedent)"13823handlerStatuswith a tombstone; enforce excluded; the class direction recorded for two sibling cards.5384ApiEndpointSchemawas still an open object afterapibecame a registered metadata type; closed strictly.apibecame a registered metadata type"13808(PR)handlerStatusas working machinery.3950(PR)13612,13613EventNameSchema's binding schemas with no runtime consumer.14478,15677api/stack of that ruling.14369RestServerConfigsub-objects"11984normalizeConfigcast the four sub-objects instead of parsing them; it parses them since.1479614691analytics-(10 ids)3891/analytics/querywith no analytics service installed) dropped the caller's identity and the contract'swherefilter at its door.wherefilter at the door)"4001387810414MetricSchema.filterswas authorable with zero consumers; removed.metric-filters-removed"175981632217593(PR)17124160414614view-(10 ids)5869,6209(PR)in/not_inanswered 500; now a named 400 INVALID_FILTER.(5869)inacceptanceCriteriadropped56855948GET /ui/view/:object/:typeanswers, and its 2026-08-07 ruling, both readGetViewResponseSchema.GET /ui/view/:object/:typeanswers AND its 2026-08-07 maintainer ruling"; "the shapes that ruling meant"623919751,195146227ViewFilterRuleSchema.valueshaped by its operator (theview-filter-rule-value-shaped-by-operatorentry).objectui#9050objectui#9853view-overlay-options-bag-judgedoptions.KINDat the door.package-(8 ids and one ruling record)7705,7780#12(short)rest-requireauth-default-fliplived in protocol 12.1911618604HttpDispatcherover a realSchemaRegistry.18058installPackagerebound onto the servingPOST /api/v1/packages.5856869656(record)12038119253877object-(11 ids)objectui#8221sortclause retired, one spelling (the array); its fourth item routes theComponentPropsMappull-back here.sortclause"; "One item of that ruling"; the item's quote kept verbatim8758(objectui PR)convertSortToQueryParams.convertSortToQueryParams"7751object-*block family's props schemas enterComponentPropsMap.object-*blocks intoComponentPropsMap(the maintainer's ruling of 2026-08-12)"objectui#6207data's kind. Ruled 2026-08-25, option A.objectui#5090datawas aligned toViewData.objectui#4648staticDataamong them) are not published as authoring surface.staticDatashortcut that objectui's deprecated-alias carve-out already refuses to publish as authoring surface"19514,objectui#9050view-.view-objectui#4772IndexSchema, droppingwhere.IndexSchema"; "objectui then converged that editor"4001analytics-.5114sharing-(11 ids)62066430,65116511answers 404).6523,7068(PR)6523answers 404).7140(PR),7206(PR),7070721818786350audit-(8 ids)76758144,8145login/logoutwriters on the auth session hooks;config_changefrom the settings service.login/logouton the auth session hooks,config_changefrom the settings service)"8147,83151883,31468011flow-(11 ids and two ruling records)4247maxRetrieshad two defaults (schema 0, engine 3).19961hotcrm#155517322,17495(insurface)registerFlow, then atobjectstack validate.15807,15430,15662ast-only envelope no engine can evaluate refused; a non-string node predicate refused at registration rather than answered a silentfalse; carried to the edge.ast-only envelope no engine can evaluate, and refused a non-string node predicate at registration instead of letting the evaluator answer it a silentfalse"5550509137(record)ast-only envelope driven throughAutomationEngine.evaluateConditionanswersfalse.ast-only envelope throughAutomationEngine.evaluateConditiondirectly)"17493,5651023407(record)1557217322,15811config.conditionand every evaluatedsourcerefuse a blank.config.conditionand a blank evaluatedsource"http-(11 ids)9650,9835,9834,1000410004answers 404).5122,39779834,52954938system/http-server.zod.tsremoved.4834,4988,5055ui/interaction configs, the widget / i18n shapes — each removed by route 3.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_PREFIXES17 → 27 (rest-,analytics-,view-,package-,object-,sharing-,audit-,flow-,http-, andinline-for the carry-over entry, theinline-family's only entry, now tracker-free);package-selects nopackages-entry.REWRITTEN113 → 147: the 34 ids this stage rewrote for the first time (the 33 nine-family entries plusinline-grid-column-currency-scale-refused;api-error-retry-after-unit-in-keywas already listed). The header comment names the new families; the threeitblocks and everyexpectare textually unchanged.Ablation, from committed HEAD
472ee29b82, one lock turn (scripts/ablation-replace.mjswrap mode, a restore trap by absolute path with a blob check,scripts/ablation-dist-preflight.mjs):registry.ts,http-server-runtime-vocabulary-retired'sreason, anchorbehind it, vocabulary second. ADR-0049.→behind it, vocabulary second. ADR-0049, #5295.(anchor 1 → 0, replacement 0 → 1, blob06c06741→702e7370). Mutate leg: spec build exit 0; the marker in 4distfiles; 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 == HEAD06c06741,git diff HEADempty). Restore leg: spec build exit 0; the marker absent from all 224distfiles 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.jsonanddocs/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.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.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 ownskipIf).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).--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.tsalone → Tests 150 passed (150).pnpm --filter @objectstack/spec typecheckexit 0 (test layer 53 files / 251 errors held);pnpm --filter @objectstack/cli typecheckexit 0 (3 files / 28 errors held).node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderives 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.eslint --no-inline-config --format jsonover the 37 changed.tsfiles → 37 files, 0 errors, 0 warnings, no file-ignored notice. Population read fromeslint.config.mjs(**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}minusNEVER_LINTED); invariance: the config enables no type-aware linting (noparserOptions.project, no typed rules), so this diff cannot move any untouched file's verdict. The fullpnpm lintis CI's.origin/mainis1378ec7c, three commits past the base, none touching the migration ledger or its projections; a driver-free bare-clonemerge-tree --write-treeof the head against it exits 0 with no conflicted path.registry.tsis shared with open PRs20504,20460and20458, ordinary concurrency; no open PR touches any of the 35 entry files or the pin.Acceptance notes
404 and 403 ids, rewritten from what
mainrecords.6206(the share-link ruling of 2026-08-07):packages/core/src/security/assemble-execution-context.ts(the share-link copies omittedaccessible_org_ids; both surfaces converted to pass the whole envelope) andpackages/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 completeresolveAuthzContextenvelope");6430(200) names the share-link half.6239:packages/client/CHANGELOG.mdrecords it as this removal itself (retireViewProtocol's five viewId-addressed methods); dropped.8758as a bare id: its sentence names objectui, andobjectui#8758answers 200 (the stringsortclause retired).10004:packages/observability/src/semconv.tsand the observability CHANGELOG pair it with9834as 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 the14640changeset (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.mdrecords the measurement (one authored document, four faces, three readings).hotcrm#1555(403 cross-repo):.changeset/15429-decision-edge-branching-first-match.mdrecords the case and the node (lead_conversion.decision_duplicate).Observations, not filed.
surfacesites remain in the other families (the largest:actor-13,hot-12,external-11,query-11,delete-10,stack-10);stack-andturso-have entries in open PRs. The pin file keeps its stage-1 name while holding twenty-seven families.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) and18.analytics-authorable-unknown-keys-refused.ts.Implemented-by:
claude/issue-20233-migrate-meta-tracker-free-stage-6·domain:specseat 4 dispatch, sessionsession_01ARcDurZ5j34RdqsGgc4jgH.Generated by Claude Code