feat(spec)!: retire the inner name on cube measures and dimensions — the record key is the member's name (#20300) - #20458
Conversation
…the record key is the member's name (WIP: schema, registry, ledger) Claude-Session: https://claude.ai/code/session_01B3TqpoQbTAfG7G74GMDWNW Co-authored-by: Claude <noreply@anthropic.com>
… changeset for the retired inner member name (WIP) Claude-Session: https://claude.ai/code/session_01B3TqpoQbTAfG7G74GMDWNW Co-authored-by: Claude <noreply@anthropic.com>
…teral Claude-Session: https://claude.ai/code/session_01B3TqpoQbTAfG7G74GMDWNW Co-authored-by: Claude <noreply@anthropic.com>
… only Claude-Session: https://claude.ai/code/session_01B3TqpoQbTAfG7G74GMDWNW Co-authored-by: Claude <noreply@anthropic.com>
…be-member-inner-name-retired
…tree Claude-Session: https://claude.ai/code/session_01B3TqpoQbTAfG7G74GMDWNW Co-authored-by: Claude <noreply@anthropic.com>
tsc found the two in driver-memory; the same map-builder shape recurs in service-analytics and runtime fixtures, and a spec scripts fixture carried the key into a leg that would then fail for the tombstone's reason. Claude-Session: https://claude.ai/code/session_01B3TqpoQbTAfG7G74GMDWNW Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B3TqpoQbTAfG7G74GMDWNW Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 3 package(s): 13 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 4 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 f7f04049f450ca34255c66ffe905ddecff3abfcf && git checkout f7f04049f450ca34255c66ffe905ddecff3abfcf
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin c876a7426d930e9e8d310fdffb1f64ae836cdced d977ee667de307147c95e08c2abfc958ac680493 && git checkout -B drift-repro c876a7426d930e9e8d310fdffb1f64ae836cdced && git merge --no-ff d977ee667de307147c95e08c2abfc958ac680493
node scripts/docs-audit/affected-docs.mjs --json c876a7426d930e9e8d310fdffb1f64ae836cdced
|
…othing (#20300) Claude-Session: https://claude.ai/code/session_01B3TqpoQbTAfG7G74GMDWNW Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Head confirmed against the API at review time ( ① Derived judgments
② Semver level
③ Boundary flagsThe five deviations.
The two out-of-scope findings.
The NOT MEASURED items are all CI-covered on this head: the two dogfood tests and Check-runs on this head, read at 2026-09-28T15:19Z, after the re-triggers from the push and the body edit had all completed — the seven required contexts:
Every other check-run on the head is Tier H status.
Implemented-by: VERDICT: PASS Generated by Claude Code |
维护者速读(终稿)改了什么:分析立方体(cube)的度量和维度不再接受内部的
为什么改:
风险与代价(含回滚):
席位意见:建议合并。
你要做的:审阅后直接合并本 PR。您的人工合并本身就是审核记录。本席今日下班,如果改为点 Approve,就由接任席位落地。 Generated by Claude Code |
…be-member-inner-name-retired # Conflicts: # packages/spec/vitest.repo-tests.json
…ublished, so the package label 17.4.0 Claude-Session: https://claude.ai/code/session_01B3TqpoQbTAfG7G74GMDWNW Co-authored-by: Claude <noreply@anthropic.com>
…red inner member name Two files landed on main after this branch retired the key: four literal members and one map builder. The tree-scoped absence pin found the literals; the AST sweep found the builder, the pin's declared blind spot. Claude-Session: https://claude.ai/code/session_01B3TqpoQbTAfG7G74GMDWNW Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: The head moved during this review. The brief named ① Derived judgments
② Semver level
③ Boundary flagsThe prior records' flags, answered. The claim's declaration line, the body's overstatement, the two escalations (#20464, #20465), the objectui pin and the five deviations stand as What the merge introduced. Does the head merge into current Tier H status, now approved.
Observation, not a gate. The CLI guidance pin's covered prefixes do not include Implemented-by: VERDICT: PASS Generated by Claude Code |
维护者速读 · 补充二(最终 head
|
…be-member-inner-name-retired # Conflicts: # packages/spec/src/migrations/registry.ts
|
Director note · record at head · 2026-09-29T05:29Z The head moved again after the PASS |
Contract reviewServed-tier: A delta record on ① Derived judgments
② Semver levelNothing moved. ③ Boundary flagsMerges clean into current Check-runs on this head: 35 runs, none in progress, none red (re-polled at 2026-09-29T05:59Z). 33 success, including every required context: Tier H.
Observation, not a gate. The body's patch-round section still ends at Implemented-by: VERDICT: PASS Generated by Claude Code |
|
Re-queued by the
维护者:无需操作,合入后席位收尾。 Generated by Claude Code |
… 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>
…/src remainder to the commits and ADRs that decided them (stage 6) (objectstack-ai#20606) Part of objectstack-ai#20234 Clause-②: no Stage 6 of the staged sweep: the `packages/spec/src` remainder outside `migrations/` and the held files. Its claim is `5884233505`, with 22 files named there. Every comment or docblock line in those files that cited a tracker number answering 404 now cites what decided its rule, in ruling C+D's form C. That is the commit on `main` that decided the rule, or, for one number, the ADR amendment that records the ruling. Each line says in its own words what was decided. Comments only: 66 lines out, 66 in, across 21 files. No code token, string literal, `describe()` text or message-catalog string moves. Two dead sites stay byte-identical, because a test reads each one by literal. The census is the gate's own `node scripts/check-issue-citations.mjs --census --json`, filtered to the 22 paths. Before: base `c876a7426d`, board enumerated (185 pages, frontier objectstack-ai#20590). After: head `9d63cb6548`, frontier objectstack-ai#20604. ## Measurement | file (under `packages/spec/src/`) | dead before | after | numbers, then anchor | |---|---:|---:|---| | `api/rest-server.zod.ts` | 11 | 0 | objectstack-ai#14691 ×9 to `b3a63d32c`; objectstack-ai#14369 ×2 to `a3d5724c8` | | `system/i18n-resolver.ts` | 14 | 0 | objectstack-ai#12961 ×6 to `901355c3b`; objectstack-ai#13218 ×4 to `c45d8e6b4`; objectstack-ai#8460 ×2 to ADR-0029 D9.2a; objectstack-ai#10926 to `d173125fb`; objectstack-ai#13109 to `8b236c826` | | `system/operation-message.ts` | 4 | 0 | objectstack-ai#12493 ×4 to `aa5994e17` (docblock lines only) | | `system/translation.zod.ts` | 2 | 0 | objectstack-ai#10926 ×2 to `d173125fb` | | `system/core-services.zod.ts` | 1 | 0 | objectstack-ai#6604 to `d127ff002` | | `system/dev-login.zod.ts` | 1 | 0 | objectstack-ai#17081 to `24d622b94` (objectstack-ai#17556 stays, 200) | | `system/environment-artifact.zod.ts` | 1 | 0 | objectstack-ai#11333 to `e58ea8b38` (objectstack-ai#14865 and objectstack-ai#13457 stay, 200) | | `shared/identifiers.zod.ts` | 7 | 0 | objectstack-ai#12245 ×2 to `c41b42e8d`; objectstack-ai#12144 ×2 to `3a04b0125`; objectstack-ai#12194 and objectstack-ai#12176 (:140-141) to `311433f6b`; objectstack-ai#12176 (:189) to `7986d973f` | | `shared/metadata-collection.zod.ts` | 1 | 0 | objectstack-ai#10485 to `35ad101bc` | | `index.ts` (package root) | 6 | 0 | objectstack-ai#11350 ×5 to `ece4dad31` (objectstack-ai#11709 stays, 200); objectstack-ai#10485 to `35ad101bc` | | `automation/control-flow.zod.ts` | 1 | 0 | objectstack-ai#14419 to `c5a7448d5` (objectstack-ai#14954 stays, 200) | | `automation/execution.zod.ts` | 1 | 0 | objectstack-ai#13681 to `18d816a50` | | `automation/index.ts` | 1 | 0 | objectstack-ai#16659 to `ecdfc9411` | | `automation/schedule-organization.zod.ts` | 1 | 0 | objectstack-ai#16659 to `ecdfc9411` | | `ai/index.ts` | 1 | 0 | objectstack-ai#11350 to `ece4dad31` | | `identity/identity.zod.ts` | 1 | **1** | objectstack-ai#8715 kept at :230 (a test reads it); `2c86fe3ea` added on :231 | | `security/explain.zod.ts` | 1 | 0 | objectstack-ai#8714 to `42b05af89` | | `security/public-form.ts` | 1 | 0 | objectstack-ai#6640 to `2ab1257c9` | | `data/api-derivation.ts` | 1 | **1** | objectstack-ai#6259 kept at :163 (two tests read it); `6968885ef` already on :164; file untouched | | `data/driver/turso.zod.ts` | 2 | 0 | objectstack-ai#6345 ×2 to `e2798fab7` | | `conversions/walk.ts` | 1 | 0 | objectstack-ai#13031 to `b799ac553` | | `meta-spelling/metadata-url-spelling.ts` | 2 | 0 | objectstack-ai#10485 ×2 to `35ad101bc` | | **22 files** | **62** | **2** | 26 numbers, 24 removed: 24 distinct shas and 1 ADR | Per-file counts at base equal the claim's (census `5884031174` at `f11b5f20a2`) in all 22 files. A second instrument agrees site for site: every `#N` in the 22 files, classified by the TypeScript parser, and each of 201 distinct numbers probed by REST `issues/N` without redirects. It found 484 sites, all in comments and none in a string, 26 dead numbers and 62 dead sites. Its string-class positive control found 19 string sites in `api/rest-server.test.ts`. Head: 424 sites and 177 numbers, 175 answer 200 (the same 175), and 2 answer 404 (the two kept sites). Lit controls objectstack-ai#16862, objectstack-ai#16847 and objectstack-ai#17698 answered 200 at every checkpoint (4 at base, 3 at head); dead controls objectstack-ai#16714, objectstack-ai#16715 and objectstack-ai#16697 answered 404 at every checkpoint. ## Why each anchor decides its line Each sha resolves uniquely, is an ancestor of `origin/main` (and of the base), has one parent, and names the number it replaces in its own message or diff. Each was read for the rule its line states. - **objectstack-ai#14691 to `b3a63d32c`**: the retirement of the ten inert `RestServerConfig` keys under ADR-0049 enforce-or-remove. Its own `rest-server.zod.ts` diff wrote all nine `objectstack-ai#14691` lines: the tombstones, the dropped `CrudEndpointPatternSchema` and the `routes` block. - **objectstack-ai#14369 to `a3d5724c8`**: seeded the four `RestServerConfig` liveness ledgers "from the census filed with objectstack-ai#14369" (its changeset heading names the number). It records both facts the two lines state: every CRUD route is mounted from hard-coded method/path pairs, and `routes` is parsed, defaulted and normalized, then never read. - **objectstack-ai#12961 to `901355c3b`**: "Ruled 2026-08-29 (option A)". `translatePage` descends into declared `properties.children`; on an id collision a region-level component wins outright, and among nested matches document order decides. Its diff wrote the `objectstack-ai#12961` lines being replaced. - **objectstack-ai#13218 to `c45d8e6b4`**: exports `walkAddressedPageComponents` and consumes it from both sides; its changeset reads "(objectstack-ai#13218, ruled 2026-08-30)". - **objectstack-ai#13109 to `8b236c826`**: its changeset says the extractor OMITTED keys the resolver reads, and "This matches the second half". The line now says the second half went live and this commit repaired it. - **objectstack-ai#8460 to ADR-0029 D9.2a**: ruling C's first rung. The amendment "D9.2a — AMENDMENT (2026-08-13)" records the option-A ruling: an extender's scalar applies only while the fold's base still carries the packaged owner's value. It also records that the mechanism is deliberately the same comparison-based one the catalog uses one layer up. The heading marker `[objectstack-ai#8460]` becomes `[ADR-0029 D9.2a]`. - **objectstack-ai#10926 to `d173125fb`**: "Option A per the maintainer ruling on objectstack-ai#10926 (2026-08-22): drop the key", the `submitLabel` retirement all three lines describe. - **objectstack-ai#12493 to `aa5994e17`**: adds `record_write_denied` and `approval_recall_not_submitter` ahead of their emitters, with no placeholders. Its diff wrote the four docblock lines. The catalog's rendered strings are untouched, per hypothesis 5. - **objectstack-ai#6604 to `d127ff002`**: "Per the maintainer's 2026-08-08 Option-B ruling the kernel side takes the domain-specific name", matching `KernelServiceMapSchema`. - **objectstack-ai#17081 to `24d622b94`**: lands objectstack-ai#17556 as "Suggestion 1 of objectstack-ai#17081". The line keeps objectstack-ai#17556 and names the parent card in words. - **objectstack-ai#11333 to `e58ea8b38`**: declares `grantedPermissions`, described by the commit itself as "the artifact-contract half of objectstack-ai#11333 option A / the objectstack-ai#13457 batch ruling". The line keeps objectstack-ai#14865 and objectstack-ai#13457 and states the ruled option in words. - **objectstack-ai#12245 to `c41b42e8d`**: rewrote this docblock from "the per-surface census (its os-dev-report comment, measured on origin/main @ e2debee)" and carries the 1218-values measurement. The report comment lived on the deleted card, so the commit is now the record, and the line says so. - **objectstack-ai#12144 to `3a04b0125`**: wrote the storage-owned length-ceiling note and its storage-column pin; its changeset heads "(objectstack-ai#12144)". - **objectstack-ai#12194 / objectstack-ai#12176 to `311433f6b`** (:140-141): stage 1, the item-name grammar declared and refused at the publish door; its message reads "Stage 1 of objectstack-ai#12176". **objectstack-ai#12176 to `7986d973f`** (:189): "Retire compound-name metadata addressing", stage 3 of the maintainer-ruled retirement. - **objectstack-ai#10485 to `35ad101bc`**: retires the `themes` carrier, `ThemeSchema` and the `PLURAL_TO_SINGULAR` fold ("Ruled B"). Its own diff wrote all four lines. - **objectstack-ai#11350 to `ece4dad31`**: "Invariant recorded (maintainer ruling 2026-08-23)". It also points the premise-delta note at objectstack-ai#11709, which is what `index.ts:148` now says. This is stage 1's wording for `kernel/index.ts:53`. - **objectstack-ai#14419 to `c5a7448d5`**: `create_record` surfaces the engine's `DUPLICATE_RECORD` code and the engine binds it on `$error`, the founding case the line names. Its message names objectstack-ai#14419 as the card it lands. - **objectstack-ai#13681 to `18d816a50`**: declares the run-level `FlowRunSummary.failed`, the spec half of the contained-failure contract. - **objectstack-ai#16659 to `ecdfc9411`**: declares the start-node `config.organization` key and "the one refusal sentence every enforcement point says". Its sub-commits pin "the three objectstack-ai#16659 consequences", so it is also the commit that closed the defect the second line describes. - **objectstack-ai#8714 to `42b05af89`**: "ONE closed contributor-state enumeration", maintainer-ruled 2026-08-18. - **objectstack-ai#6640 to `2ab1257c9`**: `preserveAudit` is UPDATE-only (stage 1's anchor for the same rule). - **objectstack-ai#6345 to `e2798fab7`**: its own `turso.zod.ts` diff wrote both lines ("The maintainer's objectstack-ai#6345 ruling closes it…", "(objectstack-ai#6345 fork 2)"). The wording is stage 3's in `config-registry.zod.ts`. - **objectstack-ai#13031 to `b799ac553`**: adds `mapViewPayloads` to `walk.ts`, the centralized walk the heading describes. Its message names objectstack-ai#13031 as the card it lands. - **objectstack-ai#8715, kept**: `2c86fe3ea` ("Maintainer ruling 2026-08-15 (disposition B: delete)"; its diff wrote :230) now sits on :231. ## Mechanical proof - **Token guard** (my `tokcmp.mjs`: TypeScript 6.0.3 leaf tokens, JSDoc kinds excluded, controls mutate the head text in memory only). Base `c876a7426d` against the head, 21 files, 37,539 base tokens: - Real run: 0 files with a token change (exit 0). - Comment-insertion control (`ai/index.ts`): 0 (exit 0). - Code-insertion positive control (`system/i18n-resolver.ts`): DIFFER at token 14452 (exit 1). - String positive control (a real `StringLiteral` in `system/operation-message.ts`, found by the parser): DIFFER at token 5 (exit 1). - The first string-control attempt matched a quoted fragment inside a comment and did not fire. It was a vacuous control, not a measurement, and the parser-located control above replaced it. - **Line balance**: every file is +N/−N (66/66 across 21 files); every line count is equal at base and head. - **Tracker numbers**: added-not-removed is empty in every file, and no `PR #N` is on an added line. Net-removed: 60 sites, 24 numbers. - **Shas**: 24 distinct on added lines, 0 on removed lines. - `rev-parse --disambiguate` answers 1 object for each. - `merge-base --is-ancestor` exits 0 against `origin/main` `e666636fd9` and against the base. - Each is single-parent; the repository is not shallow; the control leg `e9584681a4` exits 0. - Each commit's own message (17 of 24) or diff (all 24) names the number it replaces. - **Literal readers**: every string literal in the repository that carries one of the 26 numbers was matched against the 22 files' text. Three hits read these files: - `data/api-derivation.test.ts:236` splits on `[objectstack-ai#6259]`; - `packages/runtime/src/api-exposure.test.ts:152` splits on `objectstack-ai#6259`; - `identity/api-key-retirement.test.ts:118` asserts `are NOT declared here (objectstack-ai#8715`. - Those lines are the two kept sites. No test or script matches any rewritten line by pattern. ## Tests and gates (at head `9d63cb6548`) - `pnpm exec turbo run build --concurrency=2 --filter=./packages/* --filter=./packages/*/*` under `os-verify-lock`: Tasks 71 successful, 71 total, VERDICT command-exit 0. - `pnpm --filter @objectstack/spec check:generated`: exit 1, `check:docs` stale (1 of 15). `check:generated --fix` then regenerated exactly that artifact: 2 pages, 3 lines, each its docblock line verbatim. - `content/docs/references/automation/schedule-organization.mdx` - `content/docs/references/data/driver-turso.mdx` - The re-check in the gate run below is exit 0. - `vitest run --maxWorkers=2` over the touched areas (`src/api/rest-server.test.ts`, `src/api/rest-api-config-dead-keys-retirement.test.ts`, `src/system`, `src/shared`, `src/automation`, `src/ai`, `src/identity`, `src/security`, `src/data/driver`, `src/conversions`, `src/meta-spelling`): Test Files 159 passed (159), Tests 5163 passed (5163). - The 23 spec suites outside those areas that read a touched file's source text or name it: Test Files 23 passed (23), Tests 540 passed (540). - `scripts/{tombstoned-row-status,strictness-ledger,file-description,skill-map-guards,root-index,export-origins,category-title,split-entries,dist-freshness,dist-freshness-adoption,root-entry-type-nameability.pin}` tests; - `src/type-alias-convention.pin`, `src/contracts/{automation-result-status.pin,automation-service,scoped-context}`, `src/api/{export-job-family-retirement,api-entry-graph.pin}`, `src/eager-entry-import`, `src/integration/connector-author-shape`, `src/ui/{interaction-config-retirement,notification,strictness-batch14}`, `src/migrations/migrations`. - `scripts/build-schemas-check-mode.test.ts` is left to CI: it imports rather than reads, and rebuilds schemas in a temp tree. - `pnpm --filter @objectstack/spec typecheck`: exit 0; `check:test-typecheck` OK (53 files / 251 errors / 138 pinned signatures held). - Lint, a proven narrowing: `eslint --no-inline-config --format json` over the 21 touched `.ts` files gives 21 files, 0 errors, 0 warnings. - `isPathIgnored` is false for all 21, read through eslint's API. - `eslint.config.mjs:327-328` says type-aware linting is never enabled, so a comment edit cannot move an untouched file's verdict. - The repo-wide `pnpm lint` is CI's. - `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands`: 108 families derived and run, every one exit 0. `--ran` reads "108 derived, 108 run, 0 NOT-MEASURED, 0 UNRUN". Among them: - `pnpm check:issue-citations` plus the live diff-scoped `node scripts/check-issue-citations.mjs` judged 7 citations across 21 files, 7 resolve: the live numbers kept beside the anchors (objectstack-ai#9249, objectstack-ai#14954, objectstack-ai#17556, objectstack-ai#14865, objectstack-ai#13457, and objectstack-ai#11709 twice). - `pnpm check:doc-authoring`: 16,765 customer-facing strings across 1,175 spec sources clean; the sibling baseline holds. - Changeset: `patch` for `@objectstack/spec`. 13 of the 21 touched sources are `src/**/*.zod.ts`, which `files[]` ships verbatim, and the rewritten docblocks reach `dist`. For example, `ruled collision arbitration (commit 901355c)` is in 1 `.d.ts`, and the unchanged neighbouring sentence in the same exported docblock (the positive control) is in 1 `.d.ts`. - Merge probe: a no-driver `merge-tree` of the head onto `origin/main` `7a1faf1a5d`, from a bare shared clone, exits 0. The 3 commits `main` gained since the base touch none of this diff's files. No merge was made, as stages 1–4 did. - No ablation or reverse verification: the change is comment-only, so there is no behaviour to invert. ## Hypotheses (measured first) 1. **Holds.** The population is exactly the claim's 22 files: 62 dead sites at the tip, equal per file to the census at `f11b5f20a2`. 2. **Holds, with nothing to respell.** No sibling-qualified pair occurs among the 62 sites; no `pre-#N` spelling is dead here. 3. **Holds.** Re-read at 2026-09-29T06:23Z, after the last push and before this PR was opened: all 14 open PRs' full file lists (1,116 files in the version PR alone), and the newest `Claim:` on all 15 `pm:dispatched` cards. None names any of this PR's 24 paths. The five exclusions stay excluded. 4. **Holds.** The two projected pages were regenerated by the generator, never by hand. No other page under `content/docs/references/` carries any of the 26 numbers. 5. **Holds.** No `#N` in the 22 files is inside a string; the two literal-read sites stay as tokens. In `system/i18n-resolver.ts` and `system/operation-message.ts` only docblock and line-comment lines moved. ## Deviations - The file surface is 21 of the 22 named files. `data/api-derivation.ts` is untouched, because its one dead site is read by literal and its commit already stands on the next line (stage 3's disposition). - Six changed lines held no dead number. Each is the other half of a rewritten sentence: `rest-server.zod.ts:756`, `identifiers.zod.ts:19`, `index.ts:133`, `environment-artifact.zod.ts:137`, `schedule-organization.zod.ts:82`, and `identity.zod.ts:231` (the commit placed beside the kept :230). - Commit trailers follow AGENTS.md's model-free pair (`Claude-Session` plus `Co-authored-by: Claude`); the pre-push trailer check passed on every push. ## Acceptance notes **What stays for later stages.** At the tip `7a1faf1a5d` with this PR applied, `packages/spec/src` holds **260** dead sites (34 numbers). This is the gate's census on this head, with the four spec sources `main` changed since the base re-extracted and re-probed at the tip. By area: - `migrations/` **233**: objectstack-ai#20233 edits the same entry files; the author-shown fields are its form D. - `conversions/registry.ts` **12**: held by PRs objectstack-ai#20570 and objectstack-ai#20458. - `stack.zod.ts` **9**: free now; PR objectstack-ai#20579 landed as `7a1faf1a5d` at 06:02Z, after the claim, so it stayed excluded here. - `data/analytics.zod.ts` **3**: PR objectstack-ai#20458. - `integration/connector.zod.ts` **1**: objectstack-ai#20287, PR objectstack-ai#20587. - `data/api-derivation.ts:163` (objectstack-ai#6259) and `identity/identity.zod.ts:230` (objectstack-ai#8715), **1** each: kept because `api-derivation.test.ts:236`, `packages/runtime/src/api-exposure.test.ts:152` and `api-key-retirement.test.ts:118` read them by literal. Removing them is a test-string change, form D, outside this card's comment-only scope. **Carried from earlier stages, outside the gate's census** (which blanks strings and defers test files): the dead-number test-title strings, the two `why` strings, the `PROVENANCE_WAIVERS` reason, the two `AGGREGATION_CASES` notes, and the `liveness/**` notes. **Outside `packages/spec/src`** (objectstack-ai#20556's lane): `packages/spec/scripts/check-entry-nameability.ts` cites objectstack-ai#11350 in its header (:14) and PRINTS "recorded on objectstack-ai#11350" in its failure text (:727); `packages/spec/scripts/root-entry-type-nameability.pin.test.ts` cites it too. Commit `ece4dad31` is the anchor, already verified here. **Rung.** Five of the anchored retirements also have ADR-0087 D3/D2 entries: `identity-api-key-schema-retired`, `metadata-item-name-grammar-enforced`, `rest-server-config-dead-keys-retired`, `stack-themes-carrier-retired` and `translation-component-submit-label-retired`. This PR takes the commit rung, as stages 1–5 did. The D3 id is the more durable in-repo record if the ruling's first rung is later read to include those entries. **Wording, each true of its commit.** - `dev-login.zod.ts:10` names objectstack-ai#17081 in words ("suggestion 1 of its parent card"). - `environment-artifact.zod.ts:136-137` states objectstack-ai#11333's option A as "the option the objectstack-ai#13457 batch ruling chose". - `identifiers.zod.ts:18` drops "its `os-dev-report` comment is the measurement of record", since that comment went with the card, and names the commit as the record. **Observation, not filed** (a pre-existing live citation, not a tracker number; carrier: none): `environment-artifact.zod.ts:137` and `packages/runtime/src/security/artifact-granted-permissions.ts:6` cite "ADR-0025 §3.5 step 2" for the granted set. At the tip, §3.5's numbered step 2 is "Compatibility" and "Permission consent" is step 3. --- _Generated by [Claude Code](https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
…data/analytics.zod.ts to the commits that decided them (stage 7) (objectstack-ai#20616) Part of objectstack-ai#20234 Clause-②: no Stage 7 of the staged sweep: `packages/spec/src/stack.zod.ts` and `packages/spec/src/data/analytics.zod.ts`, both freed by landings (PR objectstack-ai#20579 and PR objectstack-ai#20458). Its claim is `5885635758`. Every comment or docblock line in those two files that cited a tracker number answering 404 now cites the commit on `main` that decided its rule, in ruling C+D's form C, and says in its own words what was decided. Comments only: 12 lines out, 12 in, across 2 files. No code token, string literal or `describe()` text moves. No dead site stays: none of the 12 is read by literal. The census is the gate's own `node scripts/check-issue-citations.mjs --census --json`, filtered to the two paths. Before: base `0f6dcac5e9`, board enumerated (185 pages, frontier objectstack-ai#20611). After: head `cc0580d404`, board enumerated (185 pages, frontier objectstack-ai#20615). ## Measurement | file (under `packages/spec/src/`) | dead before | after | numbers, then anchor | |---|---:|---:|---| | `stack.zod.ts` | 9 | 0 | objectstack-ai#10485 ×2 (`:415`, `:1023`) to `35ad101bc`; objectstack-ai#6238 (`:633`) to `c8d6f6e08`; objectstack-ai#14192 (`:1233`) to `4d0d9445a`; objectstack-ai#14686 ×2 (`:3037`, `:3194`) to `279431e7a`; objectstack-ai#14662 ×3 (`:4510`, `:5070`, `:5293`) to `35dffeace` | | `data/analytics.zod.ts` | 3 | 0 | objectstack-ai#10194 ×3 (`:404`, `:407`, `:485`) to `2306a765c` | | **2 files** | **12** | **0** | 6 numbers removed, 6 distinct shas | Per-file counts at base equal the claim's (9 and 3, from stage 6's census). A second instrument agrees site for site: every `#N` in the two files, classified by the TypeScript parser, and each of the 84 distinct numbers of 100 or more probed by REST `issues/N` without following redirects (the other 3 are the ordinals `Prime Directive objectstack-ai#12`, `batch objectstack-ai#23`, `batch objectstack-ai#57`). - Base: 244 sites, all in comments (0 strings, 0 code). 78 numbers answer 200 and 6 answer 404: the same 6 numbers and the same 12 sites as the gate. - Its string-class positive control found 11 string sites in `kernel/manifest-unknown-keys.test.ts` and `packages/cli/src/utils/lower-callables.test.ts`. - Head: 232 sites, 78 numbers, all 78 answer 200 (the same 78), none answers 404. - Lit controls objectstack-ai#16862, objectstack-ai#16847 and objectstack-ai#17698 answered 200 at every checkpoint (3 at base, 3 at head); dead controls objectstack-ai#16714, objectstack-ai#16715 and objectstack-ai#16697 answered 404 at every checkpoint. ## Why each anchor decides its line Each sha resolves uniquely, is an ancestor of `origin/main` (and of the base), and has one parent. No file under `docs/adr/**`, `docs/NORTH-STAR.md` or `scripts/adr-anchors/` names any of the six numbers or records these rules, so each takes the commit rung, as stages 1–6 did. - **objectstack-ai#10485 to `35ad101bc`** (`:415`, `:1023`): retires the `themes` carrier key and `ThemeSchema` under ADR-0049. Its message records the ruling, "Ruled B (退役授权面, 2026-08-21)", and its own `stack.zod.ts` diff wrote both lines. `:415` keeps ADR-0049 and the ruling in its words; the D3 entry `stack-themes-carrier-retired` it names on `:423` is unchanged. This is the anchor stages 1, 5 and 6 used for the same retirement. - **objectstack-ai#6238 to `c8d6f6e08`** (`:633`): widens the array member of `functions` so its `handler` also takes the lowered string ref, which is the fix for `objectstack build` refusing its own array output. Its message names objectstack-ai#6238, and its own diff wrote the line. objectstack-ai#4343 and objectstack-ai#4976 on the same line stay (both 200). - **objectstack-ai#14192 to `4d0d9445a`** (`:1233`): turns `ManifestSchema` and its nested blocks into `strictObject` and flips the assembled-body strip pin to a refusal pin; each of its sub-commits names objectstack-ai#14192. The line itself was written later by `c78c9180de`, whose own message says "objectstack-ai#14192 closed ManifestSchema with strictObject", so the commit that closed it is the anchor. - **objectstack-ai#14686 to `279431e7a`** (`:3037`, `:3194`): "defineStack refuses two actions that resolve to one scope-qualified runtime key". Its subject names objectstack-ai#14686, and its diff adds `collectDuplicateActionKeyErrors` and the changeset for that refusal. Both lines were written later by `773a99960a` (PR objectstack-ai#15022), whose message describes the same "same-key rule, which runs before the merge". - **objectstack-ai#14662 to `35dffeace`** (`:4510`, `:5070`, `:5293`): "composeStacks refuses two stacks whose actions resolve to one scope-qualified runtime key". It checks the composed set with the rule `defineStack` applies within one stack, with no `actionConflict` option (maintainer ruling 2026-09-03). Its message does not name objectstack-ai#14662; its own `stack.zod.ts` diff wrote all three `(objectstack-ai#14662)` lines. - **objectstack-ai#10194 to `2306a765c`** (`analytics.zod.ts:404`, `:407`, `:485`): binds `analytics_cube` (and `theme`) in `UNREGISTERED_KIND_SCHEMAS`, so `PUT /meta/analytics_cube/:name` parses through `CubeSchema`, and gives `CubeSchema` the `...MetadataProtectionFields` spread. Its message names objectstack-ai#10194, and its own diff wrote all three lines. The `[objectstack-ai#10194]` markers become `[commit 2306a76]`, the spelling stages 1 and 5 already use in `kernel/metadata-type-schemas.ts`. ## Mechanical proof - **Token guard** (my `tokcmp.mjs`: TypeScript 6.0.3 leaf tokens, JSDoc kinds excluded, controls mutate the head text in memory only). Base `0f6dcac5e9` against the head, 2 files, 17,249 base tokens: - Real run: 0 files with a token change (exit 0). - Comment-insertion control (`data/analytics.zod.ts`): 0 (exit 0). - Code-insertion positive control (`stack.zod.ts`, a declaration appended): DIFFER at token 15388 (exit 1). - String positive control (the first `StringLiteral` the parser locates in each file): DIFFER at token 5 (exit 1), once per file. - `describe()` positive control (the first `.describe()` string argument the parser locates: `stack.zod.ts:133`, `analytics.zod.ts:244`): DIFFER at tokens 507 and 442 (exit 1). - **Line balance**: `stack.zod.ts` +9/−9, `data/analytics.zod.ts` +3/−3; line counts equal at base and head (5344 and 853). - **Tracker numbers**: added-not-removed is empty in both files, and no `PR #N` is on an added line. Net-removed: 12 sites, 6 numbers. The only numbers on added lines are objectstack-ai#4343 and objectstack-ai#4976, which stay on `:633`. - **Shas**: 6 distinct on added lines, 0 on removed lines. - `rev-parse --disambiguate` answers 1 object for each. - `merge-base --is-ancestor` exits 0 for each, against `origin/main` `7510663c87` and against the base; each is single-parent; the repository is not shallow. - **Literal readers**: all 26 string, template and regex literals in the repository that carry one of the six numbers (42 code files) were matched against the two files' base text: 0 occur there. Each removed line was also cut into 4-word windows (96) and searched across the tree: the 9 hits inside string literals are other files' own test titles sharing a phrase ("the ADR-0010 protection envelope", "an assembled body is"), and none reads either file. The source-text readers of the two files read code, not these comments: `compose-stacks-refusal-envelopes.test.ts` counts `throw new Error(`, and `check-stack-collection-maps.mjs` and `check-skill-top-level-keys.mjs` read the declared collections and keys. ## Tests and gates (at head `cc0580d404`) - `pnpm exec turbo run build --concurrency=2 --filter=./packages/* --filter=./packages/*/*` under `os-verify-lock`: Tasks 71 successful, 71 total, VERDICT command-exit 0. - `pnpm --filter @objectstack/spec check:generated` under the lock: all 15 generated artifacts up to date, `check:docs` over `content/docs/references/**` included; VERDICT command-exit 0. No reference page projects any of the 12 lines, so none is regenerated. - `vitest run --maxWorkers=2` under the lock over the two files' own suites (`src/stack*`, `src/compose-stacks*`, `src/define-stack*`, `src/assembled-package-body`, `src/data/analytics*`, `src/data/cube*`): Test Files 35 passed (35), Tests 976 passed (976). - The 37 spec suites that read source text across `src/`, or carry one of these numbers, under the lock: Test Files 37 passed (37), Tests 759 passed (759). - `scripts/{category-title,dist-freshness,dist-freshness-adoption,file-description,strictness-ledger,strictness-ledger-doc,root-index,skill-map-guards,export-origins,split-entries,root-entry-type-nameability.pin}`, `scripts/liveness/{evidence,tombstoned-row-status}`; - `src/type-alias-convention.pin`, `src/eager-entry-import`, `src/api/{api-entry-graph.pin,auth,export-job-family-retirement}`, `src/ai/tool-confirmation-prescription-tense.pin`, `src/data/{currency-mode-family-closure.pin,external-lookup-retirement}`, `src/identity/position-delegatable-enforcer.pin`, `src/integration/{connector-connection-timeout-retirement,connector-resilience-keys-retirement}`, `src/security/rls-tags-retirement`, `src/shared/{alias-integrity,retired-key-migrate-sentence}`, `src/system/{compliance-families-retirement,constants/platform-object-names,email-template-floor-locale-parity.pin,message-queue-retirement}`, `src/ui/{action-requires-confirmation-docblock.pin,i18n,interaction-config-retirement,strictness-batch14}`, `src/kernel/{manifest-unknown-keys,metadata-type-schemas}`. - Left to CI: `scripts/{build-schemas-check-mode,def-key-collisions,openapi-self-consistency}` (each rebuilds artifacts in a temp tree) and `scripts/{check-generated-ledger,check-generated-fix-rebuild.pin}` (read the ledger and `dist`). None reads comment text. - `pnpm --filter @objectstack/spec typecheck` under the lock: exit 0; `check:test-typecheck` OK (53 files / 251 errors / 138 pinned signatures held). - Lint, a proven narrowing: `eslint --no-inline-config --format json` over the 2 files gives 2 files, 0 errors, 0 warnings. - `isPathIgnored` is false for both, read through eslint's API. - `eslint.config.mjs:327-328` says type-aware linting is never enabled, so a comment edit cannot move an untouched file's verdict. - The repo-wide `pnpm lint` is CI's. - `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands`: 79 families derived and run, every one exit 0. `--ran` reads "79 derived, 79 run, 0 NOT-MEASURED, 0 UNRUN". Among them: - `pnpm check:issue-citations` (self-test, 114 cases in 8 batteries) and the live diff-scoped `node scripts/check-issue-citations.mjs`: it judged the 2 citations on added lines, objectstack-ai#4343 and objectstack-ai#4976, and both are live issues. - `pnpm check:doc-authoring`: 16,804 customer-facing strings across 1,179 spec sources clean; the sibling baseline holds. - `pnpm check:stack-collection-maps`: 8 enumerations reconciled against 31 declared collections. - Changeset: `patch` for `@objectstack/spec`. Both files are `src/**/*.zod.ts`, which `files[]` ships verbatim, and the rewritten docblocks reach `dist`: "posture: commit 4d0d944 closed" and "[commit 2306a76] This docblock used to say" are each in 2 `.d.ts`, their old spellings in 0. Positive control: the unchanged neighbouring sentence "BY INHERITANCE — an undeclared key on one is REFUSED" is in the same 2 `.d.ts`. - Merge probe: a no-driver `merge-tree` of the head onto `origin/main` `7510663c87`, from a bare shared clone, exits 0. The 3 commits `main` gained since the base touch neither file nor the citation or derivation scripts, and a re-derivation prints the same 79 commands. No merge was made. - No ablation or reverse verification: the change is comment-only, so there is no behaviour to invert. ## Hypotheses (measured first) 1. **Holds.** 12 dead sites at the tip, 9 in `stack.zod.ts` and 3 in `data/analytics.zod.ts`, equal per file to stage 6's census. 2. **Holds.** Read at 2026-09-29T07:36Z and again at 08:16Z, after the last push and before this PR was opened: all open PRs' full file lists (9 PRs, 166 files at the second read) and the newest `Claim:` on all 11 `pm:dispatched` cards. None names either file, except this card's own claim. 3. **Holds, with nothing to keep.** All 12 sites are comments. No test string, exported string or `describe()` text carries one, and no test or script reads any of them by literal. 4. **Holds.** No generated reference page projects these lines; `check:docs` is green with no regeneration. ## Deviations - None to the file surface: the 12 claimed lines and one changeset, no generated page needed. - Commit trailers follow AGENTS.md's model-free pair (`Claude-Session` plus `Co-authored-by: Claude`); the pre-push trailer check passed on every push. ## Acceptance notes **What stays for later stages.** The gate's census at this PR's head (base `0f6dcac5e9` plus this PR) reads **248** dead sites (29 numbers) in `packages/spec/src`. The only `packages/spec/src` change `main` has made since the base (objectstack-ai#20610's migrations entry and registry) adds four live numbers and removes none, so 248 also stands at the tip `7510663c87` plus this PR: - `migrations/` **233**: objectstack-ai#20233 edits the same entry files (PR objectstack-ai#20607 holds `migrations/registry.ts`). - `conversions/registry.ts` **12**: PRs objectstack-ai#20570 and objectstack-ai#20587 hold it. - `integration/connector.zod.ts` **1**: PR objectstack-ai#20587 (objectstack-ai#20287). - `data/api-derivation.ts:163` (objectstack-ai#6259) and `identity/identity.zod.ts:230` (objectstack-ai#8715), **1** each: kept because tests read them by literal, so removing them is form D. **Outside the gate's census: test files.** The gate defers `*.test.ts`. The same six dead numbers still stand at 15 comment sites and 10 test-title strings in `packages/spec/src` test files: - `data/analytics-strictness-batchd.test.ts:96` (comment, objectstack-ai#10194) and its title `:93`. This file is in the `analytics*` set stages 3 and 4 excluded while PR objectstack-ai#20458 held it; `analytics-date-range-two-bound-window.test.ts` and `cube-member-inner-name-retirement.test.ts` were in that set too and are not re-measured here. - The package root: `compose-stacks-action-echo.test.ts:20`, `:34`, `:200` (objectstack-ai#14686) and titles `:176`, `:224`; `compose-stacks-action-key-collision.test.ts:3` (objectstack-ai#14662); `stack-top-level-strict.test.ts:103` (objectstack-ai#10485) and title `:128`; `type-alias-convention.pin.test.ts:257`, `:1572`, `:1937` (objectstack-ai#10485). - `shared/`: `metadata-collection.test.ts:250`, `metadata-url-spelling.test.ts:51`, `:72`, `:168` (objectstack-ai#10485), `:257` (objectstack-ai#10194), title `:254`. `automation/sync-retirement.test.ts:207` (objectstack-ai#10485). - `kernel/`: `manifest-unknown-keys.test.ts`, four titles (objectstack-ai#14192); `metadata-type-schemas.test.ts:422`, a title (objectstack-ai#10194). - Stage 6 took the package root, `shared/` and `automation/` through the gate's census, which never lists a test file, so test-file comment lines there may carry other dead numbers as well. That wider population is not measured here. **Outside `packages/spec/src`.** The same six numbers stand at 44 more sites (`packages/{metadata-protocol,objectql,rest,runtime,cli,core,metadata,qa}`, `examples/`, `scripts/`, `packages/spec/scripts/`), and at 19 sites in `migrations/` (the objectstack-ai#20233 area). **Rung.** The objectstack-ai#10485 retirement also has the ADR-0087 D3 entry `stack-themes-carrier-retired`, which `:423` already names. This PR takes the commit rung, as stages 1–6 did. **Wording, each true of its commit.** `:3037` and `:3194` now read "commit 279431e's same-key refusal": the refusal that commit added, in lines `773a99960a` wrote. `:1233` reads "commit 4d0d944 closed `ManifestSchema`", in a line `c78c9180de` wrote. --- _Generated by [Claude Code](https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
…ai#20623) ## What this is The release-time half of the 17.5.0 release notes. The page `content/docs/releases/v17/17-5.mdx` landed before the cut (objectstack-ai#20396) with a `RELEASE-TIME TODO` comment listing four edits to make once 17.5.0 was on npm. 17.5.0 was published on 2026-09-29 (`@objectstack/cli@17.5.0` at 07:58Z, the last package, `@objectstack/spec`, at 08:09Z). This PR makes those edits, deletes both TODO comments, and updates `content/docs/releases/v17/index.mdx`. Docs-only: two files under `content/docs/releases/`, which is release-owned, so this is the dedicated docs-only PR `AGENTS.md` sanctions for that tree. It publishes nothing from any package, hence `skip-changeset`. ## What changed **`17-5.mdx`** - **Publish date.** "What's new" now opens: 17.5.0 was published to the `latest` tag on 2026-09-29, 20 days after 17.4.0. - **Count.** The draft said it was compiled from "868 changesets pending on `main` at `ab6fb027`". The version commit `8c87d26a` (objectstack-ai#17076) actually consumed **958** changesets (the `.changeset/*.md` files it deletes, README excluded). The page now states 958 as its measure and cross-checks it against the CHANGELOGs: the 69 package `CHANGELOG.md` files that carry a 17.5.0 section at `8c87d26a` list **1,372 per-package entries** (703 minor, 669 patch, 0 major) in 56 of those files, and those entries de-duplicate to exactly the same 958. - **The 90 changesets the draft never read**, the ones consumed by `8c87d26a` but not pending at `ab6fb027`, were each read in full and folded in: - **Breaking changes & migration: 45.** Two new subsections: *Written values are held to the field's declared type* (date and datetime ISO spellings on a real day, the year range 0001–9999, the numeric string grammar, `precision`, `progress` bounds, `/import` thousands commas, with a Migration table) and *An edge-branched decision takes its first matching branch* (objectstack-ai#20344, with the stored-row caveat). The rest joined existing subsections: RLS cross-class comparisons; org-less grants; cube `public`; number comparands, `having` placeholders and double accumulation; flow node config, `connector_action`, `api` flow secrets and the connector resilience keys; list-view `tabs`, action `aria` and view round-trip keys; `/diff` `/history` `/audit` as authoring doors and OpenAPI `info`; remote Turso and unbuildable indexes; the one stack authoring shape and new lint positions; QA `requires`, narrowed published types and `retiredAfter`. - **New capabilities: 11.** Studio form rows for 27 structured keys, the staged `$empty` operator, the new `ComponentPropsMap` rows, and email verification under `open`. - **Notable fixes: 15.** Dispatcher-only hosts, `/diff` default range, plain-text email faces, auth-settings sibling isolation, SQLite `reclaimSpace()`, zh-CN/ja-JP/es-ES object labels, aggregate `search`, and the OSV sweep. - **New in Console: 2.** The fourth objectui pin move and the `trash-2` → `trash` icon. - **Judged too minor to surface: 17.** Each is text only, with no behaviour change an app or operator can reach: describe, docblock and comment rewrites, `os migrate meta` guidance text, liveness-ledger data and layout, a form row's declared language, a test-only import change in `plugin-dev`, and the successor `Link` header of the deprecated `?layers=true` flag on the environment-scoped mount. - **Highlights** gain three bullets drawn from the above (decision first-match, written values, the stack authoring shape). The "running deployment" warning list gains five lines. - Every breaking entry that needs an operator action has an upgrade-checklist line, marked *Not exercised* unless the HotCRM upgrade below exercised it. - **Console.** Four pin moves now, not three: `f8a9d0fb0596 → dd3f7e1be356` (`3cf6449`, objectstack-ai#20436) carries 325 releasing objectui changesets, 41 of them declared breaking upstream. The Highlights, "What's new" and Console sections all say four. - **Dependencies.** `nodemailer` is `^10.0.2`, not `^9.1.1`. That is a major bump for GHSA-6vj9-mwq6-2f5v, which has no 9.x fix. The line also carries the operator-visible note from objectstack-ai#20564's changeset: from nodemailer 10.0.12, `requireTLS` wins over `ignoreTLS`, so a `transportOptions: { ignoreTLS: true }` override on a port other than 465 now upgrades to STARTTLS or fails the send, and `secure: false` is the way to connect in the clear. - **New subsection "Also shipped in 17.5.0 — not in its CHANGELOG".** The publish ran from `main` at `0f6dcac5` (Release run 36536081716), 8 first-parent commits after the version commit, so the npm packages also contain `6e3aa75e a093ce3 92fe081 3a89d45 7001918 c96beb2 ba4648d 0f6dcac`. Their changesets are still unconsumed in `.changeset/`. The subsection gives one line per commit and says they will be listed again in 17.6.0's CHANGELOG and that the cause is tracked in objectstack-ai#20613. The breaking `92fe0814` (objectstack-ai#20458, cube member inner `name` retired) gets a Migration note taken from its own changeset and a checklist entry, and the checklist preface says where that note lives. **`v17/index.mdx`** (following the 17.4.0 curation precedent `b11bfb9a`) - frontmatter description: "17.0.0 through 17.5.0"; - status blockquote: 17.5.0 is released and current, published 2026-09-29, taking over from 17.4.0; a plain install resolves 17.5.0; the minors warning names 17.5.0; - a "17.5.0 stays in that register" paragraph drawn from the page's Highlights, linking `#breaking-changes--migration-in-1750` and `#upgrade-checklist`; - the per-release list marks 17.5.0 current and 17.4.0 no longer current; - the checklist callout records that 17.4.0 → 17.5.0 has been exercised only in part (seven lines, on HotCRM), and the per-release checklist links lead with 17.5.0. ## Findings from a HotCRM 17.4.0 → 17.5.0 upgrade These were folded in at the coordinator's request; the parent session verified them. - **Decision-mode flip** (objectstack-ai#20344): now a 17.4.0 → 17.5.0 table, a standing warning that flows stored in `sys_metadata` take the new meaning without being rewritten, and a checklist line. The line says to review each `mode: 'inclusive'` that `os migrate meta --from 17` offers, deleting it where the conditions partition, because applied blindly it draws `flow-decision-inclusive-overlap`. It then says to review the `--stored` list. - **`specVersion` / `engines.protocol`**: the checklist now says what an app does after a 17.x minor, from the code. `PROTOCOL_VERSION` is still `17.0.0`, and the handshake compares only the major, so `engines.protocol: '^17'` stays, a `^17.0.0` `specVersion` admits 17.5.0, and a `^18` range is refused `OS_PROTOCOL_INCOMPATIBLE`. "Protocol 18" is the migration registry's next major; the 17.5.0 schemas already refuse its shapes, which is why `os migrate meta --from 17` runs to 18. The Breaking-changes intro carries the same sentence. - **Seven checklist lines** are marked *Exercised on HotCRM (a 17.4.0 app with a 17.4.0-created SQLite DB), 2026-09-29* with the observed result: `os doctor` scheduled-work reading, the `account-issuer` pre-flight, `os migrate meta --from 17` (41 refusals in 874 lines, 240 of them generic protocol-18 notices, so filter the output), the decision review with `--stored` (0 rows), `page.assignedProfiles`, lookup screen field `reference`, and `chartConfig` (34 sites). Every other line stays *Not exercised*, and the preface and the v17 index callout say the hop was exercised only in part. ## Citations Every added `#N` was resolved on the board: 144 candidate numbers from the 90 commits and the 8 post-version commits, all resolving, and objectstack-ai#20613 is open. SHAs are 7-character short SHAs, and each was verified to resolve unambiguously. ## Gates run (workspace installed) The full sweep ran on `2b3b323b`. The head `664854a4` changes one phrase in one checklist line, and on it the MDX parse, `check:doc-anchors`, `check:role-word`, `check:issue-citations --base origin/main`, the audit-scope gate and the release-page gates were re-run, all green. Named in the task, all exit 0: - `pnpm check:doc-anchors`: 391 internal fragment links, all resolve. - `node scripts/check-issue-citations.mjs --base origin/main`: 119 citations judged (104 resolve as pull requests, 1 as an issue, 14 cross-repo `objectui#N` unjudged); every added citation resolves. - `pnpm check:role-word`: no new occurrences. - `node scripts/docs-audit/check-audit-scope.mjs`: in sync, and release-owned pages are review-only. - `check-release-page-status`, `check-release-section-coverage` (plain and `--strict`) and `check-release-notes`: all OK. - MDX parse: both pages compile with `@mdx-js/mdx` 3 + `remark-gfm`, and all 7 tables on `17-5.mdx` parse with no ragged rows. Derived with `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands`: 47 commands, **all 47 exit 0**. The first sweep hit 5 prerequisite refusals (exit 3, or `check:docs` on the missing gitignored `json-schema` tree) from unbuilt `@objectstack/spec`, `@objectstack/formula`, `@objectstack/lint` and `@objectstack/client-react`. None was a finding. Those packages were built and the whole list was re-run. Among the 47: `check:doc-authoring`, `check:docs-single-h1`, `check:docs-redirects`, `check:corpus-claim-drift`, `check:docs-transcript-drift`, `@objectstack/spec check:docs` / `check:skill-examples` / `check:liveness`, `@objectstack/lint check:doc-formula-expressions` / `check:doc-security-posture`, `check-doc-frontmatter`, `check-docs-section-name`, `check-section-landing-index` and `check:nul-bytes`. The diff was also re-read by hand; the fixes from that pass are the second commit (`da443bdb`). ## Not in this PR `content/docs/upgrading.mdx`'s per-release table still reads "v17.4.0 — ⛔ checklist not written; machine-draft notes only" and has no 17.5.0 row. It is a hand-written tree outside `content/docs/releases/`, so it is left for a separate change. --- _Generated by [Claude Code](https://claude.ai/code/session_014VGCS11YUtYAiinRcdqQwL)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
… notes (objectstack-ai#20667) Fixes objectstack-ai#20622 Clause-②: no One new `patch` changeset (`@objectstack/cli`, in the fixed release group) and nothing else. No code, no docs pages, no edit to any existing changeset. ## What it carries 1. An upgrade line: the raised dependency floors cover what objectstack loads; a lockfile-preserving upgrade can keep an older `hono` under `@modelcontextprotocol/sdk` (via `@objectstack/cli` to `@objectstack/mcp`), which objectstack never loads. `pnpm update hono` clears a scanner. It states no version number. 2. A section naming, by PR number and title only, the 16 changesets (15 PRs) that shipped inside 17.5.0 without being consumed. Breaking entries first: objectstack-ai#20458, objectstack-ai#20504, objectstack-ai#20567 (two changesets). ## Measurement The brief's range command (`git log --diff-filter=A --name-only 8c87d26..0f6dcac -- .changeset/`) yields only 8 files. The other 8 were added BEFORE the version commit and were already left unconsumed by it (the tree at `8c87d26a5d` still holds them). The set that shipped in 17.5.0 and is still pending is the changeset directory at `0f6dcac5e9` intersected with `origin/main`: 16 files, all still pending, matching the triage's 16 and its breaking set (3 PRs, 4 files). Breaking was decided by each file's text (`BREAKING` banner / narrowing arm). Code anchors for the upgrade line: `packages/mcp/package.json` depends on `@modelcontextprotocol/sdk ^1.30.0`; `packages/cli/package.json` depends on `@objectstack/mcp`; `packages/plugins/plugin-hono-server/package.json` carries `hono ^4.13.5`; `packages/mcp/src` imports only `server/mcp`, `server/stdio`, `server/webStandardStreamableHttp` and `types` from the SDK (no `server/streamableHttp`). ## Gates 19 derived by `dispatch-gates.mjs --commands`, all 19 run and exit 0 (adr-0087-registration, changeset-no-major, closing-keyword-parity, comment-mask-corpus, empty-changeset, gate self-tests, nul-bytes, published-files and the rest); `--ran` reconciliation: 19 derived, 19 run, 0 NOT-MEASURED, 0 UNRUN. `check-changeset-fixed` green. Ordering: must land before objectstack-ai#20639 (Version Packages, open at the time of writing). --- 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01VDtqoecgES7ScQYGbFVDRv --------- Co-authored-by: Claude <noreply@anthropic.com>
Fixes #20300
Clause-②: no (narrowing)
Retires the inner
nameon analytics cube measures and dimensions (MetricSchema.name,DimensionSchema.name).measuresanddimensionsare records, and the record key was always the member's identity:GET /api/v1/analytics/metapublishes every member asCUBE.KEY, and every consumer resolves a member by indexing the bag with its key. The inner copy was REQUIRED, read by nothing, and silently ignored when it disagreed with its key.ADR-0049 enforce-or-remove, by triage's verdict
5859547666(RETIRE) under the maintainer's criterion, verbatim: 「每族该问的是:主流平台有没有这个能力 —— 有 ⇒ 补消费端(一次做对);没有 ⇒ 退役,而不是看仓里有没有人读」. Cube.dev and LookML key a member by its declared name, with no second inner name that can disagree.Tier H. The diff touches
skills/objectstack-ui/rules/dashboards.md(a deletion only; see Deviations 1). It lands on the maintainer's word, then the seat lands it. 103 files, +1446 / -423 (1869 changed lines, under the 5,000 line class).Patch round after #20390 (head
f639af5f3)The sections below describe
c4771e604. This head adds two commits and nothing else:5ce26b0b2mergesorigin/main75b2169243throughos-regen-merge.sh. The one hand conflict,packages/spec/vitest.repo-tests.json, was resolved by stacking both entries. Main’s step-18 siblings and this PR’s entries are all present in the four registries.f639af5f3stampsretiredAfter: 17.4.0oncube-member-inner-name-removed. Forward conversion never opens on unreleased main: spec still labelled 17.4.0 while main refuses 17.5.0 retirements, so artifacts built by the published 17.4.0 CLI are refused #20390 (e956924e1) made the stamp required on everyretiredFromLoadPath: trueconversion. This entry is unpublished, so it takes the package label, asview-list-tabs-removedandaction-aria-removeddo onmain.The net diff is 103 files, +1447 / −423: the stamp is the one added line. CI is green on this head. At-tier record
5875291969: PASS.What this head carries (
c4771e604)retiredKey()tombstones onMetricSchema.nameandDimensionSchema.name(bothstrictObjects, theaction.ariaposture).tsctypes the keynever, and the parse raises the prescription atmeasures.KEY.name/dimensions.KEY.name. Themeasures/dimensionsdescribes now state that the record key IS the member's name.cube-member-inner-name-removed(protocol 18,retiredFromLoadPath), chained intostep18.conversionIdswith a rationale paragraph. It strips the innernamefrom every member of everyanalyticsCubes[]entry, and its notice names the cube.cube-member-inner-name-retired: the judgement a DISAGREEING value still owes its author (which spelling was meant).RETIRED_KEYS_BY_MAJOR[18]gainsdata/Metric:nameanddata/Dimension:name(per-file entries, generated region).analytics_cube.jsonrows STAYdead(the tombstone keeps the key in the walked shape) with aREMOVED 2026-09-28note and a re-measuredverifiedAt. The README row is updated; the counts do not move.dataset-compiler.tsstops writing it (the triage line), and so do the two untyped internal mints tsc cannot see (CubeRegistry.inferFromObject, andinferCubeFromQuery/inferMeasureinanalytics-service.ts).service-analyticsREADME example (3), and the publishedobjectstack-uiskill example (6)packages/spec/src/data/cube-member-inner-name-retirement.test.tscovers every door (schema,/metabinding,defineCube,defineStackwith itsSTACK_SCHEMA_INVALID/422 envelope, and a@ts-expect-errortsc leg), the D2 legs (stored row, boot door with a lit control, a disagreeing value, idempotence, load-path retirement), the registration, and a tree-scoped structural absence pin over the declared five-root radius. The flippedanalytics.test.tsblocks (the snake_case pins on a value nothing read) are now tombstone pins.@objectstack/specminor (BREAKING banner, FROM → TO, the one-line fix, what an author sees, and the ADR-0087registeredmarker);@objectstack/service-analyticspatchWhat an author who still writes it sees:
tscfails at the authoring site. The parse refuses it with: "measures.METRIC.namewas removed in @objectstack/spec 17.5.0 (ADR-0049 enforce-or-remove) — it never had an effect: the record key is the metric's name. … Delete the key. To rename a metric, rename its key inmeasures— and every query, dashboard and report that namesCUBE.KEY. Runos migrate meta --from 17to list the mechanical edits for existing sources; apply them by hand." A stored or built cube heals at rehydration and at the artifact door.Zone 2, measured
namein non-test source (analytics-service.ts#getMetaandmemory-analytics.ts#getMetapublishCUBE.KEY;native-sql-strategy.ts#lookupMemberandmemory-analytics.ts#resolveMeasure/#resolveDimensionindex the bag by key). Lit control: four reads ofmeasure.label/dimension.labelin the same two projections. The onemeasure.namehit (dataset-compiler.ts:383) is aDatasetMeasure, not a cube member. objectui at pinf8a9d0fb05: no cube-member authoring.CubeSchemais used only inclientValidation.ts(control: that hit resolves at the same sha).dataset-compiler.ts(2 sites),CubeRegistry.inferFromObject(3) and the ad-hoc mint inanalytics-service.ts(4, plusinferMeasure's 3 returns), the showcase, the README and the skill. Every one wrote the name EQUAL to its key, so no producer writes a disagreeing value. Test fixtures: 21 disagreed, all indriver-memory(e.g.totalAmount: { name: 'total_amount' }), and every one was queried by its key (orders.totalAmount). That is the trap, live in-repo. Noplatform-objectsor template authors any cube. Stored rows:analytics_cubewraps asanalyticsCubesatapplyConversionsToStoredItem, and the pin's stored-row leg replays it.publicenforced, feat(analytics): enforce analytics_cube.public and default it to visible #20348 landed). Line numbers are from the merged tree.origin/main6e3e5462c(which carries feat(spec)!: retire the list view's owntabskey; named presets arelistViewsentries #20357's step-18 appends) withbash scripts/pm/os-regen-merge.sh.merge.os-regen.driverregistered) answered exit 0 with no conflicted paths.content/docs/references/data/analytics.mdx, which was regenerated from the merged tree in its own commit (e19628132).view-list-tabs-removedandcube-member-inner-name-removed) and in the rationale.check:generatedreported all 15 artifacts current, measured right after a spec build of the merged tree.Deviations
skills/**split was ordered, then withdrawn. The seat ordered the skill hunk split into a companion PR, and withdrew that on this measurement:os:checkblock thatcheck:skill-examplestype-checks insidetypecheck-consumers, a member of the requiredTypeScript Type Checkaggregator.nameback into the example withablation-replace(restored to the HEAD blob,git diff HEADempty) gave exit 1:dashboards.md:450:15 error TS2322: Type 'string' is not assignable to type 'undefined'. So this PR without the hunk is red.mainwould be red too: at basedbddf02c1,MetricSchema.nameis a REQUIREDz.string(). That half is derived from the schema, not built.CubeRegistry.inferFromObjectand theanalytics-service.tsmints are untyped (aRecordofany) producers that tsc cannot see; A2 put every producer in scope. Fixture edits spanservice-analytics,driver-memory,spec,client,objectql,runtime,qa/dogfoodandqa/downstream-contract.spec-property-retirementskill's route table maps.strict()to deletion plus a guidance map. I took the triage'sretiredKey()route instead, whichshared/retired-key.tsdocuments for closed shapes (strictly stronger than a guidance entry) and whichaction.ariaused this week. As a result the ledger rows stay, per the card's acceptance. TheCubeJoinSchemadocblock line that said cube shapes never take a tombstone is corrected.from: name "total_amount",to: (removed; the record key "totalAmount" is the name)).service-analyticsis gradedpatch. Its members are filed under the same keys, and every/analytics/*answer is unchanged.@objectstack/speccarriesminor: a published narrowing shipsminorin the launch window, and the changeset declares it asClause-②: no (narrowing)under its BREAKING banner.Tests (head
c4771e604unless stated)@objectstack/spec:test564 files / 16641 tests green (merged treee19628132; specsrc/is unchanged since).test:repo37 / 675 green (pre-mergec1cae40df). Post-merge, its three tree-reading legs this diff owns were re-run green: the retirement pin,retired-key-migrate-sentence, andbuild-schemas-check-mode(107 tests together with the pin).typecheckgreen (src, scripts, and the test layer under its shrink-only ledger).tsc -p tsconfig.test.json --listFilesOnlylists the new pin, so its@ts-expect-errorlegs are live.@objectstack/service-analytics: typecheck green, 132 files / 3093 tests green (14cac89f4).@objectstack/driver-memory: typecheck (which reaches the test layer) green, 57 / 1374 green (14cac89f4). tsc found the two map-built members there; the same shape was then swept in service-analytics and runtime.client7/7,objectqlprotocol-meta95/95,runtimecross-field-refusal-operand-withhold11/11,downstream-contractcontract.test.ts13/13 plus typecheck exit 0.name: m.nameback indataset-compiler.tsviaablation-replacegavesrc/dataset-compiler.ts(673,7): error TS2322: Type 'string' is not assignable to type 'undefined'against the rebuilt spec.d.ts. Restored: blob equals HEAD,git diff HEADempty. The direction was the predicted one (red).dispatch-gates.mjs --commandsatc4771e604derives 126 families. All 126 were run and recorded, and--ranreports 0 UNRUN.check:dual-build-cjs-loadsandcheck:type-check-debt(both need the whole-repo build).check:platform-checklist, inherited frommain(see Acceptance notes). Its inputs are byte-identical tomain, and it is not a per-PR CI gate.eslint --no-inline-config --format jsonover exactly the 95 changed code files returned 0 errors and 0 warnings.eslint.config.mjs(files: ['**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}']); the file count comes from the JSON output.parserOptions.project), so no untouched file's verdict can move.NOT MEASURED (CI runs these):
qa/dogfood: the two touched dogfood tests andexpression-conformance. The package does not resolve@objectstack/verifyunbuilt, so no test ran. Static reading: 0analytics.zodreferences in that ledger, against 11.zod.tsreferences as the control.typecheck: 7 TS2307 for unbuilt connectors and plugins, and 0 diagnostics inshowcase.cube.ts.downstream-contract'sconsumer-specifier-ledgerneeds@objectstack/clibuilt.Skills readings
skills/objectstack-ui/rules/dashboards.mdgoes 468 → 468 lines. The whole package (everySKILL.md) goes 4404 → 4404. Against base the hunk is 6 lines modified and nothing added: each change deletes aname: 'KEY',fragment.Acceptance notes (noted, not filed)
check:platform-checklistis red onmainat3cf644938:areas/identity-auth.jsonanchorsplugin-auth/src/auth-plugin.ts#twoFactor, and7d6308895(fix(plugin-auth): a refused auth setting no longer drops the settings saved with it #20429) turned that line-start key into an inline nested object key (plugins: { twoFactor: true }), which the shared resolver reads as absent by design.main.identity-auth.jsonorauth-plugin.ts, or the checklist owner.namecarried a snake_case regex. The record key never had one, and it legitimately takes camelCase and dotted spellings in-repo (driver-memoryfixtures;'owner.amount_sum'indotted-measure-refusal.test.ts). Nothing is enforced on the key today; this is an observation, not a change here.Object.fromEntries(… { name: n, … })). tsc found the typed ones indriver-memory, and the rest of that shape was swept by an AST scan (object literals holdingname,sqlandtype). What remains is only this pin's own refusal specimens and the schema shape.维护者速读(草稿)
改了什么:分析立方体(cube)的度量与维度不再接受内部
name字段;成员的名字就是它在measures/dimensions里的键。写了name会在编译期和解析期被明确拒绝,并给出迁移提示。已存储的立方体在加载时自动去掉该字段,照常可用。为什么改:这个字段从来没有任何代码读取,系统一律按键识别成员;当
name与键不一致时,作者写的值被静默忽略。Cube.dev、LookML 等主流方案也只有一个名字。按您「主流平台有没有这个能力」的判据,判定退役。风险与代价(含回滚):对外行为不变 ——
/analytics/meta与查询接口的成员名仍是立方体.键。仍写name的作者源码需要删除该字段(os migrate meta --from 17列出改动)。回滚:还原本 PR 即可,无数据迁移需要撤销。本 PR 同时改了一个对外发布的 skill 示例(仅删除name),因此需要您的批准。席位意见:
你要做的:审阅后批准(Approve)本 PR。
Line 3 and Deviation 5 were amended by the
domain:specseat 1 (session_01B3TqpoQbTAfG7G74GMDWNW) before the at-tier review: this diff widens no accept set and adds no export, soClause-②isno (narrowing), as most retirements of this family onmaindeclare.20323-action-aria-removed.mddeclaredyes; the definition inclause2-line.mjsdecides, not the precedent. The changeset line moved with it in2252e5728, and the claim on #20300 was amended in place.The patch-round section above was added by the same seat after the at-tier record
5875291969.