Skip to content

Commit 4e9fe9f

Browse files
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)
Fixes #22130 Clause-②: yes (narrowing: the handshake refuses a manifest pinned to protocol 17 (`'^17'`, `'^17.x.y'`, `'17.x'`), while the exported `PROTOCOL_VERSION` moves 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_VERSION` reads `'18.0.0'`. - The two generated documents are regenerated by their own tooling: `spec-changes.json`, and the 17 → 18 section of the protocol upgrade guide. - Every in-repo handshake that means "the current protocol" reads `^18`. - The lockstep gets one narrow exception, routed by ruling record `6056808625` (letter E, maintainer 「同意」): - the local lockstep test recognises only the exception's shape; - `scripts/check-changeset-no-major.mjs` judges the evidence: Changesets pre mode AND a pending `major` for `@objectstack/spec`. **Status.** `os-zhuang` approved this PR on `c96d8e9446` (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 the `major` marker) merged as `a87d8be299` and 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/cli` fixtures declare `^17` on purpose.** Only `packages/cli/src/utils/protocol-version-gap.test.ts:68`. - That file's advisory judges a range against an explicit installed `'17.2.0'`, never against `PROTOCOL_VERSION`. `^17` there is the "the range covers the install" control. - `packages/cli/CHANGELOG.md` is release-owned history. - The other 19 files (33 sites) are manifest fixtures standing for a valid current app, and none of them asserts on the range. They read `^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. - `applyConversions` and the stored-row seam walk every conversion with no `PROTOCOL_MAJOR` filter. - The artifact door keys its conversion window on the declared floor against the installed spec package version. - `os migrate meta` already ends at 18: `CHAIN_TERMINUS_MAJOR = Math.max(PROTOCOL_MAJOR, ...MIGRATION_MAJORS)`. - `declared-index-unique-scope` rewrites bare `true` to `'global'`, which is the same physical index. `'organization'` is NULL-safe through `COALESCE` (ADR-0120 D3). - The step-18 entries that cite ADR-0131 cite D7 or D8 and describe the pre-C8 shape (rows with no organization are still answered). None assumes `organization_id` is 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 by `gen:spec-changes` and `gen:upgrade-guide` after each merge of `main`, never by hand. - Both generators read `PROTOCOL_VERSION` from source plus the two registries. Neither reads the pre-mode state. - The 17 → 18 record carries 64 conversions and 329 semantic entries (406 in the 16 → 18 aggregate). The merges of `main` since `b460153912` added five semantic entries, `sys-view-definition-retired` (#22374), `try-catch-and-retry-policy-undeclared-keys-refused` (#22380), `flow-text-slot-single-brace-refused` (#22315), `manage-org-presentation-retired` and `meta-doors-organization-scope-retired` (#22447), reworded `flow-builtin-node-config-undeclared-keys-refused` (#22380) and six rationales (#22412), and added no conversion. - **Handshakes:** 54 sites in 38 files read `^18`. Two runtime fixtures read `>=17.1.0` (see Judgments). - `main` added no `^17` handshake between `959c209d56` and `d1dbe70ebd`: per-file counts are equal. - Between `d1dbe70ebd` and `6729e107e8` it added one: `packages/cli/test/package-union-readers.test.ts:86`, a current-app fixture (#22285), which now reads `^18`. - Between `6729e107e8` and `41d0d4038c` it added none. `git diff 6729e10 41d0d40 | 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. - Between `b460153912` and `e02833c240` it added two current-app fixtures, which now read `^18`: `packages/qa/dogfood/test/security-catalog-cold-boot-environment-holder.dogfood.test.ts` (#22365) and `packages/cli/test/retry-policy-key-validate-door.test.ts` (#22380). Later, #22381 added a third, `packages/cli/test/verify-host-root.test.ts:56`, which now reads `^18` (round 12). - **The `specVersion` prescription:** `packages/spec/src/kernel/manifest.zod.ts`'s `guidance.specVersion` spells the range it prescribes from `PROTOCOL_MAJOR` (imported from `./protocol-version`, which imports nothing), so it prints `engines: { protocol: '^18' }` at this protocol and follows the next major. `manifest-unknown-keys.test.ts` pins 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.yml` runs it by that project): - The lockstep is unchanged when the majors are equal. - It recognises the exception's shape — a stable package version exactly one major behind — and hands the judgement to `check-changeset-no-major.mjs`. - Any other drift is red. It reads nothing outside the package. - **`scripts/check-changeset-no-major.mjs`** gains a third question, the protocol lockstep evidence: - It reads the protocol major from the source text of `protocol-version.ts`, not from a build. The regex is the declaration `sync-protocol-version.mjs` rewrites. - When that major is one ahead of `@objectstack/spec`'s stable version, the gate requires `.changeset/pre.json` `mode: 'pre'` AND a pending top-level `.changeset/*.md` declaring `"@objectstack/spec": major`. - Without that evidence it refuses with one `::error::` annotation that names the remedy: set the constant back to the package major, or `changeset pre enter` plus a spec `major`. - Unreadable inputs are a failure, never a pass. - Its new self-test battery (39 cases, floor 19 → 20) covers both directions. - **`scripts/check-future-spec-major.mjs`:** the generated copies carry the witnessed measurement entries. `QUOTATION_EXEMPTIONS` gains two `kind: 'measurement'` entries, modelled on the `registry.ts` one and witnessed by the same regex: `packages/spec/spec-changes.json` (`covers: 2`) and `docs/protocol-upgrade-guide.md` (`covers: 1`), major 4997. - From protocol 18 on, the two generators project that entry's evidence sentence ("controls objectstack 12966 and `@objectstack/spec` 4997 on the same corpus") into the 17 → 18 records: the per-major and aggregate records in `spec-changes.json`, and the guide section. - The matcher, the path scope and the recorded rationale are unchanged. - **`packages/cli/test/migrate-meta-default-range.test.ts`** (the #17134 pins), re-premised for protocol 18: - The spawned cases now run the tombstone's own `--from N`. N is one below the `toMajor` of the step that carries the rename conversion, read off the registry (17). - They used to run `--from PROTOCOL_MAJOR`. At protocol 18 that range lies above the rename, so four cases read an empty chain (`Test Core (1/6)` on `dd4ea171fb`). - The new `--to` default, `Math.max(PROTOCOL_MAJOR, ...MIGRATION_MAJORS)`, differs from the old one, `PROTOCOL_MAJOR`, only while the registry carries a step past the runtime major. - Measured: `MIGRATION_MAJORS` is `[17, 18]`, and no conversion registers `toMajor: 19` on this branch or on `main` `b460153912`. Both defaults are 18, so no run of the installed CLI tells them apart. - The anti-vacuity line (`TERMINUS > PROTOCOL_MAJOR`) therefore cannot hold in the spawned file. It moved, word for word, to **`migrate-meta-default-range-window.test.ts`** (`unit`). - That file runs the real command in-process. Only the kernel's two protocol constants are replaced, by the authoring major: the window the card was measured in. There the line tells the two defaults apart in every release. - **`.changeset/22130-protocol-18.md`:** now `major`, because pre mode is on `main`. Its `Clause-②` line declares the arm `narrowing`; the second line of this body is that line, byte for byte. It keeps the BREAKING banner and `not-required (already-registered …)` naming the 64 step-18 conversions. `check:adr-0087-registration` exits 0. | Directory | Files | `^17` before | `^17` after | `^18` after | |---|---|---|---|---| | `content/docs` | 3 | 3 | 0 | 3 | | `examples/app-crm` | 1 | 1 | 0 | 1 | | `examples/app-multi-package` | 2 | 2 | 0 | 2 | | `examples/app-showcase` | 1 | 1 | 0 | 1 | | `examples/app-todo` | 1 | 1 | 0 | 1 | | `packages/cli` | 20 | 34 | 0 | 34 | | `packages/lint` | 2 | 2 | 0 | 2 | | `packages/mcp` | 1 | 2 | 0 | 2 | | `packages/metadata` | 1 | 2 | 0 | 2 | | `packages/qa` | 4 | 4 | 0 | 4 | | **Total** | **36** | **52** | **0** | **52** | ## Why 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: - A spec test reading `.changeset/` must move to the `repo` project. `check:cross-package-test-inputs` exits 1 otherwise. - `release.yml` runs this file by `--project local`. A file in the `repo` list then answers `No test files found, exiting with code 1`. - The first draft of this PR (route R) used a repo-project test. It had to declare `.changeset/**` as a CI scheduling surface, and that reddened dispatch-gates' #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 `main` at `b460153912`, measured: - `vitest.repo-tests.json` - `cross-package-test-inputs.mjs` - `check-cross-package-test-inputs.mjs` - `turbo.json` - `ci.yml` - `check-ci-filter-parity.mjs` - `protocol-version-pre-mode.test.ts` `scripts/pm/dispatch-gates.mjs`, `release.yml`, `cut-rc.yml` and `sync-protocol-version.mjs` are untouched. The gate's verdict, measured three ways: | Tree | Verdict | Exit | |---|---|---| | This branch (protocol 18, `@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)` | 0 | | `main` `3513ac7781` with 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.` | 0 | | The same tree with the constant at 18 and the marker removed | `⛔ … 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 …` | 1 | **Residuals, stated:** - The Check Changeset step is skipped under the `skip-changeset` and `allow-major` labels. - Check Changeset is not one of the seven required contexts AGENTS.md §7 names. ## Judgments: every handshake left at `^17`, with its reason | Site | Reason | |---|---| | `packages/cli/src/utils/protocol-version-gap.test.ts:68` | Paired with an explicit installed `'17.2.0'` (M1). | | `packages/cli/CHANGELOG.md:7788` | Release-owned history. | | `content/docs/releases/v17/17-5.mdx`, `17-6.mdx`, `17-7.mdx` | Release-owned; each describes a 17.x release. | | `packages/create-objectstack/src/templates/blank/objectstack.config.ts:39` | The version pass stamps it together with the template's `@objectstack/*` ranges (replay: `engines.protocol → '^18'` beside `^18.0.0`). Moving it now would scaffold `^18` over `^17.0.0` dependencies. `template-consistency.test.ts` holds 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:12` | Frozen artifacts built by 17.x. Their floor is the subject, and the metadata plugin door runs no handshake. | | `packages/metadata/src/plugin-artifact-forward-conversion-retired-after.test.ts:194`, `plugin-artifact-forward-conversion.test.ts:221` | The artifact's age is the subject. | | `packages/metadata-core/src/artifact-forward-conversion.test.ts:228`, `form-predicate-root-policy.test.ts:26,50` | Age fixtures against an injected runtime version, or a repro shape. No handshake runs. | | `packages/runtime/src/app-plugin-artifact-forward-conversion.test.ts:81` | The real incident's floor. Its readers run no handshake, and it is green at protocol 18. | | `packages/spec/src/kernel/manifest-unknown-keys.test.ts:53,278,282`, `manifest-permissions-string-list.test.ts:53` | `ManifestSchema` shape 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` | Governed Tier H; the seat routes them. | | `.github/workflows/scaffold-e2e.yml:106` | A comment. | Moved to `>=17.1.0`: - `packages/runtime/src/standalone-stack-security-registrar.test.ts:49` - `standalone-stack-seeder-declaration-copy.test.ts:117` Both boot through the load seam, which refused `^17.1.0` at protocol 18. Their subject is the forward-conversion window, and that window opens on the floor. `>=17.1.0` keeps the floor and admits 18. Re-premised for protocol 18. This is not a handshake: it is a `--from` that meant the authoring major. | Site | Before | After | |---|---|---| | `packages/cli/test/migrate-meta-default-range.test.ts` (#17134) | `--from String(PROTOCOL_MAJOR)`, with the anti-vacuity line `TERMINUS > PROTOCOL_MAJOR` | `--from` the rename step's `toMajor − 1`. The anti-vacuity line moved, word for word, to `migrate-meta-default-range-window.test.ts`, which holds the runtime major at that authoring major in-process. | ## Replay of the root `version` script (throwaway clone of `bdee4d2da1`) A clone of the merged branch. `pre.json` and the `major` marker come from `main`, so no overlay was needed. `release-rehearsal-clone.mjs --prepare` answered FIT. Then `pnpm install --frozen-lockfile` and `pnpm run version`: ```text ✓ PROTOCOL_VERSION already 18.0.0 — in lockstep with @objectstack/spec@18.0.0-next.0 ✓ blank/objectstack.config.ts: engines.protocol → '^18' ``` `release.yml`'s shape filter (`SURFACES`, `stampedPaths()`, `syncedPaths()`, the fixed pattern): - 361 moved paths, with `UNEXPECTED` empty. - `protocol-version.ts` not moved, so `UNVALIDATED` is empty. Content gates on that tree: | Gate | Result | |---|---| | `vitest run --project local src/kernel/protocol-version.test.ts` | 3/3 | | `check:spec-changes` | exit 0 | | `check:upgrade-guide` | exit 0 | | `check:template-manifests` | exit 0 | | `pnpm --filter create-objectstack test` | 17 files, 254 tests | | `check-changeset-no-major.mjs` | exit 0; `not one major ahead of a stable version` (18 vs `18.0.0-next.0`) | Control leg (round 1), the same overlay on `ef1fcb26a2`: `✓ PROTOCOL_VERSION 17.0.0 → 18.0.0`, and `protocol-version.ts` is in the moved set. That is the UNVALIDATED refusal this PR removes. ## Verification **Round 12, at `6ec484243e`** (report on #22130). The merge queue ejected `4ba5952ea1` in a group behind PR #22381, whose new fixture `verify-host-root.test.ts:56` declared `^17` (triage `6081302430`). `main` `8b713fad78` merged (`3b97df5a7f`, nothing of its own); the fixture moved to `^18` in its own commit (`5159d64866`), red before it and green after it. `main` `e148ca9842` merged (`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 on `6ec484243e`: 35 runs, 33 success, 2 expected skips. **Round 11, at `4ba5952ea1`** (report `6080275052` on #22130). The merge queue ejected `5ef8ab6ac5` when PR #22315 landed first with a new step-18 semantic entry. `main` `3ca71b6e05` merged (`a46840ee68`, no path of its own per `git 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 PASS `6079895139`. CI on `4ba5952ea1`: 35 runs, 33 success, 2 expected skips. **Round 10, at `5ef8ab6ac5`** (report `6077854211` on #22130; seat ACCEPT `6078284011`). A pure regeneration: `main` `bf492c8548` (#22412) merged (`b50d5c3866`), then `spec-changes.json` (+12/−12) and the upgrade guide (+6/−6) regenerated in a separate commit; every changed line is a `rationale` of the six step-18 entries #22412 reworded, and the counts stay 64 / 326. The at-tier PASS `6076766071` is carried by the `Regen-provenance:` line in PR comment `6077906555`. CI on `5ef8ab6ac5`: 35 runs, 33 success, 2 expected skips. **Patch round 1, at `eb23db8a82`** (report `6076558925` on #22130). `main` `e02833c240` merged with `os-regen-merge.sh` (`c80423bc16`, committed before any generator ran); no step-18 entry arrived, `check:spec-changes` and `check:upgrade-guide` exited 0 on the merged tree, and both generators rewrite byte-identical (64 / 326). The `specVersion` prescription fix above. `spec` `test` 627 files / 18,757 passed and `test:repo` 54 files / 915; `cli` `protocol-version-gap.test.ts` 16 passed; the dogfood cold-boot file 4 passed. The 32 steps of `Type Check · source gates` exit 0, `check:future-spec-major` included; `dispatch-gates`: 138 derived, 138 run. CI on `eb23db8a82`: 35 runs, 33 success, 2 expected skips. **Round 9, at `4ff2827472`** (report `6075705847`; seat review `6075740377`). The re-sync the handover named: `main` `e75dceddd7` merged (`e04231ab07`), `spec-changes.json` and the upgrade guide regenerated after the merge commit (`062e3545c8`, 324 → 326 semantic entries), the changeset count updated, the two current-app fixtures `main` added moved to `^18`, and PR #22416's one-file fix ported verbatim (`02d07755d7`; `main` now carries the identical blob, so it no longer shows in the diff). The merge-queue build of `c96d8e9446` had failed `check:spec-changes` for exactly this staleness. **Round 8, at `c96d8e9446`.** A merge of `main` `b460153912` only, with `os-regen-merge.sh` (merge commit `c96d8e9446`). `main` touched no migration, conversion or kernel file and added no `^17` handshake since `41d0d4038c`. - `gen:spec-changes` and `gen:upgrade-guide` both rewrote their files byte for byte: `git diff HEAD` is empty. - `check:spec-changes`, `check:upgrade-guide`, spec `check:generated` (15 artifacts), `check:future-spec-major` and `check:adr-0087-registration --base origin/main` exit 0. `check-changeset-no-major --base origin/main --event` exits 0 against this body. - The two `Test Core` timeouts on `c78f55e352` are not this PR's. Both now exit 0: - `@objectstack/lint` `src/build-access-matrix.test.ts` (fixed on `main` by #22358): 8 tests, 3.5s. - `@objectstack/spec` `src/integration/connector-author-shape.test.ts` (`local`): 11 tests, 16.4s. One case, "compiles every full connector example verbatim", takes 11.5s against a 60s `testTimeout`. **Round 7, at `c78f55e352`.** `main` `41d0d4038c` was merged with `os-regen-merge.sh` (merge commit `ecde207250`, no regeneration in MERGE state). Then `gen:spec-changes` and `gen:upgrade-guide` were run (`fed6cd402f`), and the changeset now counts 324 semantic entries (`c78f55e352`). - Every step of `lint.yml`'s `Type Check · source gates` job exits 0, run in order: 32 run steps. They include `check:spec-changes`, `check:upgrade-guide`, `check:authorable-surface` and `check:future-spec-major` (8 witnessed ledger entries; the new generated copies cite no `@objectstack/spec` major). - Spec `check:generated` (all 15 artifacts) exits 0. - `check:adr-0087-registration --base origin/main`, `check:empty-changeset`, `check:changeset-gate-self-tests` and `check:nul-bytes` exit 0. `check-changeset-no-major --base origin/main --event` exits 0 against this body. - `dispatch-gates --commands` derives the same 138 families; the merge adds none. - The full `test` task of every touched package, each exit 0: - `@objectstack/spec`: `test` 626 files, 18,740 tests passed, 1 todo; `test:repo` 54 files, 914 tests. - `@objectstack/cli` `test` (both projects): 366 files (269 `unit` + 97 `integration`), 4,877 tests passed, 2 skipped. `typecheck` exits 0. - `@objectstack/runtime`: `test` 340 files, 4,776 tests passed, 19 skipped; `test:repo` 3 files, 751 tests. - `@objectstack/dogfood`: 226 files passed, 1 skipped; 1,770 tests passed, 9 skipped. - `@objectstack/lint` 127 files, 5,843 tests. `@objectstack/mcp` 35 files, 390 tests. `@objectstack/metadata` 58 files, 870 tests. - `example-crm` 5 files, 45 tests. `example-showcase` 33 files, 408 tests. `example-todo` 7 files, 238 tests. **Round 6, at `99b16b2999`.** `Test Core (1/6)` was red on `dd4ea171fb` in `@objectstack/cli`'s `integration` project: `migrate-meta-default-range.test.ts`, 4 tests. My earlier rounds ran `--project unit` only. Now: - `pnpm --filter @objectstack/cli test` (both projects) passes. 361 files (267 `unit` + 94 `integration`): 4,839 tests passed, 2 skipped, exit 0. - `pnpm --filter @objectstack/cli typecheck` exits 0. It includes `check:test-typecheck` over `test/`. - The full `test` task 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`: `test` 336 files, 4,749 tests passed, 19 skipped; `test:repo` 3 files, 751 tests. - `@objectstack/spec`: `test` 625 files, 18,711 tests passed, 1 todo; `test:repo` 54 files, 914 tests. - `lint`, `mcp`, `metadata`, `example-crm`, `example-showcase` and `example-todo` have one project each. `example-multi-package` has no `test` script. - Two ablations, run from the committed state with `scripts/ablation-replace.mjs`. Each mutation was proven on disk, and each restore was proven by blob = `HEAD` and an empty `git diff HEAD`. Both subjects resolve to `src/`, so no build leg applies. - With the `--to` default reverted to `PROTOCOL_MAJOR`, exactly the window file's three default pins go red: `expected 17 to be 18`, `expected [] to deeply equal [ …(5) ]`, and a missing `Applied 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. - With the window mock neutralised (runtime major left at 18), the window guard and the anti-vacuity line go red: `the registry carries a step past the runtime major: expected 18 to be greater than 18`. **Round 5, at `dd4ea171fb`.** Every step of `lint.yml`'s `Type Check · source gates` job exits 0, run in the job's order: 32 run steps, from `pnpm install --frozen-lockfile` to `check:llms-txt`. - Before the two ledger entries, `check:future-spec-major` was red on three sites: `docs/protocol-upgrade-guide.md:1039` and `packages/spec/spec-changes.json:2340`, `:5880`. - After them: self-test 29 cases, 10,167 files scanned, 8 witnessed ledger entries, every witness still matching. - `readClause2Line` reads the changeset as `{ value: yes, arm: narrowing }`. - These exit 0 against this body: `check-changeset-no-major --base origin/main --event`, `check:adr-0087-registration --base origin/main`, `check:empty-changeset` and `check:changeset-gate-self-tests`. All runs below happened after merging `main` (`3513ac7781`, then `d1dbe70ebd`), on a shared box, through the verify lock. After the third merge (`6729e107e8`, which added one step-18 semantic entry), at `23e9e302d9`, these all exit 0: - `check:spec-changes`, `check:upgrade-guide` and spec `check:generated`; - `check-adr-0087-registration --base origin/main`; - `check-changeset-no-major --base origin/main --event`, against this body; - the spec `local` tests under `src/kernel src/migrations src/conversions` (70 files, 1,853 tests); - `package-union-readers.test.ts` (7/7). `dispatch-gates --commands` derives the same 137 families as at `bdee4d2da1`; the merge adds none. **Builds.** The turbo closure of `dogfood`, `cli`, `cloud-connection`, the examples, `create-objectstack` and `mcp`: 65/65 tasks. Then `lint`/`cli` (59/59) and the seven dist-only packages (43/43). `check:generated`: all 15 artifacts up to date. **Tests** | Package | Scope | Result | |---|---|---| | `@objectstack/spec` | `local`, full | 625 files, 18,661 tests | | `@objectstack/spec` | `repo`, full | 53 files, 903 tests (in shards; `build-schemas-check-mode.test.ts` alone 88/88 in 909s) | | `@objectstack/spec` | after the second merge: `src/kernel src/migrations src/conversions` | 71 files, 1,861 tests | | `@objectstack/spec` | `typecheck` | exit 0 | | `@objectstack/runtime` | `local`, full | 335 files (3 shards: 112 + 112 + 111) | | `@objectstack/runtime` | `repo` | 3 files, 751 tests | | `@objectstack/metadata-protocol` | full | 221 files, 28,277 tests | | `@objectstack/metadata` | full | 58 | | `@objectstack/metadata-core` | full | 18 | | `@objectstack/cloud-connection` | full | 41 | | `@objectstack/lint` | full, after the second merge | 127 files, 5,843 tests | | `create-objectstack` | full | 17 | | `@objectstack/cli` | `--project unit` | 263 files (2 shards), 3,901 tests | | `@objectstack/cli` | touched `.e2e` files | 2 files, 45 tests | | `@objectstack/dogfood` | touched files | 6 files, 44 tests | | `example-crm` | full | 5 | | `example-todo` | full | 7 | | `example-showcase` | full | 33 | | `check-changeset-no-major.mjs --self-test` | — | 378 assertions | **Gates** at `bdee4d2da1`: - I ran every gate `dispatch-gates --commands` derives (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, the #15753 case included. - Three gates were re-run after a build: - `check:skill-examples` and `check:dual-build-cjs-loads` first answered PREREQUISITE NOT MET. - `check:docs-transcript-drift` first read a stale `lint` dist (49 against the docs' 50). - All three exit 0 after rebuilding. ## Acceptance notes - **Docs versus npm `latest`.** The getting-started, CLI and upgrading pages show `^18`, as the ruling directs for docs snippets. npm `latest` stays 17.x until 18 GA, and a 17.x runtime refuses `^18`. Noted for the v18 release page (#22085 Q2). - **`os migrate meta` and the declared range.** Measured on this branch, which carries the #22219 fix `9f0de32a03`, with a probe manifest declaring `engines: { protocol: '^17' }` and one bare `unique: true` declared index. - The dry run (`--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'.` - `--write` rewrote both edits: `Rewrote the declared protocol range, so the load admits protocol 18: objectstack.config.ts:2 manifest.engines.protocol: '^17' → '^18'`, beside the `declared-index-unique-scope` edit. - The changeset's "What you change" bullet says the same in the house sentence's shape. - **CLI advisory versus handshake.** Until the first `next.0` version pass, `main`'s CLI advisory judges ranges against the installed spec package (17.7.0), while the handshake judges against `PROTOCOL_VERSION` (18). A published `18.0.0-next.N` has both on 18. --- _Generated by [Claude Code](https://claude.ai/code/session_01LAi5BVvQNiYzepSAcsoFLK)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 446c8b2 commit 4e9fe9f

