test(create-objectstack,metadata): make two version-sensitive tests hold across the 17.5.0 version pass - #20565
Merged
Merged
Conversation
… CLI version from the scaffolder's own The "with dependencies installed" fixture planted `<major>.4.2` as the npm-resolved @objectstack/cli. The scaffolder writes `^<ownVersion>`, so that literal sits inside the range only while create-objectstack's minor is at most 4: the 17.5.0 version pass put it below the floor and the agreement test failed with "scaffolded Dockerfile pins :17.4.2 but the scaffolded package.json asks for ^17.5.0" — a fixture defect reported as a scaffolder one. The resolved version is now same major, next minor, patch 2 — always inside `^<ownVersion>` and always above its floor, which is what the fixture's comment says it is for. A new precondition test asserts exactly that, so a future fixture drift names the fixture instead of the scaffolder. Claude-Session: https://claude.ai/code/session_014VGCS11YUtYAiinRcdqQwL Co-authored-by: Claude <noreply@anthropic.com>
…not as the pre-release literal "logs one conversion summary per retired entry it replayed" asserted that no conversion notice says "predates this runtime's spec". That holds only while packages/spec still carries the 17.4.0 label: the 17.4.0-floored fixture then sits in the per-entry half of the window. The version pass moves the label to 17.5.0, the floor falls below it, the label half opens the window (verdict converted-forward), and the door correctly prints "predates this runtime's spec (authored engines.protocol floor 17.4.0, runtime spec 17.5.0)" — so the test went red on the versioned tree while the runtime behaved exactly as designed. The assertion now reads the running spec's label the way the door does (resolveInstalledSpecVersion) and pins the clause of whichever half that label selects: the per-entry "was built on a surface that still accepted shapes this runtime has since retired" clause below the floor, the "predates" clause with the exact floor and runtime version above it. Claude-Session: https://claude.ai/code/session_014VGCS11YUtYAiinRcdqQwL Co-authored-by: Claude <noreply@anthropic.com>
Contributor
📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs. What this run could not see
Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
hotlong
marked this pull request as ready for review
September 29, 2026 03:29
hotlong
enabled auto-merge
September 29, 2026 03:29
veigajoao
pushed a commit
to veigajoao/objectstack
that referenced
this pull request
Sep 29, 2026
… sites to the commits that decided them (stage 5) (objectstack-ai#20576) Part of objectstack-ai#20234 Clause-②: no ## What changed This is stage 5 of the staged sweep: the `ui/` area (`packages/spec/src/ui/**`, 133 files), plus two sites freed since stage 4: `data/filter-subtree-provenance.ts` (PR objectstack-ai#20460 landed without touching it) and `meta-spelling/manifest-collection-spelling.ts` (the census hand-over, comment 5882628946 on objectstack-ai#20234). `ui/view-grouping-query.ts` is excluded because objectstack-ai#20446's claim holds it; it carries 4 citations and none of them is dead, so the exclusion removes nothing. Later stages cover the other areas, so this PR says `Part of`. Every comment and docblock site in that population that cites a tracker number answering 404 has been rewritten in ruling C+D's form C (comment 5749154545 on objectstack-ai#19123). That is **87 comment sites**: 84 re-anchored and 3 respelled. - **84 re-anchored, over 25 numbers.** Each rewritten line now cites the commit in `origin/main` history that decided what the line describes, and says in its own words what that commit decided. One line (`ui/dashboard.zod.ts:686`) quotes ADR-0087 itself; it keeps ADR-0087 as its citation and paraphrases the amendment heading instead of quoting its number. - **3 respelled**, so each number of a sibling pair carries its own qualifier: `ui/view.zod.ts:3634` now reads `objectui#6110 + objectui#6111`, and `ui/component.zod.ts:3479` and `:4140` now read `objectui#8221's PR objectui#8758`. Each second number answers 404 here, and the sentence attributes it to objectui (objectui REST: `issues/6111` 200, `pulls/8758` 200, merged 2026-09-09). Only comments changed, plus the one generated reference page they project into and a patch changeset. Every source file keeps its line count (90 lines out, 90 in, over 25 files). Three of the 90 lines held no dead number; each is the other half of a rewritten sentence: `ui/action.zod.ts:400`, `ui/action-param-carryover.test.ts:13` and `ui/expression-bindable-text-keys.test.ts:119`. No code token moves (see the guard below). **No tracker number is added.** Every tracker number on an added line was already on the lines it replaces, and no `PR #N` is added. ## Census: before and after **Instrument.** This is the instrument of stages 1 to 4, rebuilt for this stage. It sends REST `GET /repos/objectstack-ai/objectstack/issues/N` without following redirects, for every distinct number cited in the population. The population is: - the citation gate's own exported `CITATION_RE` and `NON_CITATION_HEADS` at the base, kept when the qualifier is none, `objectstack`, `objectstack-ai/objectstack`, `framework`, `pre-` or `post-`; - matched case-insensitively (`Pre-`, `POST-`, `Framework`); - N of 100 or more, excluding `summon` heads. A qualifier covers only the number it is joined to. Each site is classified by the TypeScript parser as a line comment, a docblock, a block comment or a string. **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: 18 of 18 lit (200) and 18 of 18 dead (404) over 6 checkpoints in the base run, and 15 of 15 and 15 of 15 over 5 checkpoints in the head run. | reading | tree | numbers probed | 200 | 404 | 301 or other | dead sites | lines | files | of which comments | of which strings | |---|---|---|---|---|---|---|---|---|---|---| | before | base `487a7846df`, probed 2026-09-29T02:57:01Z to 02:59:36Z | 400 | 372 | 28 | 0 | **111** | 110 | 26 | 90 | 21 | | after | head `83e39641d0`, probed 2026-09-29T03:15:00Z to 03:18:06Z | 383 | 372 | 11 | 0 | **24** | 23 | 8 | 3 | 21 | The head probe found no number newly dead since the base probe: the same 372 numbers answer 200. The head was probed at `83e39641d0`; every census file is byte-identical at the final head. **Cross-check under the grammar that landed during this stage.** PR objectstack-ai#20554 (`199002b3e4`) landed the closed qualifier set while this stage ran, and it reads `objectui PR objectstack-ai#8758` as objectui's number. Re-run with that gate's own `extractCitations` and `namesThisRepository`, the same population reads **108** dead sites before and **21** after, all 21 test strings. The difference is exactly the three `objectui PR objectstack-ai#8758` prose sites below, which that PR's own header measured as "census deaths here that are not deaths at all". **Per file.** Cited sites are every in-repo citation the population reads, live or dead. | file | cited sites (base) | dead before | by class | dead after | |---|---|---|---|---| | `data/filter-subtree-provenance.ts` | 9 | 3 | 3 docblock | 0 | | `meta-spelling/manifest-collection-spelling.ts` | 8 | 2 | 2 line comment | 0 | | `ui/action-param-carryover.test.ts` | 5 | 3 | 2 line comment, 1 string | 1 | | `ui/action.test.ts` | 39 | 3 | 3 line comment | 0 | | `ui/action.zod.ts` | 90 | 7 | 5 docblock, 2 line comment | 0 | | `ui/bulk-action.test.ts` | 9 | 4 | 2 line comment, 2 string | 2 | | `ui/bulk-action.zod.ts` | 9 | 3 | 2 docblock, 1 line comment | 0 | | `ui/component-element-navigation-17987.test.ts` | 8 | 5 | 1 docblock, 4 string | 4 | | `ui/component-type-vocabulary.test.ts` | 8 | 2 | 2 docblock | 0 | | `ui/component-type-vocabulary.ts` | 2 | 1 | 1 docblock | 0 | | `ui/component.test.ts` | 190 | 22 | 11 line comment, 11 string | 11 | | `ui/component.zod.ts` | 265 | 20 | 16 docblock, 4 line comment | 0 | | `ui/dashboard.zod.ts` | 52 | 1 | 1 docblock | 0 | | `ui/expression-bindable-text-keys.test.ts` | 3 | 2 | 2 line comment | 0 | | `ui/expression-bindable-text-keys.zod.ts` | 4 | 2 | 2 docblock | 0 | | `ui/form-select-option.test.ts` | 3 | 1 | 1 docblock | 0 | | `ui/index.ts` | 15 | 2 | 2 line comment | 0 | | `ui/interaction-config-retirement.test.ts` | 19 | 1 | 1 docblock | 0 | | `ui/react-blocks.test.ts` | 9 | 2 | 1 docblock, 1 string | 1 | | `ui/react-blocks.ts` | 17 | 4 | 2 docblock, 2 line comment | 0 | | `ui/view-form-features-root.test.ts` | 4 | 1 | 1 line comment | 0 | | `ui/view-metadata-schema.test.ts` | 31 | 3 | 2 line comment, 1 string | 1 | | `ui/view-submit-redirect-url.test.ts` | 8 | 1 | 1 line comment | 0 | | `ui/view.test.ts` | 136 | 2 | 1 docblock, 1 string | 2 | | `ui/view.zod.ts` | 331 | 13 | 10 docblock, 3 line comment | 2 | | `ui/widget-i18n-retirement.test.ts` | 17 | 1 | 1 line comment | 0 | `ui/` alone went from 106 dead sites in 24 files to 24. The other 107 `ui/` files carry no dead site. ## Per-number table Anchors are 9-hex commit abbreviations. "Wrote" means the commit's own diff added the line being rewritten. | number | comment sites / files | anchor: what it decided | |---|---|---| | `objectstack-ai#5970` | 4 / 2, `action.zod.ts:819`, `action.test.ts:268`, `:304`, `:354` | `97e7e3caa`: `ActionSchema.visible` / `disabled` speak one condition shape; `visible` gains its boolean arm. Stage 1's anchor for the same number | | `objectstack-ai#6276` | 7 / 1, `component.zod.ts:34`, `:165`, `:568`, `:2455`, `:2546`, `:2554`, `component.test.ts:2126` | `78f0be872`: declares `element:record_picker`'s flat `sort` / `limit` on the objectstack-ai#5611 rule (maintainer ruling 2026-08-08, direction A). It wrote `:34`, `:2455` and the "enumerate by the renderer's read pattern" lesson | | `objectstack-ai#8794`, `objectstack-ai#8836` | 3 / 1, `filter-subtree-provenance.ts:130`, `:131`, `:156` | `1850ebbb0`: corrects the reuse-safety claim from the survey and pins the invariant. It wrote `:131` itself. Stages 1 and 3 gave both numbers this anchor | | `objectstack-ai#9933` | 7 / 2, `view.zod.ts:2517`, `:5060`, `:5086`, `:5118`, `:5513`, `view-metadata-schema.test.ts:398`, `:410` | `d5552ca13`: admits `columnState` as an explicitly runtime-only view-overlay key, rejected by name at every authoring door. It wrote "explicitly out of objectstack-ai#9933's scope" | | `objectstack-ai#9972` | 3 / 2 (+1 unread), `component.zod.ts:2213`, `component.test.ts:382`, `:3565`; also `:3612`'s `objectstack-ai#9881/objectstack-ai#9972`, a slash-joined spelling the grammar does not read | `60e0f900a`: records the live read point of `page:tabs` `items[].icon` and its accept-pin. It wrote the `:382` header | | `objectstack-ai#10194` | 1 / 1, `manifest-collection-spelling.ts:71` (`pre-objectstack-ai#10194`) | `2306a765c`: `/meta/theme` and `/meta/analytics_cube` stop storing any JSON as success and validate at the write door. The line now says "the store-anything branch from before commit 2306a76". Stage 1's anchor | | `objectstack-ai#10274` | 6 / 2, `component.zod.ts:2304`, `component.test.ts:308`, `:405`, `:3591`, `:3603`, `:3612` | `d1ba685ec`: re-measures the four pin citations and gates the class. Its gate header records that the re-measure found two anchors wrong since they were written, which is why a refresh re-reads. Stage 3's anchor | | `objectstack-ai#10485` | 4 / 4, `index.ts:51`, `interaction-config-retirement.test.ts:116`, `widget-i18n-retirement.test.ts:112`, `manifest-collection-spelling.ts:67` | `35ad101bc`: retires the `themes` carrier and `ThemeSchema` whole (ruled B, 2026-08-21; ADR-0049 stays cited). Stage 1's anchor | | `objectstack-ai#11284` | 5 / 2, `react-blocks.ts:39`, `:94`, `:109`, `:295`, `react-blocks.test.ts:139` | `5383fa670`: the react tier converges on the metadata-tier vocabulary, deprecate-first. Its changeset heads "(objectstack-ai#11284, maintainer ruling 2026-08-23)" | | `objectstack-ai#11350` | 1 / 1, `index.ts:105` | `ece4dad31`: records the maintainer ruling of 2026-08-23 that a type in an entry's public declarations must be nameable from that entry. Same wording as stage 1's `kernel/index.ts:53` | | `objectstack-ai#11507` | 2 / 2, `component.zod.ts:1465`, `component.test.ts:2726` | `88b9d749a`: declares `sys_activity.type` an open, author-extensible vocabulary (maintainer ruling 2026-08-24, direction 4). Stage 3's anchor | | `objectstack-ai#11658` | 2 / 2, `component.zod.ts:1464`, `component.test.ts:2725` | `1a6a19c31`: opens `RecordActivityProps.types` to author-contributed kinds, executing that ruling. Stage 3's anchor | | `objectstack-ai#11703` | 3 / 2, `action.zod.ts:399` to `:400`, `:460`, `action-param-carryover.test.ts:12` to `:13` | `5cb62d88b`: `clone_permission_set` carries all five copied facets; its params list had silently dropped three. The lines now name "the silent-drop shape commit 5cb62d8 fixed" | | `objectstack-ai#11753` | 5 / 2, `action.zod.ts:66`, `:390`, `:398`, `:409`, `action-param-carryover.test.ts:1` | `0e4e51b0a`: `ActionParamSchema.carryOver`, the spec half of the 2026-08-25 maintainer ruling (recommendation A). It wrote every one of these lines, and its changeset records the `visible: false` measurement `:398` names | | `objectstack-ai#12194` | 1 / 1, `view.zod.ts:4838` | `311433f6b`: declares the metadata item-name grammar (`QUALIFIED_ITEM_NAME_PATTERN` among it) and refuses it at the publish door. Stage 2's anchor | | `objectstack-ai#12868` | 5 / 2, `view.zod.ts:2938`, `:2966`, `:3179`, `:7087`, `form-select-option.test.ts:4` | `c459da6bc`: narrows the per-option `default` key out of the form-view options vocabulary. Its changeset records the ruled census `:2966` cites ("measured ZERO occurrences"). Stages 3 and 4's anchor | | `objectstack-ai#12950` | 3 / 2, `component-type-vocabulary.ts:4`, `component-type-vocabulary.test.ts:4`, `:101` | `225e7690f`: created `component-type-vocabulary.ts`; its message records the readiness read `:101` pins (`global:search` and `global:notifications` stay declared) | | `objectstack-ai#13156` | 2 / 2, `view-form-features-root.test.ts:70`, `view-submit-redirect-url.test.ts:110` | `fd289be45`: strips tracker ids from function-declaration-built refusal prose. It wrote both lines. Stage 3's wording ("commit fd289be's strip") | | `objectstack-ai#13670` | 1 / 1, `expression-bindable-text-keys.zod.ts:72` | `8c6a7fc0b`: records `text.value` as deliberately omitted; its message states the ruling that `text`'s evaluation channel is `content` alone | | `objectstack-ai#13672` | 3 / 2, `expression-bindable-text-keys.zod.ts:89`, `.test.ts:65`, `:118` | `e854a531a`: narrows the `button` row to the spelling its key reaches, and records `action:button` and `ui:button` as deliberately out | | `objectstack-ai#16626` | 1 / 1, `component.test.ts:2336` | `30b099078`: the objectui pin bump to `53ded82bf7a4` that ships objectui#7754's array-analytics lowering, the door the family waited on. The association is PR objectstack-ai#16788's body (it names objectstack-ai#16626 as the card it lands), and `30b099078` is that PR's merge commit; neither its message nor its diff names objectstack-ai#16626 (the stage-3 objectstack-ai#11065 precedent) | | `objectstack-ai#17987` | 9 / 2, `component.zod.ts:10`, `:4014`, `:4062`, `:4153`, `:4193`, `:5068`, `:5226`, `:5457`, `component-element-navigation-17987.test.ts:4` | `e233db9db`: declares element-level `navigation` on `object-kanban` / `object-calendar` and gives `object-timeline` its `ComponentPropsMap` row, executing the objectui#8652 ruling (verbatim `B`) | | `objectstack-ai#18003` | 1 / 1, `dashboard.zod.ts:686` | **ADR-0087**, the rung above a commit. The line quoted the ADR's own amendment heading, number included. It now reads "(ADR-0087, its 2026-09-13 amendment, 「the level half」)": the ADR stays the citation and the fragment it quotes is verbatim | | `objectstack-ai#18177` | 5 / 2, `bulk-action.zod.ts:51`, `:169`, `:262`, `bulk-action.test.ts:61`, `:307` | `adabccf5f`: `BulkActionParamSchema` is strict and declares `dependsOn`, executing decision batch objectstack-ai#146 item 4, letter A | | `objectstack-ai#6111` | 1 / 1, `view.zod.ts:3634` | respelled `objectui#6111` (not re-anchored): it is objectui's number | | `objectstack-ai#8758` | 2 / 1, `component.zod.ts:3479`, `:4140` | respelled `PR objectui#8758` (not re-anchored): objectui's PR objectstack-ai#8758, merged 2026-09-09 | No ADR or ruling-record file in `docs/adr/` or `scripts/adr-anchors/` records the decision behind any of the 25 re-anchored numbers except objectstack-ai#18003. ADR-0126 mentions objectstack-ai#11703 and objectstack-ai#11753 only as references ("permission-set precedent"), not as the record of either decision. **Anchor checks.** Every sha on an added line is one of 23, and none is on a removed line. At the base `487a7846df`: - each matches exactly one object (`git rev-parse --disambiguate`, count 1, 23 of 23); - each is an ancestor (`git merge-base --is-ancestor`, exit 0, 23 of 23); the control leg `e9584681a4` also exits 0, and the repository is not shallow; - for 22 of the 23, a grep of the commit's own message or diff finds the number it replaces (the message for 16; the diff for `0e4e51b0a`, `5383fa670`, `c459da6bc`, `225e7690f`, `e854a531a`, and for objectstack-ai#8836 in `1850ebbb0`). `30b099078` is the exception explained in the table. - Each commit was read for the rule its line states, not only for the number. In most cases the commit wrote the very line it now anchors. Wordings to check, each true of its commit: - `filter-subtree-provenance.ts:130` and `:156` read 「survey commit 1850ebb records」: the survey was the card's, and the commit's message records its measurement. It is stage 3's wording for the same relation (「from the survey it records」). - `component.zod.ts:1465` and `component.test.ts:2726` read 「maintainer ruling commit 88b9d74 declared」: that commit landed the ruling (direction 4) as the `sys_activity.type` declaration. - `manifest-collection-spelling.ts:71` reads 「the store-anything branch from before commit 2306a76」: before that commit, `PUT /meta/theme/:name` stored any JSON as success. ## Mechanical guard: no code token moves The check compares leaf tokens with comments stripped, base `487a7846df` against the head. It uses the TypeScript parser's leaf tokens from the head's lockfile, so template literals are scanned in context, and it excludes JSDoc nodes. It ran over all 25 touched `.ts` files, and every control mutates the head text in memory only. - Real run: 101,836 base tokens, **0 files with a token change** (exit 0). - Comment-insertion control (`ui/index.ts`): 0 files changed (exit 0). - Positive control (a declaration inserted into `ui/view.zod.ts`): 1 file reads DIFFER at token 19222 (exit 1). - Positive control (one digit changed in a `component.test.ts` test title): 1 file reads DIFFER at token 14972 (exit 1). Line balance holds in every file, 90 out and 90 in over the 25, and every line count is equal at base and head. Tracker numbers: added-not-removed is empty in every file. The net-removed numbers are the 25 in the table, 85 sites: the census's 84 comment sites, plus the slash-joined `objectstack-ai#9972` at `component.test.ts:3612`. ## Generated page `check:generated` proved one artifact stale: `content/docs/references/ui/expression-bindable-text-keys.mdx`, the projection of `expression-bindable-text-keys.zod.ts`'s module docblock. `check:generated --fix` regenerated only that page, and a re-run read `All 15 generated artifacts are up to date`. Its two changed lines are the `:72` and `:89` substitutions verbatim. No other docblock here projects into a reference page, and nothing under `skills/**` moved. ## 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: 6 touched sources are `src/**/*.zod.ts`, which `files[]` ships verbatim. The rewritten comments also reach `dist`: - `c459da6bc` appears in 32 bundled `.js` files and 2 `.d.ts`; - `adabccf5f` in 24 `.js` and 2 `.d.ts`; `d5552ca13` and `0e4e51b0a` in 24 `.js` each; `e233db9db` and `78f0be872` in 2 `.js` and 2 `.d.ts` each; - the positive control, the pre-existing sentence 「the object-field face enforces」, appears in 32 files. ## Gates (head `1b885d3c27`) - **Citation judging pass, run as CI runs it:** `pnpm check:issue-citations && node scripts/check-issue-citations.mjs`, both under the grammar PR objectstack-ai#20554 landed, exit 0. The self-test passes 114 cases in 8 batteries. The live, diff-scoped run judged 13 citations across 11 files: 3 resolve and 10 are declared cross-repo references. It reads "every citation this change adds resolves". - **Doc authoring:** `pnpm check:doc-authoring` exits 0. Its 16,759 customer-facing strings across 1,174 spec sources carry no internal issue id, and the sibling-package prose-id baseline holds with no growth. - **Derived gates:** `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` at this head derived 112 families, and all 112 exit 0. `--ran` reports 112 run, 0 NOT MEASURED, 0 unrun. A full `turbo run build` of `./packages/*` at this head ran first, under the shared verify lock: 71 of 71 tasks, VERDICT command-exit 0. So no gate met an unbuilt prerequisite. - **Five roster gates the derivation flags for this diff** (their rosters sit in `.changeset/` or `packages/`, so their silence proves nothing): `node scripts/check-changeset-fixed.mjs`, `pnpm --filter @objectstack/spec run check:spec-changes`, `pnpm check:authz-resolver`, `pnpm check:error-code-casing` and `pnpm check:filter-alias-parity`. All exit 0. - `pnpm --filter @objectstack/spec run check:generated` (derived) reads `All 15 generated artifacts are up to date`, and `check:docs` reads `226 generated files in sync`. - **Tests and typecheck, under the lock:** - `pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 src/ui src/meta-spelling`: Test Files 98 passed (98), Tests 3452 passed (3452), VERDICT command-exit 0 (the chain held the lock 142s on a shared box). - The 16 spec suites outside `src/ui` that read the touched files' source text or pin their lines: Test Files 16 passed (16), Tests 489 passed (489). They are `scripts/{export-origins,file-description,root-index,schema-closure,skill-map-guards,strictness-ledger}.test.ts`, `src/ai/tool-confirmation-prescription-tense.pin.test.ts`, `src/api/api-entry-graph.pin.test.ts`, `src/contracts/scoped-context.test.ts`, `src/data/filter-subtree-provenance.test.ts`, `src/shared/{alias-integrity,evaluated-slot-population,retired-key-migrate-sentence,union-author-message-pins}.test.ts`, `src/system/constants/platform-object-names.test.ts` and `src/type-alias-convention.pin.test.ts`. Three more suites matched the reader scan and are not run here: `scripts/build-schemas-check-mode.test.ts` only imports `ViewItemSchema` (code the guard proves unchanged) and rebuilds schemas in a temp tree; `scripts/def-key-collisions.test.ts` names `ui/view.zod.ts` only in a comment; `scripts/published-projection-choke-point.test.ts` matched on `build-react-blocks-contract.ts`, not a touched file. They are left to CI. - `pnpm --filter @objectstack/spec typecheck`: exit 0, including `check:test-typecheck` (53 files, 251 errors, 138 pinned signatures held). The same three runs also passed, with the same counts, on the pre-merge tree. - **Lint, as a proven narrowing:** `eslint --no-inline-config --format json` over the 25 touched `.ts` files gives 25 files, 0 errors and 0 warnings. All 25 are in eslint's own population (`isPathIgnored` is false for each, read through eslint's API). `eslint.config.mjs` never enables type-aware linting (no `parserOptions.project`, which its own lines 327 to 328 state), so a comment edit here cannot move the verdict on any untouched file. The repo-wide `pnpm lint` is CI's run. - **Merge probe:** a `merge-tree` of the head onto `origin/main` `f572a7eb3c`, from a bare shared clone with no merge driver registered, exits 0 (2026-09-29T04:15Z). ## Acceptance notes - **Base and merge.** The branch forked from `487a7846df`, one commit past the claim's stamp `6154165484` (PR objectstack-ai#20551, outside the surface). `origin/main` then moved three commits, and `199002b3e4` (PR objectstack-ai#20554) changed `scripts/check-issue-citations.mjs`, so the gate derivation read STALE TREE. `origin/main` `dee9b26f6c` was merged in (`1b885d3c27`): a clean merge with no driver-routed path and no lockfile change, touching none of this diff's files. The PR's delta against `dee9b26f6c` is exactly its 27 files. `origin/main` has since moved one more commit, `1c761c0d71` (PR objectstack-ai#20565, tests in two other packages), which touches none of them. - **One site beyond the hand-over's list.** The hand-over named `manifest-collection-spelling.ts:71` (`pre-objectstack-ai#10194`). The same comment's first line, `:67`, cites `objectstack-ai#10485`, which also answers 404, and it is rewritten too. The claim names this file; the fix is the same defect class, mechanical in the form stages 1 to 4 fixed, in a file no other claim holds, under the same gates. Reverting it would be one line. - **Open PRs, re-read at 2026-09-29T04:22Z:** 8 open PRs, and none touches any file in this diff. The one that touches `ui/` is objectstack-ai#20570 (objectstack-ai#20446's), on the excluded `view-grouping-query.ts`. The in-flight `Claim:` comments on the 12 `pm:dispatched` cards were read too: only objectstack-ai#20446's names a `ui/` file (`view-grouping-query.ts`, excluded above). - **Hypothesis 2, measured.** In `ui/`, 14 sibling-qualified pairs leave the second number bare: 13 on one line (`+`, `and`, `,`, `/` or `'s PR` between them) and `component.zod.ts:3478` to `:3479`, split across a line break. In 3 of them the second number answers 404 here and the sentence attributes it to objectui (`objectstack-ai#6111` once, `objectstack-ai#8758` twice); they are respelled above. In the other 11 the second number answers 200 here, so it is judged as this repository's and left: `action.zod.ts:1603`, `component.test.ts:2052`, `:2199`, `:2315`, `component.zod.ts:3276`, `:3793`, `:4812`, `expression-bindable-text-keys.zod.ts:33`, `page.test.ts:696`, `react-blocks.ts:256` and `widget.zod.ts:34`. The PR objectstack-ai#20554 header measured `,` and `and` pairs as naming this repository's number and `/` pairs as mostly, but not always, the qualifier's. Whether any `/` pair here names objectui's number is not measured; a 200 here cannot tell. - **What stays in this population: 24 dead sites** (21 under the landed grammar). - **21 test strings**, left as tokens (vitest `it` / `describe` titles in 7 test files): `objectstack-ai#6276` ×6, `objectstack-ai#11658` ×3 and `objectstack-ai#11507` ×1 in `component.test.ts`; `objectstack-ai#9972` in `component.test.ts:411`; `objectstack-ai#17987` ×4 in `component-element-navigation-17987.test.ts`; `objectstack-ai#18177` ×2 in `bulk-action.test.ts`; `objectstack-ai#9933` in `view-metadata-schema.test.ts:406`; `objectstack-ai#11284` in `react-blocks.test.ts:147`; `objectstack-ai#11753` in `action-param-carryover.test.ts:17`; `objectstack#11195` in `view.test.ts:3396`. None is a Zod `.describe()` text, an exported string or a migration-entry field, so no form D site arises here. - **3 comments that name objectui's live PR objectstack-ai#8758 in prose** (`objectui PR objectstack-ai#8758`): `view.zod.ts:2354`, `:2579` and `view.test.ts:426`. They are not pairs, and the landed grammar reads them as objectui's. - **Left for later stages of objectstack-ai#20234** (the stage-4 landing comment 5882686893's list, unchanged): the migrations area, the `liveness/**` notes, the `why` strings and the `PROVENANCE_WAIVERS` reason, `rest-server.zod.ts`, the held `analytics*` and `driver/turso.*` files, the 2 `AGGREGATION_CASES` note strings, and `data/`'s test strings and deliberate markers. - **The same rot outside `packages/spec/src`** is objectstack-ai#20556's, not this card's. Examples met here: ADR-0087's own amendment heading (`docs/adr/0087-metadata-protocol-upgrade-contract.md:390`, `:397`) cites the dead `objectstack-ai#18003`, and ADR-0126 cites `objectstack-ai#11703` and `objectstack-ai#11753`; both are governed. `packages/spec/scripts/strictness-ledger.test.ts:375`, `:380` cite `objectstack-ai#9933`, and `check-objectui-pin-citations.ts` cites `objectstack-ai#10274` and `objectstack-ai#9972`. - **Unchanged wording.** `react-blocks.test.ts:140` says the 2026-08-23 ruling was "recorded on-card". The card is gone, and the changeset of `5383fa670` (now cited on `:139`) records the ruling. The line holds no number, so it is left. - **The citation gate's reach.** It defers `packages/**/*.test.ts`. The 11 touched non-test files are in its judging population. --- _Generated by [Claude Code](https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #20560
Unblocks the 17.5.0 Version Packages PR #17076 (
changeset-release/main). Its requiredTest Core (1/6)is red on headde35e567(CI run 36510180528):Both changes are test-only. No runtime code moves, and neither file ships: both packages publish only
dist,README.mdandCHANGELOG.md, and neither file is a tsup entry. So there is no changeset and the PR carriesskip-changeset.1.
create-objectstack: the runtime-image fixture (the reported failure)runtime-image.test.tsplanted the npm-resolved@objectstack/clias the literal${major}.4.2. The scaffolder writes a caret range on its own version, so that literal is inside the range only while the minor is 4 or lower. At 17.5.0 it falls below the floor, and the agreement test blamed the scaffolder for what was a fixture defect.resolvedis now derived from the package's own version: same major, own minor + 1, patch 2 (17.4.0 gives 17.5.2, 17.5.0 gives 17.6.2). It is always inside the caret range and always above its floor, which is what the fixture's own comment says it is for.plants a resolved version inside the scaffolded range and above its floor, makes future fixture drift point at the fixture rather than at the scaffolder.I checked the other create-objectstack tests for a current-minor assumption. They key on the major only (
template-consistency), use fixed literals unrelated to the package version (thereadResolvedCliVersioncases,banner-version), or carry a fixture version of their own (template-version-stamps). The whole suite passes on the versioned tree (table below).2.
metadata: artifact-door notice wording (found by running the unreached shard-1 packages on the versioned tree)Turbo stopped shard 1 at the failure above, so ten scheduled packages never ran against 17.5.0. Running them found one more failure of the same class:
The fixture is a 17.4.0-built artifact (
engines.protocol: ^17.4.0). Whilepackages/speccarries the 17.4.0 label, the floor is not below the label. The per-entry half of the window opens (converted-retired-after), and its clause deliberately avoids "predates". The version pass moves the label to 17.5.0 and the floor falls below it. The label half opens instead (converted-forward), and the door correctly printspredates this runtime's spec (authored engines.protocol floor 17.4.0, runtime spec 17.5.0). The runtime behaves as designed; the assertion had the pre-release label built in.The assertion now reads the running spec label the same way the door does, with
resolveInstalledSpecVersion().plugin-artifact-forward-conversion.test.tsalready uses that pattern. The test then pins the exact clause of whichever half that label selects. Both halves were exercised: the per-entry clause onmain(label 17.4.0), and thepredatesclause with the exact floor and runtime version on the versioned tree (label 17.5.0).Verification on the versioned tree
I made a throwaway worktree at
origin/changeset-release/main=de35e567, the #17076 head that the failing run tested, and copied both fixes in. Then:pnpm install --frozen-lockfile.pnpm turbo run buildover each package's dependency closure (--filter='PKG^...'; 61 tasks, 0 cached).pnpm --filter PKG run SCRIPT, with the env Test Core sets (OS_TEST_TIERS=queue,CI=true).Of shard 1's other scheduled packages,
@objectstack/typeshad already passed in that run, and shards 2–6 were green. So every package that run scheduled has now passed against thede35e567tree.Checks on this branch (base
b0574343)pnpm --filter create-objectstack test: 16 files / 234 tests pass;typecheckclean.pnpm --filter @objectstack/metadata test: 55 / 826 pass (the per-entry half);typecheckclean.eslinton both changed files: clean.mainhas since moved tof6ceddc3, six commits on. None of them touchespackages/create-objectstack,packages/metadataorpackages/metadata-core.A wider gap is tracked separately in #20562 and not addressed here: a version-sensitive test is only exercised after
pnpm run version.Generated by Claude Code