Repository navigation
Commit 223ae93
docs(releases): the 18.0.0 notes and upgrade checklist, walked from 17.7.0; Upgrading's table gains 18.0.0 and 17.5–17.7 (#24922)
Fixes #24905
Clause-②: no
The v18 line gets its curated 18.0.0 page,
`content/docs/releases/v18/18-0.mdx`, beside `index.mdx` as the v17 line
does it, with a `## Upgrade checklist` walked from 17.7.0 on the
published 18.0.0 packages. `index.mdx` drops the "curated notes … are
not written yet" sentence and links the page; `meta.json` lists it.
Upgrading's per-release table gains a v18.0.0 row and the 17.5.0, 17.6.0
and 17.7.0 rows, and its stale v17.4.0 row now links the checklist
`17-4.mdx` carries. The first downstream reader is
objectstack-ai/hotcrm#2202 (HotCRM's upgrade to 18.0.0); this PR does
not claim to have walked it.
## The CHANGELOG filter
- **Read.** The `## 18.0.0` sections of every package CHANGELOG at the
version commit `06cab4849a`: 53 packages, 1,115 distinct entries
(`@objectstack/spec` alone: 341), 340 of them marked BREAKING or
carrying a `(narrowing)` Clause-② arm. The 340 were read in body (their
FROM → TO tables and one-line fixes); the other 775 were screened by
title, and the 253 whose title says refuse / retire / remove / default /
rename were read further.
- **Kept: 129 changeset commits cited on the page** (153 of the 1,115
entries; 137 of the 340 flagged).
- **The rule.** An entry is kept when an application must edit something
for 18.0.0 (its metadata, its code against the published packages, its
REST or SDK client, its tests on `@objectstack/verify`, or its
deployment configuration) **and** the change could be measured on the
18.0.0 artifact. Left out: platform internals and tooling, the Console
refresh entries, rules that bind only a walled operator, entries a later
entry undoes, surfaces that shipped only in a prerelease
(`record:json`), and two app-relevant behaviours that could not be
measured here (`1b40780` approvals resume as the decider; `f05649f` /
`3f0b6f2` hook `readonly` and `onError: 'abort'` rollback).
- **Deviation from the named corpus:** the dispatch named
`packages/spec/CHANGELOG.md`; it holds 132 of the 340 flagged entries,
so the other packages' sections were read too (decided on the
business-need axis: the CLI, runtime and plugin breaks an upgrader meets
live there).
## Checklist steps, as measured on 18.0.0
| Step | What it says | How it was measured on 18.0.0 |
| :--- | :--- | :--- |
| 1 · pins | every `@objectstack/*` to 18.0.0 together, lockfile | walk:
`pnpm install` moved the seven ranges to 18.0.0, no peer warning |
| 1 · protocol | `engines.protocol: '^18'`, `specVersion: '^18.0.0'`;
`migrate meta --from 17 --write` rewrites the first | walk: on `'^17'`
validate warns and the boot exits 1 `✗ package … targets protocol ^17 …
Run: objectstack migrate meta --from 17`; `--write` printed
`manifest.engines.protocol: '^17' → '^18'` |
| 1 · migrate meta | read its list as a search list | walk: `406 manual
change(s)` on a one-object app |
| 1 · skills | remove the `skills add` skills, add `@objectstack/skills`
with the project's manager, sync | walk: `Skipped … installed with
skills add` ×10 + `Nothing to sync.` exit 0 before; `Synced 10 skills`
after; `npm install --save-dev` exit 1 `edgesOut` in the pnpm project |
| 1 · validate/build | run both | walk: both exit 0 |
| 2 · overlays | `os migrate security-catalog-overlays` before the first
boot | walk 2: exit 0, `✓ No environment-wide permission-set or position
row is stored over a package-held name` |
| 2 · refused boot | boot once; quoted refusal, code
`ORGANIZATION_OWNERSHIP_MIGRATION_REQUIRED` | walk: `objectstack start`
exit 1, port never bound; code read off `@objectstack/runtime`
`dist/index.js` |
| 2 · not apply first | `os migrate apply --allow-destructive` is not
the remedy | walk, on a DB copy: `os migrate plan` 28 destructive, apply
exit 1 `✗ Refusing to drop sys_comment.reactions …` |
| 2 · backup / plan / apply | `--out plan.json`, then `--apply
--plan-file plan.json --backup-taken` | walks 1 and 2: 81 tables planned
(77 before the refused boot), `unattributableRows: 0`; apply attributed
32 rows, rewrote 2 grants, deleted 49 mirrors, dropped 14 columns, NOT
NULL on 32, `✓ The ceremony is complete` |
| 2 · serve | the server serves the upgraded DB | walk: admin signs in,
keeps `platform_admin` / `admin_full_access`, 3 notes read, a 4th
written, the 17.7.0 API key answers 200 |
| 2 · drift | `comment-reactions` preview + `--apply --yes`, then
`migrate apply --allow-destructive --yes` | walk 2: `Converted 1
reaction(s)`, 3 columns dropped, next boot 0 drift lines, reaction reads
back |
| 3 · flows | value slots CEL, text slots `{{ }}`, bound roots, `$`
names, node contracts, aliases, edges, retry | probes: `os validate`
(rule `expression-invalid`, exit 1) and `defineStack`
(`STACK_SCHEMA_INVALID` 422) on the installed 18.0.0 spec, each position
the tag names |
| 3 · pages, views, actions | props are errors, `dataSource` binding,
retired keys, layout, list views, `editInline`, url schemes | probes as
above; save door measured: `PUT /api/v1/meta/page/NAME` → `422
INVALID_METADATA` for an undeclared prop and `kind: 'jsx'` |
| 3 · objects, fields | `externalSharingModel`, `unique: true`,
`controlled_by_parent`, stage `kind`, one-hop formulas, option
`visibleWhen`, metadata-table lookups, duplicates, `defineSeed` | probes
(`defineStack`, `os validate`, `defineSeed`) |
| 3 · security | `permissionSets`, retired permission-set keys,
`packageId`, `'.FIELD'`, one name per holder, sharing rules, manifest
`permissions` | probes; `NAMESPACE_CONFLICT` read off
`@objectstack/objectql` `dist/core.js`; declared-rule text read off
`@objectstack/plugin-sharing` |
| 3 · datasources, templates, webhooks, inbound hooks | `mongo`,
template keys and registry, declared webhooks, timestamped signature |
probe + walk (`os serve` with `OS_DATABASE_DRIVER=mongo` exit 1);
tarball: `plugin-email`, `DeclaredWebhookNameConflictError`
(`plugin-webhooks`), `signature-body-only` (`trigger-api`) |
| 3 · hooks and code | hook-body lowering, engine context, removed
exports, `allowEnvironmentOverlay`, hand-built kernels, `os meta
resync`, verify | tarball: `cli/dist/utils/extract-hook-body.js`,
`plugin-security` (`carries no principal`,
`AUTHZ_STORE_IDENTITY_OBJECTS`), `service-i18n` d.ts, `verify`; exports
probed absent; walk: `os meta resync` exit 2 `command meta:resync not
found` |
| 3 · REST, SDK, keys | key lifecycle, CORS, anonymous docs and i18n,
body validation, catalog rows gone, upload scopes, reactions, form
submit, imports, search | walk 2 against the served 18.0.0: new key
`expires_at` +90 d, 2-year key `400 API_KEY_EXPIRY_TOO_LONG`, 17.x key
stamped +1 y; no `access-control-allow-origin` for a foreign origin (GET
and preflight); `401 UNAUTHENTICATED` on `/api/v1/openapi.json` and
`/api/v1/i18n/locales`; `400 INVALID_REQUEST` for `text/plain`; `400
VALIDATION_FAILED` for `null`, `[1,2]` and `expectedVersion: 3`; 404 on
the four catalog objects; grants carry `permission_set`, no
`permission_set_id`; `scope: 'public'` → `400 INVALID_REQUEST`; `403`
codes, `PAYLOAD_TOO_LARGE`, the submit and import doors read off the
tarballs |
| 4 · not carried / unlisted | at `a7199ef974`: 20 pending changesets,
six `minor` ⇒ 18.1.0; 8 shipped in 18.0.0 unlisted; 12 not in it; 7a-2
an open PR — **re-read at `71696121a6` in [Merge with
#24912](#merge-with-24912): 26 / 10 minor / 8 / 18, 7a-2 merged after
the cut** | `git merge-base --is-ancestor` of each changeset's adding
commit against `06cab4849a` in a full clone (`--is-shallow-repository`
false): 8 exit 0, 12 exit 1, the exit-0 rows of the same run being the
control leg; tarball: `deploymentWide` in `plugin-sharing`,
`codeInviteRoleNotFound` in `plugin-auth`, adopt-orphans guard absent
from `runtime`; walk: `os serve` with no auth secret exit 1 with the
quoted refusal |
| 4 · walled postures | `group` / `isolated` boot on a fresh DB with
their prerequisites | walk: three refusals in order (organizations
runtime, `OS_PLATFORM_OWNER_EMAIL`, membership policy), then `✓ Server
is ready` with `Tenancy: group` and `Tenancy: isolated` |
| 5 · tags | `latest` 18.0.0, `next` 18.0.0-next.5 (older), `rc`
17.0.0-rc.6; never `@next` | `npm view … dist-tags` on spec, cli,
runtime, core, objectql, skills, organizations, create-objectstack; `npm
view … time` / `versions` (no `next.4`) |
## The walk
- **Fixture.** `create-objectstack@17.7.0` blank scaffold (`v17app`,
pnpm 10.28, Node 22.22), its ten skills installed by `skills add`,
booted once on 17.7.0 with `objectstack start` into
`.objectstack/data/objectstack.db`. Walk 1 seeded an admin and three
notes; walk 2, on a fresh scaffold that followed the written steps 1 and
2 verbatim, also seeded an API key and a comment carrying a stored emoji
reaction. Everything ran under the session scratchpad, never in the
repo.
- **Outcomes.** Every step in the table above ran as the page says;
walks 1 and 2 printed the same ceremony numbers. Extra measurements: the
walled postures on a fresh database, `OS_DATABASE_DRIVER=mongo`, a
production boot with no auth secret, and the save door.
- **What the walk changed in the page.**
1. The skills step: `npm install --save-dev` exits 1 (`npm error Cannot
read properties of null (reading 'edgesOut')`) in the pnpm project
`create-objectstack` makes, so the page (and `index.mdx`) adds the
package with the project's manager.
2. `specVersion` is a hand edit: `--write` rewrites `engines.protocol`
only, and nothing on the boot path reads `specVersion`.
3. The order in step 2: overlays first, then a boot that is refused on
purpose (its schema sync creates the tables the ceremony needs: 77
planned before it, 81 after), and ⛔ not `os migrate apply
--allow-destructive`, which the drift lines recommend and which refuses.
4. The tail of step 2: a served boot still logs three orphaned columns;
`comment-reactions` then `apply --allow-destructive` clears them.
5. Step 4: the "not carried yet" premise split in two once the tarballs
were read (below).
## The PM's hypotheses, measured
1. **Falsified.** `walledPostureRefusal` is in
`@objectstack/cli@18.0.0-next.5` (`dist/commands/serve.js`) and absent
from `@objectstack/cli@18.0.0`; history: added by `ae6bd1a696` (#24480),
removed by `fc34d000fc` (#24667). `os serve` 18.0.0 boots `group` and
`isolated` once the organizations runtime, the owner email and a
membership policy are present; point 4 states that posture with the
measured refusals.
2. **Confirmed.** The boot is refused before the port binds, exit 1, by
the `com.objectstack.organization-ownership-boot-guard` plugin; the
error code is `ORGANIZATION_OWNERSHIP_MIGRATION_REQUIRED` (on the error
object, not in the printed line), and the remedy names `os migrate
organization-ownership --out PLAN` then `--apply --plan-file PLAN
--backup-taken`.
3. **Confirmed.** `17-4.mdx` carries `## Upgrade checklist` (line 606)
and `### 17.4.0`; 17.5–17.7 each carry one (lines 1671, 1524, 1259) and
had no row.
**The card's own premise, measured** (at `a7199ef974`; 7a-2 has since
merged after the cut — see the merge section). "The 18.x steps that
landed after the cut (for example 7a-2, PR #24872) ship in 18.1.0" does
not hold as written: #24872 is an open PR, not on `main`; and 7a-3
(#24828) and 7b-3 (#24845) landed **before** the version commit, so
their code is in the 18.0.0 packages while their changesets are still
pending. Step 4 says so.
## Choices settled here
- **The corpus**: all 53 package CHANGELOGs, not spec's alone — the
business-need axis (above).
- **Measurement tags per line** (measured in the walk / probed on 18.0.0
/ read off the tarball) instead of the v17 pages' single "not exercised"
tag — the guard-against-AI-error axis: a reader can tell a run step from
a probed refusal, and nothing unmeasured is presented as a step.
- **Two unmeasurable behaviours left out** rather than tagged "not
measured" — the dispatch's ruling that every step is measured on the
artifact.
## Deviations, named
- **File surface breach: `scripts/docs-audit/handwritten-docs.json`, one
line.** `pnpm check:docs-audit-scope` reds when a hand-written page
exists that the roster does not list; the line was written by `node
scripts/docs-audit/check-audit-scope.mjs --write` (`+1 added, -0
removed`). The dispatch both bounds the surface to
`content/docs/releases/v18/**` and `content/docs/upgrading.mdx` and
requires the docs gates to stay green; the ruled page shape needs the
roster line, so this PR keeps the gate green and names the breach here
rather than choosing silently.
- **One sentence of Upgrading's prose changed beside the rows.** "Only
the v17 line is broken out per release. Each of its three minors …" is
false once the v18.0.0 row links a per-release page (and "three" was
already wrong), so it reads "The v17 and v18 lines are broken out per
release. Each v17 minor …". The page's rules are untouched; Upgrading's
step-4 skills command is untouched (see the acceptance notes).
## Acceptance notes
- **18.0.0's CHANGELOG omits eight changesets whose code shipped in
18.0.0.** *(Downgraded after the epic seat's note `6294471945`: the
#23818 ruling's `annotate-shipped` step carries such entries into the
next CHANGELOG with a "Shipped in …" lead line — the 18.0.0 CHANGELOG
already holds entries headed `**Shipped in 18.0.0-next.2**, whose
CHANGELOG missed this entry` — so this is the release lane working as
ruled, not a defect.)* At `a7199ef974`, `.changeset/24583-7a3-…`,
`24583-7b3-…`, `24750-…`, `24751-…`, `24767-…`, `24772-…`, `24773-…` and
`24785-…` belong to commits that are ancestors of the version commit
`06cab4849a`, and the 18.0.0 tarballs carry their code
(`deploymentWide`, the no-auth-secret refusal measured with `os serve`,
`codeInviteRoleNotFound`); their entries will first appear in 18.1.0's
CHANGELOG. Reported to the PM for filing.
- **Before the ceremony, the `[schema-drift]` lines and `os migrate
plan` prescribe `os migrate apply --allow-destructive` for the
`organization_id` columns without naming the ceremony** (28 destructive
changes on the walk's database), and that apply then refuses on
`sys_comment.reactions`. Reported to the PM for filing.
- **`npm install --save-dev "@objectstack/skills@…"` fails in a
pnpm-installed project** and is still the command in
`content/docs/upgrading.mdx` step 4, `content/docs/ai/skills.mdx`,
`content/docs/ai/skills-reference.mdx`, `skills/README.md` and
`skills/objectstack-upgrade/SKILL.md`. Reported to the PM for filing.
- `18.0.0-next.4` was never published, while every CHANGELOG carries a
`## 18.0.0-next.4` section. Observation only.
- `os migrate security-catalog-overlays`'s preview printed "Raw
execute() called … FORWARDED, not refused — if it wrote, this boot
wrote"; the database file's mtime did not move. Observation only.
## Merge with #24912
#24912 (card #24904, `71696121a6`) edited `content/docs/upgrading.mdx`
and `content/docs/releases/v18/index.mdx` and amended
`packages/cli/CHANGELOG.md` in place. `git merge origin/main` (no
rebase, no force) stopped on one conflict: both sides added a `v18.0.0`
row to Upgrading's "Per-release specifics" table, and #24912's side
still carried the stale `v17.4.0 — ⛔ checklist not written` row.
Resolution: **one `v18.0.0` row** linking
`/docs/releases/v18/18-0#upgrade-checklist`, `#upgrading-to-180` and
`/docs/releases/v18`, and this PR's 17.4–17.7 rows. `index.mdx` merged
clean; `packages/cli/CHANGELOG.md` is #24912's and untouched here. Merge
commit `b6917f0b72`; `git merge-tree --write-tree origin/main HEAD` exit
0 before the push and again after it (main `71696121a6`).
New measurements for the reconciliation (all on npm 18.0.0, under the
session scratchpad):
- **Walk 3, walled:** a `create-objectstack@17.7.0` app with
`@objectstack/organizations@17.7.0`, booted under
`OS_TENANCY_POSTURE=group`, an owner and two records, moved to 18.0.0
(the exact `"17.7.0"` organizations pin moved by hand): `os migrate
security-catalog-overlays` exit 0; refused boot exit 1; plan `posture
group`, 81 tables, `unattributableRows: 0`; apply `✓ The ceremony is
complete`; served `Tenancy: group`, both records in their organization.
- **Walk 3, development database:** a 17.7.0 `os dev` database moved to
18.0.0. `OS_DATABASE_URL=file:… os serve --dev` exit 1 naming `Run 'os
migrate organization-ownership --dev --out PLAN' …`. `os dev` with the
artifact 17.7.0's `os start` had compiled still in `dist/`: `✗ The
upgrade plan was refused, and nothing was changed.` (the plan refused
protocol `^17` although `objectstack.config.ts` read `'^18'`). After `os
build`: `✓ Upgraded the development database to the v18 organization
ownership` and `✓ server started on the upgraded database`.
- **`os start` with no secret:** a fresh `--home`, no `AUTH_SECRET` /
`OS_AUTH_SECRET`: it wrote `HOME/auth-secret` (mode `0600`), served,
sign-up `200`.
- **The CHANGELOG copies:** the `@objectstack/cli@18.0.0` tarball's
`CHANGELOG.md` line 162 still reads `ae6bd1a: feat(cli)!: 18.0 supports
fresh 'single' installs only — …`; the repository's
`packages/cli/CHANGELOG.md` at `71696121a6` line 162 reads `ae6bd1a:
feat(cli)!: the 18.0 pre-release refusals of … both lifted in 18.0.0`,
and its `fc34d00` headline adds "and on databases the ownership ceremony
upgraded".
| # | File | Old → new | Measurement |
| :--- | :--- | :--- | :--- |
| 1 | `upgrading.mdx` table | two `v18.0.0` rows + #24912's stale
`v17.4.0` row → one `v18.0.0` row (checklist · Upgrading to 18.0 · v18)
+ this PR's 17.4–17.7 rows | hypothesis 3 (17-4.mdx carries its
checklist) |
| 2 | `upgrading.mdx` "Upgrading to 18.0" | a 6-step sequence → 8 steps
in the walk's order: `os migrate security-catalog-overlays` first, the
drift tail (`comment-reactions --apply --yes`, then `apply
--allow-destructive --yes`) last, and one link to
`/docs/releases/v18/18-0#2-upgrade-the-database` for each step's output;
the rest of #24912's prose unchanged | walks 2 and 3 (overlays exit 0);
walk 2 (`Converted 1 reaction(s)`, `Applied 3 change(s)`, next boot 0
drift lines) |
| 3 | `upgrading.mdx` `--dev` paragraph | "`os dev` runs the whole
sequence itself" → + "Run `os build` before it: while
`dist/objectstack.json` was still the artifact 17.x compiled, `os dev`'s
in-place upgrade refused its plan and changed nothing." | walk 3,
development database |
| 4 | `upgrading.mdx` walled bullet | + the declared membership policy,
`OS_AUTH_MEMBERSHIP_POLICY=invite-only` (or `auto`), with its refusal `✖
FATAL: tenancy posture 'group' is walled, but this deployment never
declared a membership policy.` | fresh-DB walk (refusal); walk 3
(upgraded DB served with it set) |
| 5 | `v18/index.mdx` `--dev` bullet | + the same `os build` caveat |
walk 3, development database |
| 6 | `v18/index.mdx` walled bullet | + "18.0.0 also refuses a walled
boot until a membership policy is declared …" with the refusal; #24912's
"same prerequisites as 17.x" sentence kept as written | as 4 |
| 7 | `v18/index.mdx` "Upgrading a database to 18.0.0" | closing
sentence + one link to the walked 18.0.0 checklist; `## Per-release
notes` kept; nothing of #24912's removed | — |
| 8 | `18-0.mdx` "How to read the 18.0.0 CHANGELOG" | the `ae6bd1a`
caution → says it reads the tarball's copy (what `node_modules` holds),
names #24912 and quotes the amended repository text | the two CHANGELOG
copies |
| 9 | `18-0.mdx` step 2 intro | + the overview link to
`/docs/upgrading#upgrading-to-180` | — |
| 10 | `18-0.mdx` step 2, `os dev` | "according to the CLI reference …
did not exercise that path" → the measured `os serve --dev` refusal, `os
dev`'s in-place upgrade, and the `os build`-first caveat | walk 3,
development database |
| 11 | `18-0.mdx` step 4, auth secret | "A production server boot …" →
"A production `os serve` boot …" + "`os start` is not refused: it wrote
`HOME_DIR/auth-secret` (mode `0600`) … and served sign-up" — now agrees
with #24912 | `os start` with no secret |
| 12 | `18-0.mdx` Callout and step 4, walled | "a walled database … not
walked" → walked under `group` (an `isolated` database not walked) — now
agrees with #24912's "on a database the ceremony upgraded" for `group` |
walk 3, walled |
| 13 | `18-0.mdx` step 4, pending changesets | read at `a7199ef974` (20,
six `minor`; "7a-2 is an open PR") → read at `71696121a6`: 26, ten
`minor`; 8 shipped in 18.0.0 unlisted; 18 not in it, six `minor`, 7a-2
(#24872, merged 2026-10-20 15:26 UTC, after the cut) first among them |
`git merge-base --is-ancestor` per changeset against `06cab4849a`; `gh
api pulls/24872` (`merged: true`) |
Point 13 is not one of #24912's: it is named because the merge brought
`.changeset/24583-7a2-…` into this branch's tree, which made "7a-2 is
not on `main`" false.
Already in agreement, re-measured: #24912's "start 18.0 against the
database once (`os serve` / `os start`)" (the walks used `os start`);
"the refusal `os serve --dev` prints names the command with `--dev`"
(walk 3).
## Round 3
Two sites told a reader to run a destructive apply without reading its
plan first; `os migrate apply --allow-destructive` applies **every**
destructive change in the plan, and `--yes` skipped its confirmation.
- **Measured on 18.0.0 first** (on a copy of the `group` walk's
post-ceremony database): `os migrate plan` listed 4 destructive changes
(`sys_comment.reactions`, `sys_sso_provider.created_by` / `updated_by`,
index `idx_sys_sso_provider_organization_id`). `os migrate
comment-reactions --apply` without `--yes`, non-TTY: exit 1, `⚠ Apply
mode writes sys_comment_reaction rows. Re-run with --yes to confirm …`.
`os migrate apply --allow-destructive` without `--yes`, non-TTY (stdin
`/dev/null`): applied nothing, printed `⚠ Confirmation required. Re-run
with --yes to apply, or use "os migrate plan" to preview.`, **exit 0**
(the plan still listed 4). On a TTY (a pty): it listed the changes and
asked `Apply 4 change(s) to DATABASE? [y/N]`; `n` → `ℹ Aborted — no
changes made.` exit 0 (plan still 4); `y` → `✓ Applied 4 change(s).`
exit 0, then `✓ Physical schema is in sync with metadata`.
- **`content/docs/upgrading.mdx`, "Upgrading to 18.0" step 8:** "`os
migrate comment-reactions --apply --yes`, then `os migrate apply
--allow-destructive --yes`" → preview `os migrate comment-reactions`,
then `--apply --yes`; run `os migrate plan` and check its destructive
entries are only the ceremony's leftovers (in the walk: the three
columns); any other entry is the app's own and decided separately; then
`os migrate apply --allow-destructive` in a terminal, answering its
`[y/N]` prompt after reading the list; in a non-interactive shell it
applies nothing and still exits `0`.
- **`content/docs/releases/v18/18-0.mdx`, step 2 "Then clear the drift
…":** "then `os migrate apply --allow-destructive --yes`, which dropped
the three columns" → the same plan check, the `[y/N]` prompt and the
non-TTY behaviour, each quoted from the measurement; the walk's own
`--yes` run stays recorded as a fact of the walk; the `group` walk's
extra index entry is named.
- **Rule 4 sweep, found and fixed the same way:** `npx skills remove
--all` (`-y` implied, removes every installed skill) in `upgrading.mdx`
step 4 and `v18/index.mdx` → "run `npx skills list` first", and
`18-0.mdx` step 1 names `npx skills list` before the named `skills
remove … -y` (measured: it listed the ten `objectstack-*` skills with
their source). **Reviewed, not changed:** the ceremony's `--apply
--plan-file … --backup-taken` (ruled out of scope); `os migrate
security-catalog-overlays` (its bare run is the preview, `--apply`
follows it); `os migrate comment-reactions --apply --yes` in `18-0.mdx`
(already after its preview); `os migrate meta --from 17 --write` (writes
only the source edits it can prove, re-runs the chain and restores on a
mismatch; not destructive); `skills experimental_sync --copy -y` (copies
the package's skills into the agent directory, skipping `skills add`
ones); Upgrading's `os migrate apply --allow-destructive # … once you
have read the plan` (already after `os migrate plan`).
## Gates
At head `9da4c8d328` (merge base `71696121a6`), the re-derived list (62
commands, the same 62 as rounds 1 and 2) ran locally, every one exit 0,
after `pnpm --filter '@objectstack/lint...' --filter
'@objectstack/client-react...' build` under the verify lock (VERDICT
command-exit 0); `--ran` with recorded exit codes: `62 derived, 62 run,
0 NOT-MEASURED, 0 UNRUN`. `git merge-tree --write-tree origin/main HEAD`
exit 0 just before the push. Earlier heads `5019f5152f` and `b6917f0b72`
were green on the same 62. CI's own check-runs are the record for
everything outside that list.
**Changeset:** none. Measured: all 73 published `package.json` manifests
declare `files[]` (control: all 73 name `dist`), none names `content/`,
a docs path or `scripts/`, and the root package is private, so
`skip-changeset`.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01FM29ZqLTrnPNXxijioZwMp)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent b9a79ef commit 223ae93
5 files changed
Lines changed: 780 additions & 22 deletions
File tree
- content/docs
- releases/v18
- scripts/docs-audit
0 commit comments