52 files changed

Lines changed: 7854 additions & 757 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.changeset/22130-protocol-18.md‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
'@objectstack/spec': major
3+
'@objectstack/mcp': patch
4+
---
5+
6+
feat(spec)!: `PROTOCOL_VERSION` is `18.0.0`; the runtime handshake accepts `engines.protocol: '^18'` and refuses `'^17'`
7+
8+
Clause-②: yes (narrowing: the handshake refuses a manifest pinned to protocol 17 (`'^17'`, `'^17.x.y'`, `'17.x'`), while the exported `PROTOCOL_VERSION` moves to `'18.0.0'` and a `'^18'` manifest is admitted)
9+
10+
<!-- adr-0087: not-required (already-registered field-malformed-scale-precision-removed, record-chatter-position-vocabulary, element-input-target-variable-removed, element-filter-removed, element-form-removed, field-column-lists-canonicalized, metric-filters-removed, cube-sub-day-granularities-removed, cube-join-sql-and-relationship-removed, record-highlights-field-icon-removed, mapping-lookup-params-removed, translation-component-submit-label-removed, page-component-responsive-removed, object-grid-default-sort-removed, object-kanban-quick-add-removed, permission-allow-restore-purge-removed, form-view-option-default-removed, field-reference-to-alias, connector-error-mapping-removed, connector-connection-timeout-ms-removed, hook-timeout-to-timeout-ms, job-timeout-to-timeout-ms, api-endpoint-cache-ttl-to-cache-ttl-seconds, dashboard-refresh-interval-to-refresh-interval-seconds, connector-resilience-keys-removed, connector-triggers-removed, memory-persistence-auto-save-interval-to-ms, turso-config-timeout-to-timeout-ms, view-page-mount-removed, list-view-sort-string-clause-to-array, page-assigned-profiles-removed, chart-config-aria-removed, dashboard-widget-chart-config-structure-removed, translation-per-app-settings-removed, object-tenancy-organization-field-removed, page-component-filter-record-to-rule-array, view-item-owner-hidden-removed, report-joined-chart-removed, view-overlay-owner-hidden-removed, form-layout-inline-grid-to-vertical, currency-config-precision-removed, permission-rls-tags-removed, action-aria-removed, cube-member-inner-name-removed, flow-decision-mode-inclusive-explicit, view-list-tabs-removed, cube-refresh-key-removed, time-default-utc-suffix-dropped, page-header-breadcrumb-removed, connector-sync-keys-removed, form-view-subform-columns-canonicalized, action-block-endpoint-to-target, form-field-public-picker-removed, dataset-count-measure-empty-field-removed, agent-structured-output-refused-members-removed, translation-widget-sub-caption-removed, agent-memory-long-term-store-removed, agent-lifecycle-removed, object-grid-resizable-columns-removed, page-requires-non-compiled-kind-removed, element-text-variant-heading-levels, object-master-detail-form-detail-sort-field-removed, declared-index-unique-scope, manifest-permissions-string-list-removed) The protocol major moves, and that move publishes the already-registered step-18 chain as the current upgrade path: these are its 64 D2 conversions, which `os migrate meta --from 17` replays together with the step's 329 D3 semantic entries. Each entry was registered by the change that made its break, under that change's own disposition; this diff adds, renames and removes none. The one edit outside that chain is the manifest's `engines.protocol` range: it is the ADR-0087 D1 handshake's input rather than a metadata shape any conversion rewrites, and `os migrate meta --write` rewrites it from `'^17'` to `'^18'`. The other categories are closed on facts: the package publishes (not unpublished); a consumer has an edit to make (not no-migration-prescription); and an exported value and the load door move, not a TypeScript declaration alone (not runtime-interface-only or type-surface-only). -->
11+
12+
**BREAKING**, graded `major`: Changesets is in pre mode (`next`) for the v18 line, so this ships in an `18.0.0-next.N` prerelease. The protocol major moves in this reviewed change, not in the version pass (ruling record 6049734955, Q1 → B). Until that line's first version pass, `PROTOCOL_VERSION` is one major ahead of the package version; `check-changeset-no-major.mjs` admits that only in pre mode with a pending `major` for `@objectstack/spec` (ruling record 6056808625).
13+
14+
- **`PROTOCOL_VERSION` reads `'18.0.0'`** and `PROTOCOL_MAJOR` reads `18`. The ADR-0087 D1 handshake on every load and install door (the app load seam, `POST /api/v1/packages`, `POST /api/v1/marketplace/install-local`) now admits a package whose `engines.protocol` admits major 18, and refuses one pinned to 17 (`'^17'`, `'^17.x.y'`, `'17.x'`) with `OS_PROTOCOL_INCOMPATIBLE` (422), naming `objectstack migrate meta --from 17`.
15+
- **What you change.** Run `objectstack migrate meta --from 17` to list the edits for existing sources; `--write` applies the ones it can prove, including the declared `engines.protocol` range, rewritten in place from `'^17'` to `'^18'`, and you apply the rest by hand.
16+
- **`spec-changes.json` and the protocol upgrade guide** now carry the protocol 17 → 18 record, generated from the registries: 64 mechanical conversions and 329 semantic entries. The aggregate record spans 16 → 18.
17+
- **`os init`** stamps `'^18'` in the manifests it writes, and `os lint`'s `protocol/missing-engines-range` fix names `'^18'`. The `create-objectstack` template keeps `'^17'` until the version pass stamps it together with its `@objectstack/*` dependency ranges.
18+
- **`@objectstack/mcp`**: the README's `defineStack` examples declare `'^18'`.

