Repository navigation
feat(spec)!: PROTOCOL_VERSION 17 → 18 in an ordinary PR — regenerated spec-changes.json and upgrade guide, ^18 handshakes, pre-mode lockstep exception (#22085 Q1 → B) - #22215
Conversation
…nd upgrade guide, ^18 handshakes, pre-mode lockstep exception The protocol major moves in an ordinary pull request with full CI (ruling record 6049734955, Q1 -> B). spec-changes.json and the 17 -> 18 section of docs/protocol-upgrade-guide.md are regenerated by gen:spec-changes and gen:upgrade-guide. Handshakes that mean the current protocol read ^18. The lockstep's one exception (Changesets pre mode with a pending major for @objectstack/spec) is judged in a new repo-project test that reads .changeset/, declared on @objectstack/spec#test:repo; the local lockstep recognises only the exception's shape and hands off to it. Claude-Session: https://claude.ai/code/session_01LAi5BVvQNiYzepSAcsoFLK Co-authored-by: Claude <noreply@anthropic.com>
…ADR-0087 disposition Claude-Session: https://claude.ai/code/session_01LAi5BVvQNiYzepSAcsoFLK Co-authored-by: Claude <noreply@anthropic.com>
… merging main; changeset names the 64th step-18 conversion Claude-Session: https://claude.ai/code/session_01LAi5BVvQNiYzepSAcsoFLK Co-authored-by: Claude <noreply@anthropic.com>
… floor, which the protocol 18 handshake admits Claude-Session: https://claude.ai/code/session_01LAi5BVvQNiYzepSAcsoFLK Co-authored-by: Claude <noreply@anthropic.com>
…spec's pre-mode lockstep check Claude-Session: https://claude.ai/code/session_01LAi5BVvQNiYzepSAcsoFLK Co-authored-by: Claude <noreply@anthropic.com>
… top-level directory turbo.json now declares Claude-Session: https://claude.ai/code/session_01LAi5BVvQNiYzepSAcsoFLK Co-authored-by: Claude <noreply@anthropic.com>
…* root (22 -> 23) Claude-Session: https://claude.ai/code/session_01LAi5BVvQNiYzepSAcsoFLK Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 2 package(s): 3 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 — 144 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 faf473962ac6defe325d4c5ad2487aa1725185fb && git checkout faf473962ac6defe325d4c5ad2487aa1725185fb
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin e148ca984258c7c0d162eefd04b1f0f77c3fa9fd 6ec484243e3808af9680bb5b05822d768fb24cc0 && git checkout -B drift-repro e148ca984258c7c0d162eefd04b1f0f77c3fa9fd && git merge --no-ff 6ec484243e3808af9680bb5b05822d768fb24cc0
node scripts/docs-audit/affected-docs.mjs --json e148ca984258c7c0d162eefd04b1f0f77c3fa9fd
|
… merging main (step 18: 64 conversions, 319 semantic entries) Claude-Session: https://claude.ai/code/session_01LAi5BVvQNiYzepSAcsoFLK Co-authored-by: Claude <noreply@anthropic.com>
…ation edits, per ruling 6056808625 (E) Each reverted file now equals origin/main at 3513ac7. Claude-Session: https://claude.ai/code/session_01LAi5BVvQNiYzepSAcsoFLK Co-authored-by: Claude <noreply@anthropic.com>
…evidence; the local lockstep test hands the shape to it (ruling 6056808625, E) When PROTOCOL_VERSION's major is one ahead of @objectstack/spec's stable version, the gate requires Changesets pre mode AND a pending major for the spec package, read from source text with no build, and otherwise refuses naming the remedy. Its own self-test battery covers both directions. The changeset is regraded major now that pre mode is on main. Claude-Session: https://claude.ai/code/session_01LAi5BVvQNiYzepSAcsoFLK Co-authored-by: Claude <noreply@anthropic.com>
…nt protocol, ^18 #22381 (97610a5) added test/verify-host-root.test.ts with a current-app fixture pinned to engines.protocol '^17'. Under PROTOCOL_VERSION 18.0.0 the runtime handshake refuses that app, so `os verify` exits 1 before the pin's subject (the app-dir anchoring) is ever reached. The fixture now declares '^18', the same sweep rule this branch applies to every current-app fixture. Claude-Session: https://claude.ai/code/session_01VZqqwTj2wsihZEbfT6yyYN Co-authored-by: Claude <noreply@anthropic.com>
… merging main at e148ca9 (step 18: 64 conversions, 329 semantic entries) main added two step-18 semantic entries since 8b713fa, both from #22447 (7895671): manage-org-presentation-retired and meta-doors-organization-scope-retired. At protocol 18 both generators project every step-18 entry, so both documents gain them. The conversion ids are unchanged; the 16 -> 18 aggregate is 406, equal to the registry. Claude-Session: https://claude.ai/code/session_01VZqqwTj2wsihZEbfT6yyYN Co-authored-by: Claude <noreply@anthropic.com>
…erge Claude-Session: https://claude.ai/code/session_01VZqqwTj2wsihZEbfT6yyYN Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewRead at 2026-10-09T16:26Z · PR #22215 (open, non-draft, auto-merge off, Served-tier: Inputs: the card body and its 29 comments (the ruling ① Derived judgmentsWhat this delta is, read off the trees. Five first-parent commits. The hand-edited fixture, judged. The regenerated documents equal the merged registries. What The hand-edited changeset, line by line. The word diff of Nothing else the PR owns moved. Of the 52 paths, 48 have byte-identical +/− lines in the two net diffs ( No accept set moves beyond what the PR already declared. The one new site is a fixture (a test input), not a door; the two documents project entries the registries already carried; the changeset edit is prose. No door, schema, export or refusal string changed in this delta. The public surface of the PR is the one Re-sync at my read: none owed. ② Semver levelUnchanged in grade and re-verified where the delta could touch it: ③ Boundary flagsDev flags (report
Check-runs on this head at my reads (42 runs across 35 names; seven names ran twice because the body edit after the dev report fired a second Implemented-by: VERDICT: PASS |
…e merged tree Main's 4e9fe9f (PROTOCOL_VERSION 17 -> 18, #22215) began rendering the 18.* semantic entries into both artifacts. Six of them carry this branch's re-measured objectui pin readings, so the merged tree's copies were stale. Generators only: pnpm --filter @objectstack/spec gen:spec-changes and gen:upgrade-guide. No source edit. Claude-Session: https://claude.ai/code/session_01BmsuLyUeuG5CNpZFMH1jzS Co-authored-by: Claude <noreply@anthropic.com>
…t scaffold with the workspace protocol (nightly tiers) (#22699) Fixes #22622 Clause-②: no Two nightly-tier CLI test files change and nothing else: no accepted input, key, export or error code moves, and nothing publishes. Two independent reds on the nightly tiers, both in `domain:cli`. **Both are fixed inside the nightly tests**, following the seat's review 6099406666 on #22622. Nothing else changes: - the family-roster check; - the noise budget and every assertion in that file; - the `create-objectstack` template, the version-time stamp (`scripts/sync-template-versions.mjs`) and their tests, which are byte-for-byte as `main` has them. ## (a) Both reds, reproduced before any fix Base `9646991283`, `OS_TEST_TIERS=nightly`, `--project integration`, through the verify lock: `Tests 2 failed | 51 passed (53)`. - `json-stdout-purity.e2e.test.ts`, `is exactly the set listed here`: `expected [ 'meta resync', …(17) ] to deeply equal [ 'meta resync', …(15) ]`, with `+ "migrate organization-ownership"` and `+ "migrate security-catalog-overlays"`. `migrate organization-ownership` came from PR #22643, after the nightly that filed the card. The members were taken from `discoverFamily()`'s output. - `serve-boot-diagnostics-noise-budget.e2e.test.ts`, `highlights the dead button once…`: `expected 0 to be greater than 0` at `:176`. The boot printed `✗ package 'com.example.support-desk' targets protocol ^17 (engines.protocol) but this runtime is protocol 18.0.0. This is a major-version break. Run: objectstack migrate meta --from 17`. That is a refusal, so no *Boot diagnostics* block is printed at all. After, on `3d63ed7db5`: `Test Files 2 passed (2)`, `Tests 59 passed (59)`. That is 53, plus 3 cases for each of the two new members. ## Failure 1: the two new `--json` members are driven `FAMILY` gains both members, each in its bare form. The CLI was first run against the purity fixture to see each one's face: - `migrate organization-ownership` shows its refusal face. Over a database with none of the platform tables it prints `{"error":"plan_refused","reason":"not-an-objectstack-database",…}` and exits 1. The JSON is emitted after `stack.shutdown()`, and no plan file is written. - `migrate security-catalog-overlays` shows its read-only preview: `listed: 0`, exit 0. There is no `sys_metadata` hydration and nothing is deleted. Both print all three boot diagnostics on stderr. The discovery and the `toEqual` roster check are untouched. ## Failure 2: the noise-budget e2e aligns the pair it boots ### (b) What the version-time stamp writes Measured read-only at `9646991283`: - `changeset status --output` (`pre.json` is `{"mode":"pre","tag":"next"}`) gives `@objectstack/spec major 17.7.0 -> 18.0.0-next.0` and `create-objectstack major 17.7.0 -> 18.0.0-next.0` (the `fixed` group). - `loadScaffolderVersion()` on `18.0.0-next.0` gives `{"version":"18.0.0-next.0","major":"18","range":"^18.0.0"}`. So the version pass stamps `engines: { protocol: '^18' }` into the released template. **Triage's "every project that v18's `create-objectstack` scaffolds would boot with the protocol-gap warning" is false.** No released v18 scaffold carries the gap. PR #22215's changeset says the same ("the template keeps `'^17'` until the version pass stamps it"). The red is `main`'s pre-mode window: packages at `17.7.0`, `PROTOCOL_VERSION` at `18.0.0`. Only an in-repo pairing sees it: a published-line scaffold booted on the workspace runtime with nothing installed. ### Round 1 changed the product side, and CI falsified that Round 1 (`bff640d98`) stamped the template's `engines.protocol` from `PROTOCOL_VERSION`'s major. On that head, `Scaffold with repo dist` (`scaffold-e2e.yml`, job 1) went red: "package 'com.example.e2e-app' targets protocol ^18 (engines.protocol) but this runtime is protocol 17.0.0". That gate guards the path a user takes. It scaffolds with the repo-built scaffolder and installs `@objectstack/*` from the registry (`^17.0.0`, so protocol 17). On that path a template's range must match the protocol of the packages it installs, which is the package major. The sync script and the `template-consistency` ratchet already enforce exactly that on `main`. Commit `60fa01c7a7` removes round 1's product-side change: - `templates/blank/objectstack.config.ts`, `template-consistency.test.ts`, `template-version-stamps.test.ts` and `scripts/sync-template-versions.mjs` are back at their `main` blobs (`6308f29e`, `d32c819c`, `48ba146e`, `f7704db8`, all equal at base and `origin/main`); - the `create-objectstack` changeset is deleted. ### This round: the setup aligns the pair it boots This follows the seat's verification-strategy ruling in 6099406666. After scaffolding, and before the fixture files are written, `alignProtocolRange()` rewrites the scaffold's `engines.protocol` to `'^' + PROTOCOL_MAJOR`. `PROTOCOL_MAJOR` is read from `@objectstack/spec/kernel`, the workspace spec this file boots, the same import other CLI e2e files use. - **Only the major's digits are rewritten.** When the majors agree, the scaffold is left byte-for-byte as written, and the helper does not write the file at all. - Measured on the template: at `'^17'` (the window) the hash moves `684e38ed0d1a` → `5d6e6468255e`, and only the `engines` line differs. - At `'^18'` (majors agree) the hash stays `5d6e6468255e` → `5d6e6468255e`, byte-identical. - **A missing stamp throws in `beforeAll`.** A template that stops declaring the key fails this file loudly instead of skipping the alignment. - **Why a direct rewrite of the one key, and not `objectstack migrate meta --from 17 --write`.** The `--write` command is the step the refusal prescribes, and it was measured first. On a scaffold under `packages/cli/node_modules/` it exited 0 with `not written [outside-project]` and left the config unchanged. The codemod refuses any file whose real path has a `node_modules` segment (`authored-source-codemod.ts`, the `outside-project` refusal). This project has to live under this package's `node_modules` so that its imports resolve to workspace copies. - **What stays the same:** the noise budget, all three assertions, and the ticket object and actions. Once the version pass lands, the window closes and the alignment becomes a no-op. ## Ablations Both ran through `scripts/ablation-replace.mjs` in wrap mode on committed trees, under the verify lock. Each restore was proven by blob equal to HEAD and an empty `git diff HEAD`. Neither subject resolves through `dist/`: the FAMILY and the setup live in the test files. | Mutation | Anchor | Result | |---|---|---| | `alignProtocolRange(join(dir, 'objectstack.config.ts'));` deleted from the setup | x1 → x0, blob `058fbecec7dc` → `bfedb0522aa5` | noise-budget red with the original refusal: `✗ package 'com.example.support-desk' targets protocol ^17 … this runtime is protocol 18.0.0 …`, `expected 0 to be greater than 0`, 1 failed and 2 passed. After the restore to blob `058fbecec7dc`: 3 passed. | | `'migrate security-catalog-overlays': [],` deleted from `FAMILY` (round 1, same blob as now) | x1 → x0, blob `698034ba9bcb` → `5dfe8780897a` | roster pin red: `expected [ 'meta resync', …(17) ] to deeply equal [ 'meta resync', …(16) ]`, `+ "migrate security-catalog-overlays"` | ## Tests (on `3d63ed7db5`) - **Nightly tier** (`OS_TEST_TIERS=nightly`, `--project integration`, verify lock): the two card files, 59 passed. - **`create-objectstack`:** suite 17 files and 254 tests passed. `pnpm check:template-version-sync` is green with 40 assertions, the same count as on `main`. Both are unchanged, because this diff does not touch the package or the script. - **`@objectstack/cli`:** - unit layer: 279 files and 4126 tests passed; - `typecheck` (`tsc --noEmit` plus `check:test-typecheck`) is OK, and `tsconfig.test.json`'s `--listFiles` includes both edited files. - **Changeset:** `node scripts/check-empty-changeset.mjs --base origin/main` exits 0. It reports no empty-frontmatter changeset added and no merge-base changeset modified or deleted. - **Gates:** `dispatch-gates --commands` (no paths) derived 50 commands from the 2-file change set. `--ran` reports `50 derived, 50 run, 0 NOT-MEASURED, 0 UNRUN`, a derived zero: every command recorded `exit 0`. `check:type-check-debt` and `check:dual-build-cjs-loads` ran last, after every locked suite. - `dispatch-gates` noted that the tree is behind `origin/main`: 6 derived-from files changed there (`ci.yml`, `check-shard-attestation.mjs` and the fleet-write relay scripts). - No conflict exists, so per the round's direction there was no merge. CI judges the merge ref. - **Lint, proven narrowing:** 1. Population is read from ESLint's own config: both changed files return results with no "File ignored". 2. File count is from `--format json`: 2 results, 0 errors, 0 warnings. 3. Invariance: `eslint.config.mjs` enables no type-aware linting, and this diff touches neither the config nor a baseline it reads. ## Changeset Nothing publishes: `@objectstack/cli` ships `dist`, `README.md` and `CHANGELOG.md`, and this diff is two `test/` files. By the repo's rule this PR takes the `skip-changeset` label. An empty-frontmatter changeset would be the risky path that `check:empty-changeset` refuses. The seat applies the label. ## Acceptance notes - `objectstack migrate meta --write` on a project whose path has a `node_modules` segment says `the literal is in …/objectstack.config.ts, outside the project at …`, although the file is inside the project. The real cause is the `node_modules` segment rule. Only a test layout reaches it, so this is a wording polish, not a finding. No carrier. - Version Packages PR #21988 was last refreshed on 2026-10-08T06:19Z, before pre mode was entered (`a87d8be29`). This is release-lane state, recorded as a reading, ⛔ not acted on. --- _Generated by [Claude Code](https://claude.ai/code/session_01BmsuLyUeuG5CNpZFMH1jzS)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
Fixes #22130
Clause-②: yes (narrowing: the handshake refuses a manifest pinned to protocol 17 (
'^17','^17.x.y','17.x'), while the exportedPROTOCOL_VERSIONmoves to'18.0.0'and a'^18'manifest is admitted)Executes #22085 Q1 → B (ruling record
6049734955, the maintainer's 「22082 22085 同意」): the protocol major moves in an ordinary pull request that runs the full CI, not in the version pass.PROTOCOL_VERSIONreads'18.0.0'.spec-changes.json, and the 17 → 18 section of the protocol upgrade guide.^18.6056808625(letter E, maintainer 「同意」):scripts/check-changeset-no-major.mjsjudges the evidence: Changesets pre mode AND a pendingmajorfor@objectstack/spec.Status.
os-zhuangapproved this PR onc96d8e9446(an authorized APPROVED review, Tier H); the owning seat lands it after the re-sync rounds below and a delta at-tier contract review. PR #22084 (pre mode plus themajormarker) merged asa87d8be299and is merged into this branch, so the pre-mode condition is live.Size: 8,611 changed lines against the merge base
e148ca9842(+7,854 / −757, 52 files). 7,819 of them are the two generated documents. That is over the human-merge line (3,000 since #22490; AGENTS.md §7 class (c)), so this PR lands like a Tier H surface: an authorized approval and then the owning seat, or a human merge.First measurements (answered before the edit, on
ef1fcb26a2)M1 — which
packages/clifixtures declare^17on purpose. Onlypackages/cli/src/utils/protocol-version-gap.test.ts:68.'17.2.0', never againstPROTOCOL_VERSION.^17there is the "the range covers the install" control.packages/cli/CHANGELOG.mdis release-owned history.^18.M2 — may #5082's 17 → 18 conversions (PR #22103) take effect before ADR-0131 C8? The bump does not change when any conversion applies, and no step-18 entry assumes C8. No fork.
applyConversionsand the stored-row seam walk every conversion with noPROTOCOL_MAJORfilter.os migrate metaalready ends at 18:CHAIN_TERMINUS_MAJOR = Math.max(PROTOCOL_MAJOR, ...MIGRATION_MAJORS).declared-index-unique-scoperewrites baretrueto'global', which is the same physical index.'organization'is NULL-safe throughCOALESCE(ADR-0120 D3).organization_idis NOT NULL.What changes
packages/spec/src/kernel/protocol-version.ts:PROTOCOL_VERSION = '18.0.0'; its docblock names the exception.packages/spec/spec-changes.json,docs/protocol-upgrade-guide.md: regenerated bygen:spec-changesandgen:upgrade-guideafter each merge ofmain, never by hand.PROTOCOL_VERSIONfrom source plus the two registries. Neither reads the pre-mode state.mainsinceb460153912added five semantic entries,sys-view-definition-retired(feat(metadata-core,metadata-protocol,metadata,platform-objects,spec)!: sys_view_definition retires as inert (ADR-0131 D13, C5 stage S1) #22374),try-catch-and-retry-policy-undeclared-keys-refused(fix(spec)!: close the shared retry policy, and judge try_catch config keys at the build doors #22380),flow-text-slot-single-brace-refused(feat(spec)!: flow text slots read the {{ }} delimiter, refusing a single-brace token with its hole spelling (#22110) #22315),manage-org-presentation-retiredandmeta-doors-organization-scope-retired(feat(metadata-core,rest,runtime,plugin-email,spec)!: the /meta doors carry no organization; organization-admin metadata authoring closes (ADR-0131 D6, #15206 S3) #22447), rewordedflow-builtin-node-config-undeclared-keys-refused(fix(spec)!: close the shared retry policy, and judge try_catch config keys at the build doors #22380) and six rationales (chore(objectui): bump the console pin to f0268ad78485 (carries objectui#11880) #22412), and added no conversion.^18. Two runtime fixtures read>=17.1.0(see Judgments).mainadded no^17handshake between959c209d56andd1dbe70ebd: per-file counts are equal.d1dbe70ebdand6729e107e8it added one:packages/cli/test/package-union-readers.test.ts:86, a current-app fixture (fix(cli): read a multi-package artifact's package bodies in the capability preflight and the translation readers #22285), which now reads^18.6729e107e8and41d0d4038cit added none.git diff 6729e107e8 41d0d4038c | grep -E "protocol.*'\^17"matches nothing; the same grep over this PR's own diff matches 60 lines. Per-file counts of'^17…'literals are equal across the merge.b460153912ande02833c240it added two current-app fixtures, which now read^18:packages/qa/dogfood/test/security-catalog-cold-boot-environment-holder.dogfood.test.ts(feat(objectql)!: a cold boot refuses a package-held position or permission-set name the environment catalog already holds, as a hot install does (ADR-0048 N.3) #22365) andpackages/cli/test/retry-policy-key-validate-door.test.ts(fix(spec)!: close the shared retry policy, and judge try_catch config keys at the build doors #22380). Later, feat(core,cli,verify): bootStack composes what serve composes — item 1 stage 2 of #22301 (HELD at stop conditions) #22381 added a third,packages/cli/test/verify-host-root.test.ts:56, which now reads^18(round 12).specVersionprescription:packages/spec/src/kernel/manifest.zod.ts'sguidance.specVersionspells the range it prescribes fromPROTOCOL_MAJOR(imported from./protocol-version, which imports nothing), so it printsengines: { protocol: '^18' }at this protocol and follows the next major.manifest-unknown-keys.test.tspins the current-major range and refuses the previous one (ablation: restoring the'^17'literal turns it red).packages/spec/src/kernel/protocol-version.test.ts(local;release.ymlruns it by that project):check-changeset-no-major.mjs.scripts/check-changeset-no-major.mjsgains a third question, the protocol lockstep evidence:protocol-version.ts, not from a build. The regex is the declarationsync-protocol-version.mjsrewrites.@objectstack/spec's stable version, the gate requires.changeset/pre.jsonmode: 'pre'AND a pending top-level.changeset/*.mddeclaring"@objectstack/spec": major.::error::annotation that names the remedy: set the constant back to the package major, orchangeset pre enterplus a specmajor.scripts/check-future-spec-major.mjs: the generated copies carry the witnessed measurement entries.QUOTATION_EXEMPTIONSgains twokind: 'measurement'entries, modelled on theregistry.tsone and witnessed by the same regex:packages/spec/spec-changes.json(covers: 2) anddocs/protocol-upgrade-guide.md(covers: 1), major 4997.@objectstack/spec4997 on the same corpus") into the 17 → 18 records: the per-major and aggregate records inspec-changes.json, and the guide section.packages/cli/test/migrate-meta-default-range.test.ts(theos migrate meta --from 17— the invocation the spec 17 tombstones prescribe — reportsNothing to migratefor the conversions it is meant to list, because--todefaults to the current major and the conversions aretoMajor: 18#17134 pins), re-premised for protocol 18:--from N. N is one below thetoMajorof the step that carries the rename conversion, read off the registry (17).--from PROTOCOL_MAJOR. At protocol 18 that range lies above the rename, so four cases read an empty chain (Test Core (1/6)ondd4ea171fb).--todefault,Math.max(PROTOCOL_MAJOR, ...MIGRATION_MAJORS), differs from the old one,PROTOCOL_MAJOR, only while the registry carries a step past the runtime major.MIGRATION_MAJORSis[17, 18], and no conversion registerstoMajor: 19on this branch or onmainb460153912. Both defaults are 18, so no run of the installed CLI tells them apart.TERMINUS > PROTOCOL_MAJOR) therefore cannot hold in the spawned file. It moved, word for word, tomigrate-meta-default-range-window.test.ts(unit)..changeset/22130-protocol-18.md: nowmajor, because pre mode is onmain. ItsClause-②line declares the armnarrowing; the second line of this body is that line, byte for byte. It keeps the BREAKING banner andnot-required (already-registered …)naming the 64 step-18 conversions.check:adr-0087-registrationexits 0.^17before^17after^18aftercontent/docsexamples/app-crmexamples/app-multi-packageexamples/app-showcaseexamples/app-todopackages/clipackages/lintpackages/mcppackages/metadatapackages/qaWhy the evidence lives in
check-changeset-no-major.mjs(H3 falsified; ruling E)H3 put the exception inside
protocol-version.test.ts, reading.changeset/pre.json. Measured, that cannot stand:.changeset/must move to therepoproject.check:cross-package-test-inputsexits 1 otherwise.release.ymlruns this file by--project local. A file in therepolist then answersNo test files found, exiting with code 1..changeset/**as a CI scheduling surface, and that reddened dispatch-gates'extractWatchHintsreads a noise-floor EXCLUSION constant as a watch hint:.changesetderivescheck:pm-half-statesonto every changeset-adding card #15753 invariant: a changeset path alone must reach no value-bearing family.The maintainer ruled E. R's repo-project test and its six declaration edits are reverted. Each of these files now equals
mainatb460153912, measured:vitest.repo-tests.jsoncross-package-test-inputs.mjscheck-cross-package-test-inputs.mjsturbo.jsonci.ymlcheck-ci-filter-parity.mjsprotocol-version-pre-mode.test.tsscripts/pm/dispatch-gates.mjs,release.yml,cut-rc.ymlandsync-protocol-version.mjsare untouched.The gate's verdict, measured three ways:
@objectstack/spec@17.7.0, pre mode, the #22084 marker pending)✓ Protocol lockstep: … the one exception, evidenced: pre mode (tag next) and a pending major for @objectstack/spec (.changeset/22080-v18-line-opens.md)main3513ac7781with this gate copied in (an ordinary PR, no shape)✓ Protocol lockstep: PROTOCOL_VERSION major 17, @objectstack/spec@17.7.0 — not one major ahead of a stable version, so there is no pre-mode exception to judge.⛔ … protocol lockstep refused … no pending changeset declares "@objectstack/spec": major. Either put the constant back in lockstep — PROTOCOL_VERSION = '17.0.0' … or open the next major line first …Residuals, stated:
skip-changesetandallow-majorlabels.Judgments: every handshake left at
^17, with its reasonpackages/cli/src/utils/protocol-version-gap.test.ts:68'17.2.0'(M1).packages/cli/CHANGELOG.md:7788content/docs/releases/v17/17-5.mdx,17-6.mdx,17-7.mdxpackages/create-objectstack/src/templates/blank/objectstack.config.ts:39@objectstack/*ranges (replay:engines.protocol → '^18'beside^18.0.0). Moving it now would scaffold^18over^17.0.0dependencies.template-consistency.test.tsholds it equal to the scaffolder major.packages/metadata/src/__fixtures__/forward-probe-17.4-built.artifact.json:11,hotcrm-17.1-built-bare-root-predicates.artifact.json:12,hotcrm-17.1-built-permissions.artifact.json:12packages/metadata/src/plugin-artifact-forward-conversion-retired-after.test.ts:194,plugin-artifact-forward-conversion.test.ts:221packages/metadata-core/src/artifact-forward-conversion.test.ts:228,form-predicate-root-policy.test.ts:26,50packages/runtime/src/app-plugin-artifact-forward-conversion.test.ts:81packages/spec/src/kernel/manifest-unknown-keys.test.ts:53,278,282,manifest-permissions-string-list.test.ts:53ManifestSchemashape samples; the schema never judges the major.skills/objectstack-platform/SKILL.md:143,skills/objectstack-platform/evals/config-plugins-ops.json:7,skills/objectstack-upgrade/SKILL.md:91.github/workflows/scaffold-e2e.yml:106Moved to
>=17.1.0:packages/runtime/src/standalone-stack-security-registrar.test.ts:49standalone-stack-seeder-declaration-copy.test.ts:117Both boot through the load seam, which refused
^17.1.0at protocol 18. Their subject is the forward-conversion window, and that window opens on the floor.>=17.1.0keeps the floor and admits 18.Re-premised for protocol 18. This is not a handshake: it is a
--fromthat meant the authoring major.packages/cli/test/migrate-meta-default-range.test.ts(#17134)--from String(PROTOCOL_MAJOR), with the anti-vacuity lineTERMINUS > PROTOCOL_MAJOR--fromthe rename step'stoMajor − 1. The anti-vacuity line moved, word for word, tomigrate-meta-default-range-window.test.ts, which holds the runtime major at that authoring major in-process.Replay of the root
versionscript (throwaway clone ofbdee4d2da1)A clone of the merged branch.
pre.jsonand themajormarker come frommain, so no overlay was needed.release-rehearsal-clone.mjs --prepareanswered FIT. Thenpnpm install --frozen-lockfileandpnpm run version:release.yml's shape filter (SURFACES,stampedPaths(),syncedPaths(), the fixed pattern):UNEXPECTEDempty.protocol-version.tsnot moved, soUNVALIDATEDis empty.Content gates on that tree:
vitest run --project local src/kernel/protocol-version.test.tscheck:spec-changescheck:upgrade-guidecheck:template-manifestspnpm --filter create-objectstack testcheck-changeset-no-major.mjsnot one major ahead of a stable version(18 vs18.0.0-next.0)Control leg (round 1), the same overlay on
ef1fcb26a2:✓ PROTOCOL_VERSION 17.0.0 → 18.0.0, andprotocol-version.tsis in the moved set. That is the UNVALIDATED refusal this PR removes.Verification
Round 12, at
6ec484243e(report on #22130). The merge queue ejected4ba5952ea1in a group behind PR #22381, whose new fixtureverify-host-root.test.ts:56declared^17(triage6081302430).main8b713fad78merged (3b97df5a7f, nothing of its own); the fixture moved to^18in its own commit (5159d64866), red before it and green after it.maine148ca9842merged (462a24045b); #22447 had added two step-18 semantic entries, so both documents were regenerated in a separate commit (4d1a408a75: +28 and +6 lines), and the changeset's count tokens moved 327 → 329 in their own commit (6ec484243e). 138 derived gates run, 0 not measured. CI on6ec484243e: 35 runs, 33 success, 2 expected skips.Round 11, at
4ba5952ea1(report6080275052on #22130). The merge queue ejected5ef8ab6ac5when PR #22315 landed first with a new step-18 semantic entry.main3ca71b6e05merged (a46840ee68, no path of its own pergit diff-tree --cc);spec-changes.json(+14) and the upgrade guide (+4/−1) regenerated in a separate commit (b8a3e2a45c; 64 conversions, 327 semantic entries, aggregate 404 = the registry's 404); the changeset's two count tokens 326 → 327 in their own commit (4ba5952ea1). At-tier delta contract review PASS6079895139. CI on4ba5952ea1: 35 runs, 33 success, 2 expected skips.Round 10, at
5ef8ab6ac5(report6077854211on #22130; seat ACCEPT6078284011). A pure regeneration:mainbf492c8548(#22412) merged (b50d5c3866), thenspec-changes.json(+12/−12) and the upgrade guide (+6/−6) regenerated in a separate commit; every changed line is arationaleof the six step-18 entries #22412 reworded, and the counts stay 64 / 326. The at-tier PASS6076766071is carried by theRegen-provenance:line in PR comment6077906555. CI on5ef8ab6ac5: 35 runs, 33 success, 2 expected skips.Patch round 1, at
eb23db8a82(report6076558925on #22130).maine02833c240merged withos-regen-merge.sh(c80423bc16, committed before any generator ran); no step-18 entry arrived,check:spec-changesandcheck:upgrade-guideexited 0 on the merged tree, and both generators rewrite byte-identical (64 / 326). ThespecVersionprescription fix above.spectest627 files / 18,757 passed andtest:repo54 files / 915;cliprotocol-version-gap.test.ts16 passed; the dogfood cold-boot file 4 passed. The 32 steps ofType Check · source gatesexit 0,check:future-spec-majorincluded;dispatch-gates: 138 derived, 138 run. CI oneb23db8a82: 35 runs, 33 success, 2 expected skips.Round 9, at
4ff2827472(report6075705847; seat review6075740377). The re-sync the handover named:maine75dceddd7merged (e04231ab07),spec-changes.jsonand the upgrade guide regenerated after the merge commit (062e3545c8, 324 → 326 semantic entries), the changeset count updated, the two current-app fixturesmainadded moved to^18, and PR #22416's one-file fix ported verbatim (02d07755d7;mainnow carries the identical blob, so it no longer shows in the diff). The merge-queue build ofc96d8e9446had failedcheck:spec-changesfor exactly this staleness.Round 8, at
c96d8e9446. A merge ofmainb460153912only, withos-regen-merge.sh(merge commitc96d8e9446).maintouched no migration, conversion or kernel file and added no^17handshake since41d0d4038c.gen:spec-changesandgen:upgrade-guideboth rewrote their files byte for byte:git diff HEADis empty.check:spec-changes,check:upgrade-guide, speccheck:generated(15 artifacts),check:future-spec-majorandcheck:adr-0087-registration --base origin/mainexit 0.check-changeset-no-major --base origin/main --eventexits 0 against this body.Test Coretimeouts onc78f55e352are not this PR's. Both now exit 0:@objectstack/lintsrc/build-access-matrix.test.ts(fixed onmainby test(lint): index the access-matrix rows once in the exhaustive fold case #22358): 8 tests, 3.5s.@objectstack/specsrc/integration/connector-author-shape.test.ts(local): 11 tests, 16.4s. One case, "compiles every full connector example verbatim", takes 11.5s against a 60stestTimeout.Round 7, at
c78f55e352.main41d0d4038cwas merged withos-regen-merge.sh(merge commitecde207250, no regeneration in MERGE state). Thengen:spec-changesandgen:upgrade-guidewere run (fed6cd402f), and the changeset now counts 324 semantic entries (c78f55e352).lint.yml'sType Check · source gatesjob exits 0, run in order: 32 run steps. They includecheck:spec-changes,check:upgrade-guide,check:authorable-surfaceandcheck:future-spec-major(8 witnessed ledger entries; the new generated copies cite no@objectstack/specmajor).check:generated(all 15 artifacts) exits 0.check:adr-0087-registration --base origin/main,check:empty-changeset,check:changeset-gate-self-testsandcheck:nul-bytesexit 0.check-changeset-no-major --base origin/main --eventexits 0 against this body.dispatch-gates --commandsderives the same 138 families; the merge adds none.testtask of every touched package, each exit 0:@objectstack/spec:test626 files, 18,740 tests passed, 1 todo;test:repo54 files, 914 tests.@objectstack/clitest(both projects): 366 files (269unit+ 97integration), 4,877 tests passed, 2 skipped.typecheckexits 0.@objectstack/runtime:test340 files, 4,776 tests passed, 19 skipped;test:repo3 files, 751 tests.@objectstack/dogfood: 226 files passed, 1 skipped; 1,770 tests passed, 9 skipped.@objectstack/lint127 files, 5,843 tests.@objectstack/mcp35 files, 390 tests.@objectstack/metadata58 files, 870 tests.example-crm5 files, 45 tests.example-showcase33 files, 408 tests.example-todo7 files, 238 tests.Round 6, at
99b16b2999.Test Core (1/6)was red ondd4ea171fbin@objectstack/cli'sintegrationproject:migrate-meta-default-range.test.ts, 4 tests. My earlier rounds ran--project unitonly. Now:pnpm --filter @objectstack/cli test(both projects) passes. 361 files (267unit+ 94integration): 4,839 tests passed, 2 skipped, exit 0.pnpm --filter @objectstack/cli typecheckexits 0. It includescheck:test-typecheckovertest/.testtask of every other touched package with more than one vitest project, each exit 0:@objectstack/dogfood: 221 files passed, 1 skipped; 1,753 tests passed, 9 skipped.@objectstack/runtime:test336 files, 4,749 tests passed, 19 skipped;test:repo3 files, 751 tests.@objectstack/spec:test625 files, 18,711 tests passed, 1 todo;test:repo54 files, 914 tests.lint,mcp,metadata,example-crm,example-showcaseandexample-todohave one project each.example-multi-packagehas notestscript.scripts/ablation-replace.mjs. Each mutation was proven on disk, and each restore was proven by blob =HEADand an emptygit diff HEAD. Both subjects resolve tosrc/, so no build leg applies.--todefault reverted toPROTOCOL_MAJOR, exactly the window file's three default pins go red:expected 17 to be 18,expected [] to deeply equal [ …(5) ], and a missingApplied 5 mechanical change(s). The spawned file stays green under the same mutation, which measures that it cannot tell the defaults apart at protocol 18.the registry carries a step past the runtime major: expected 18 to be greater than 18.Round 5, at
dd4ea171fb. Every step oflint.yml'sType Check · source gatesjob exits 0, run in the job's order: 32 run steps, frompnpm install --frozen-lockfiletocheck:llms-txt.check:future-spec-majorwas red on three sites:docs/protocol-upgrade-guide.md:1039andpackages/spec/spec-changes.json:2340,:5880.readClause2Linereads the changeset as{ value: yes, arm: narrowing }.check-changeset-no-major --base origin/main --event,check:adr-0087-registration --base origin/main,check:empty-changesetandcheck:changeset-gate-self-tests.All runs below happened after merging
main(3513ac7781, thend1dbe70ebd), on a shared box, through the verify lock.After the third merge (
6729e107e8, which added one step-18 semantic entry), at23e9e302d9, these all exit 0:check:spec-changes,check:upgrade-guideand speccheck:generated;check-adr-0087-registration --base origin/main;check-changeset-no-major --base origin/main --event, against this body;localtests undersrc/kernel src/migrations src/conversions(70 files, 1,853 tests);package-union-readers.test.ts(7/7).dispatch-gates --commandsderives the same 137 families as atbdee4d2da1; the merge adds none.Builds. The turbo closure of
dogfood,cli,cloud-connection, the examples,create-objectstackandmcp: 65/65 tasks. Thenlint/cli(59/59) and the seven dist-only packages (43/43).check:generated: all 15 artifacts up to date.Tests
@objectstack/speclocal, full@objectstack/specrepo, fullbuild-schemas-check-mode.test.tsalone 88/88 in 909s)@objectstack/specsrc/kernel src/migrations src/conversions@objectstack/spectypecheck@objectstack/runtimelocal, full@objectstack/runtimerepo@objectstack/metadata-protocol@objectstack/metadata@objectstack/metadata-core@objectstack/cloud-connection@objectstack/lintcreate-objectstack@objectstack/cli--project unit@objectstack/cli.e2efiles@objectstack/dogfoodexample-crmexample-todoexample-showcasecheck-changeset-no-major.mjs --self-testGates at
bdee4d2da1:dispatch-gates --commandsderives (137) together with the claim-time list (146 commands in all).dispatch-gates --ran: "137 derived, 137 run, 0 NOT-MEASURED, 0 UNRUN". All exit 0.pnpm check:pm-dispatch-gates: 1,976 of 1,976 cases pass, theextractWatchHintsreads a noise-floor EXCLUSION constant as a watch hint:.changesetderivescheck:pm-half-statesonto every changeset-adding card #15753 case included.check:skill-examplesandcheck:dual-build-cjs-loadsfirst answered PREREQUISITE NOT MET.check:docs-transcript-driftfirst read a stalelintdist (49 against the docs' 50).Acceptance notes
latest. The getting-started, CLI and upgrading pages show^18, as the ruling directs for docs snippets. npmlateststays 17.x until 18 GA, and a 17.x runtime refuses^18. Noted for the v18 release page (release(v18): the version-pr lane refuses the major boundary atValidate the post-version tree, so #21988 never refreshes into 18.0.0-next.0 — wire the lane's major-only gates in, and stop prerelease cuts re-dating the last GA #22085 Q2).os migrate metaand the declared range. Measured on this branch, which carries the cli(migrate meta):os migrate meta --from 17 --writeleavesengines.protocol: '^17', so the load refusal that names this command as its resolution still refuses the migrated manifest #22219 fix9f0de32a03, with a probe manifest declaringengines: { protocol: '^17' }and one bareunique: truedeclared index.--from 17) names it:⚠ manifest.engines.protocol '^17' does not admit protocol 18, so the load refuses this stack even after migrating; --write rewrites it to '^18'.--writerewrote both edits:Rewrote the declared protocol range, so the load admits protocol 18: objectstack.config.ts:2 manifest.engines.protocol: '^17' → '^18', beside thedeclared-index-unique-scopeedit.next.0version pass,main's CLI advisory judges ranges against the installed spec package (17.7.0), while the handshake judges againstPROTOCOL_VERSION(18). A published18.0.0-next.Nhas both on 18.Generated by Claude Code