‎content/docs/deployment/cli.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2474,7 +2474,7 @@ export default defineStack({
24742474
name: 'My App',
24752475
description: 'My ObjectStack application',
24762476
// Protocol major this app is authored against (ADR-0087 load-time check).
2477-
engines: { protocol: '^17' },
2477+
engines: { protocol: '^18' },
24782478
},
24792479

24802480
objects: Object.values(objects),

‎content/docs/getting-started/your-first-project.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ export default defineStack({
135135
type: 'app',
136136
name: 'My App',
137137
// Protocol major this app is authored against (ADR-0087 load-time check).
138-
engines: { protocol: '^17' },
138+
engines: { protocol: '^18' },
139139
},
140140
// `automation` runs flows and, per ADR-0097, materializes declarative
141141
// `connectors:` entries at boot. The three generic executors below register

‎content/docs/upgrading.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ runtime checks that handshake **first**, before it loads anything:
156156
```ts
157157
manifest: {
158158
// ...
159-
engines: { protocol: '^17' },
159+
engines: { protocol: '^18' },
160160
}
161161
```
162162

‎docs/protocol-upgrade-guide.md‎

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

‎examples/app-crm/objectstack.config.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export default defineStack({
4545
// Protocol major this package is authored against (ADR-0087). The kernel
4646
// checks the range at load time and refuses a major-incompatible runtime
4747
// with a structured diagnostic instead of failing deep in a schema parse.
48-
engines: { protocol: '^17' },
48+
engines: { protocol: '^18' },
4949
},
5050

5151
// Auto-resolved by the CLI; `ui` enables the Studio shell, `automation`

‎examples/app-multi-package/src/packages/core/index.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export default defineStack({
1919
version: '1.0.0',
2020
type: 'app',
2121
description: 'The App half of a two-package release artifact (ADR-0130 D4)',
22-
engines: { protocol: '^17' },
22+
engines: { protocol: '^18' },
2323
},
2424

2525
objects: [

‎examples/app-multi-package/src/packages/orders/index.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export default defineStack({
5858
version: '1.0.0',
5959
type: 'module',
6060
description: 'The Module half of a two-package release artifact (ADR-0130 D4)',
61-
engines: { protocol: '^17' },
61+
engines: { protocol: '^18' },
6262
// The App package this module extends. `resolveArtifactPackageOrder` reads
6363
// it as the topological edge that registers core BEFORE orders (ADR-0130
6464
// D5, ADR-0116's one sorter) — the array order below is not what decides.

‎examples/app-showcase/objectstack.config.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ export default defineStack({
8686
// Protocol major this package is authored against (ADR-0087). The kernel
8787
// checks the range at load time and refuses a major-incompatible runtime
8888
// with a structured diagnostic instead of failing deep in a schema parse.
89-
engines: { protocol: '^17' },
89+
engines: { protocol: '^18' },
9090
},
9191

9292
// Capability tokens the CLI resolves to platform plugins:

‎examples/app-todo/objectstack.config.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export default defineStack({
4545
// Protocol major this package is authored against (ADR-0087). The kernel
4646
// checks the range at load time and refuses a major-incompatible runtime
4747
// with a structured diagnostic instead of failing deep in a schema parse.
48-
engines: { protocol: '^17' },
48+
engines: { protocol: '^18' },
4949
},
5050

5151
// Platform services this app needs — the closed `PLATFORM_CAPABILITY_TOKENS`

0 commit comments

Comments
 (0)