Skip to content

docs(skills): objectstack-upgrade names os migrate meta --write beside the default run - #22122

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-22120-upgrade-skill-migrate-meta-write
Oct 8, 2026
Merged

os-zhuang merged 1 commit into
mainfrom
claude/issue-22120-upgrade-skill-migrate-meta-write

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #22120

Clause-②: no

The published upgrade skill said that os migrate meta "writes nothing but --out" (Quickstart comment, failure-mode row) and that it "does not rewrite your source files" (§1). Since os migrate meta --write landed (a959493cdf), that is true only of the default run. This PR makes every sentence in skills/objectstack-upgrade/SKILL.md that states what the command writes true of both routes, within the skill's token ceiling.

What changed (one file: skills/objectstack-upgrade/SKILL.md)

  • Quickstart step 1 comment: "(writes only --out; --write also rewrites the sites it can prove)".
  • Flag table in §1: one new example line, os migrate meta --from 16 --write # rewrite the proven sites in place.
  • §1 "The one fact that surprises every operator": now opens "By default os migrate meta rewrites no source file. It lists the mechanical edits and writes only the --out JSON snapshot." followed by one sentence for --write: it rewrites in place each edit it can trace to one literal in one project file, lists every other with the reason it was not written, never writes a semantic change, and if re-running the chain over the written files disagrees, restores every file and exits 1. The porting sentence now reads "Porting the edits left unwritten is yours" — true on both routes.
  • Failure-mode row "migrate meta reports changes, but the files are unchanged": cause "Working as designed — the default run only lists."; fix "Pass --write, or port the printed edits by hand; then replay from the target major to confirm 0 changes."
  • Failure-mode row "--apply refused / stored-only flag rejected": the tail "the authored-source chain has nothing to write to" (false under --write) now mirrors the CLI's own refusal text: "writes only --out and, with --write, the sources".

Every claim is read from packages/cli/src/commands/migrate/meta.ts at db4c45b8c3 (flag description and exclusive: ['stored']; WriteOutcome.status = written | restored | unwritten; printWriteOutcome lists each unwritten site as "not written [kind]: reason"; this.exit(1) whenever write.status !== 'written') and from the packages/cli/src/utils/authored-source-codemod.ts module docblock (one object or array literal in one project file, statically matching the loaded value, no second reference to any binding the walk crossed; semantic TODOs never read). The default run and --write are both described; the text does not say what --out does on a run with nothing to migrate and does not describe the semantic-notice list (both are in flight on meta.ts in #22121 and #22115).

Token ratchet — paid in content, not wrapping

node scripts/check-skills-token-ratchet.mjs (tokens = ceil(utf8 bytes / 4); ceiling for this file 6193):

reading lines bytes tokens headroom
before (db4c45b8c3) 488 24772 6193 0
after (9bb10014) 484 24748 6187 6

Diff: +13 / −17 lines. Gate line at 9bb10014: ✓ check-skills-token-ratchet: skills/objectstack-upgrade/SKILL.md is 6187 tokens (ceiling 6193; headroom 6).

The growth (+179 bytes gross) was paid by deleting three pieces of duplicated content, no rule, failure-mode row or needed command among them:

  1. the --out recheck code block in §1 — its first line was byte-identical to Quickstart step 1 (os migrate meta --from 16 --out .upgrade/migrated.stack.json), and the "replay from the target major, 0 changes" recheck is already carried by Quickstart step 3, the §3.3 callout and the failure-mode row;
  2. the clause "It rewrites the loaded stack in memory and reports the diff" — the mechanism paragraph three lines above already states load → normalize without the load-time pass → replay per hop → parse;
  3. the sentence "The authored-source flags and the stored-only flags are mutually exclusive, and mixing them is refused rather than ignored" in the --stored subsection — the failure-mode row "--apply refused / stored-only flag rejected" carries the same fact with its fix, and the preceding "--stored takes no --from" keeps the other direction.

Scope held

Gates

Derived with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack from the changeset at 9bb10014 (24 families; identical to the path-derived list). All 24 run, exit codes recorded beside the printed command and reconciled with --ran (see the report comment on #22120 for the table). check:doc-formula-expressions first exited 3 (prerequisite: @objectstack/lint not built) and was re-run after the prescribed build.

Acceptance notes

  • Out of scope, reported for a card: SKILL.md:103 says "os migrate meta --from 10 replays every step in order", but MIGRATION_SUPPORT_FLOOR = 16 (packages/spec/src/migrations/registry.ts:79), so that command refuses with MigrationFloorError / unsupported_from_major — the skill's own failure-mode row says so. Not fixed here: the sentence's point ("several majors late is the designed-for case") cannot be re-exampled truthfully on a 16 → 17 chain, so the fix is a rewording, not a mechanical edit.
  • Observation, not changed: evals/protocol-major-upgrade.json eval 1 expected_output says "the command rewrites nothing on disk" — true of the default route it describes; a --write-aware eval is a product decision, not a drift fix.

维护者速读(草稿)

改了什么:只改一份对外发布的技能文件 skills/objectstack-upgrade/SKILL.md。原文在三处断言 os migrate meta "只写 --out、不改源文件";自 --write 落地后这只对默认运行成立。现在每一句关于"命令写什么"的话都同时对默认运行和 --write 成立:默认只列出机械修改、只写 --out 快照;--write 只就地改写它能证明来源的站点(一个项目文件里的一个字面量),其余逐条列出未写原因,语义修改永不写,复跑不一致时恢复全部文件并以 1 退出。

为什么改:客户项目的 AI agent 整包加载这份技能;它读到"命令只写 --out"就永远不会发现 --write,而读到无条件的"自动改写"又会被误导。文字必须与 CLI 源码一致(meta.ts 的 flag 描述、written | restored | unwritten 三态、exit 1),并与 spec 侧退休句的措辞("列出机械修改")保持一致。

风险与代价(含回滚):纯文本改动,无代码、无 changeset、无生成物。token 棘轮:6193 → 6187(上限 6193),净减 24 字节,靠删除三处重复内容支付,未删任何规则、故障行或命令。回滚即 revert 本 PR 的一个提交。注意 #22121(--out 在无变更运行时的行为)与 #22115(语义通知列表)在 meta.ts 上并行,本文未对那两点做任何断言。

席位意见:(留空,席位定稿)

你要做的:skills/** 为 Tier H 受管面:请以授权账号 APPROVE 一次,或亲手合入;本 PR 保持 draft,不由 agent 翻 ready。


Generated by Claude Code

…ide the default run

The published upgrade skill said the command "writes nothing but --out" at
the Quickstart comment and the failure-mode row, and that it "does not
rewrite your source files". Since `--write` landed, that is true only of
the default run. Every sentence stating what the command writes now holds
for both routes: the default run lists the mechanical edits and writes only
the `--out` snapshot; `--write` rewrites in place each edit it can trace to
one literal in one project file, lists every other with the reason it was
not written, never writes a semantic change, and on a disagreeing re-run
restores every file and exits 1.

Token ratchet paid in content, not wrapping: the duplicated `--out` recheck
block (its first line was byte-identical to Quickstart step 1), the
"in memory" clause the mechanism paragraph already states, and the
stored/authored exclusivity sentence the stored-only-flag row already
carries. 24772 -> 24748 bytes, 6193 -> 6187 tokens, 488 -> 484 lines.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0181E4ZeZmWyknawnauxD2CE
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 9bb1001480d1edc20ff9b32b0d77c3aa3a7b6c09
Local-runs: none

Inputs: card #22120 (body and thread), the diff of 9bb10014 against its merge-base db4c45b8c3 (one file, skills/objectstack-upgrade/SKILL.md, +13/−17), the PR body, and the head's check-runs read through the API. Nothing built, run or re-run locally; the token figure below is arithmetic on the blob (24,748 bytes ⇒ ceil(/4) = 6187, ceiling 6193). Reviewed adversarially: every sentence the diff adds about what os migrate meta writes was traced to packages/cli/src/commands/migrate/meta.ts and packages/cli/src/utils/authored-source-codemod.ts on db4c45b8c3, and every deletion was checked for a surviving home in the same file.

① Derived judgments

  • Accept set and public surface: unchanged. No package source, schema or export moves; the diff is published teaching text. Clause-②: no is correct for the reason the claim gives (a documentation card).
  • Governed-text face, claim by claim:
    1. "By default os migrate meta rewrites no source file. It lists the mechanical edits and writes only the --out JSON snapshot." — true: write defaults to false (meta.ts :657-664); the authored-source mode writes the --out snapshot only. The sentence makes no claim about a run with nothing to migrate, so the in-flight fix(cli): os migrate meta --out writes its snapshot on a run with nothing to migrate #22121 cannot falsify it. PASS.
    2. "--write rewrites in place each edit it can trace to one literal in one project file" — true: the flag description and the codemod docblock's proof rule (one object or array literal in one project file, statically matching the loaded value, no second reference to any binding the walk crossed). PASS.
    3. "lists every other with the reason it was not written" — true: printWriteOutcome prints not written [kind]: reason per refused site over a closed CodemodRefusalKind set. PASS.
    4. "never writes a semantic change" — true: the flag description ("Never writes the manual (semantic) changes."); the planner is not handed todos. PASS.
    5. "if re-running the chain over the written files disagrees, restores every file and exits 1" — true: outcome status restored puts every file back and this.exit(1) fires whenever write.status !== 'written' (meta.ts :828, :859). The unwritten exit (a file changed on disk between read and write; nothing written, exit 1) is not described — an omission, not a false claim. PASS.
    6. The Quickstart comment "(writes only --out; --write also rewrites the sites it can prove)", the table line --write # rewrite the proven sites in place, the failure-mode row ("the default run only lists" / "Pass --write, or port the printed edits by hand; then replay from the target major to confirm 0 changes") and the stored-only row tail ("writes only --out and, with --write, the sources") — true of both routes; the replay-to-confirm instruction survives in the row. PASS.
  • Deletions and their homes: (a) the --out recheck code block — the recheck is carried by Quickstart step 3 (applied must be []), the §3.3 callout and the failure-mode row "Replay from the target major still applies changes"; its --out command line is Quickstart step 1 byte for byte. Survives. (b) "It rewrites the loaded stack in memory and reports the diff" — the mechanism paragraph above it (load, normalise without the load-time conversion pass, replay each major as a chain hop, parse) and the "Applied N mechanical change(s) … This is the diff, already attributed" bullet carry the fact; the words "in memory" are gone. Survives. (c) "The authored-source flags and the stored-only flags are mutually exclusive, and mixing them is refused rather than ignored" — the stored-only-flag direction is carried by the failure-mode row; the authored-flag-with---stored direction now survives only for --from ("--stored takes no --from"). The CLI refuses that mix loudly (oclif exclusive, exit 2), so the reader is not misled by silence; judged acceptable at a 6-token headroom and recorded here as the one deletion with a partial home — not a rule lost, not a reason to withhold.
  • Ratchet: 6193 → 6187 tokens (24,772 → 24,748 bytes), paid by the three deletions, no re-wrap counted; the gate's own line is quoted in the PR body and the arithmetic on the blob agrees.
  • Vocabulary against the spec sentence (feat(cli): os migrate meta --write — the AST codemod that rewrites authored sources for the mechanical applied set (v18) #9591 remainder 1): "lists the mechanical edits" matches "to list the mechanical edits for existing sources"; no unqualified "automatically". PASS.
  • Siblings: references/examples-upgrade.md:57 and evals/protocol-major-upgrade.json untouched; every must_contain anchor (os migrate meta --from 16, --out, tsc --noEmit, REPORT.md, applied) is present in the new blob (11 / 7 / 2 / 2 / 3 hits). Frontmatter unchanged, so no generated listing moves.

② Semver level

No package touched; skills/** ships by npx skills add / npm create objectstack, not through any package files[]. No changeset is the correct declaration; skip-changeset is on the PR and Check Changeset concluded success on this head. Consistent.

③ Boundary flags

Implemented-by: claude/issue-22120-upgrade-skill-migrate-meta-write
Reviewed-by: session_0181E4ZeZmWyknawnauxD2CE

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

维护者速读(终稿)

改了什么:只改一份对外发布的技能 skills/objectstack-upgrade/SKILL.md(+13/−17,30 行)。原文三处断言 os migrate meta「只写 --out、不改源文件」;自 --write(PR #22108)落地后这只对默认运行成立。现在每一句「命令写什么」都同时对默认运行与 --write 成立:默认只列出机械修改、只写 --out 快照;--write 只就地改写能证明来源的站点(一个项目文件里的一个字面量),其余逐条列出未写原因,语义修改永不写,复跑不一致则恢复全部文件并以 1 退出。

为什么改:客户项目的 agent 整包加载这份技能;读到「只写 --out」就永远不会用 --write,读到无条件的「自动改写」又会被误导。措辞与 CLI 源码(meta.ts 的 flag 描述、written | restored | unwritten 三态、exit 1)一致,也与 spec 侧退休句的「列出机械修改」一致;#9591 的 spec 半边独立落地,互不等待。

风险与代价(含回滚):纯文本,无代码、无 changeset、无生成物(frontmatter 未动,check:skill-docs 绿)。token 棘轮 6193 → 6187(上限 6193),靠删三处重复内容支付,未删规则、故障行或命令;其中「authored 与 stored 两组 flag 互斥、混用即拒」一句删后只剩单向表述,CLI 本身会响亮拒绝(exit 2),席位判可接受并记入契约复核。#22121(--out 空跑行为)与 #22115(语义通知列表)在 meta.ts 上并行,本文未对这两点做断言。回滚 = revert 一个提交。

席位意见:ACCEPT;席内契约复核 PASS(记录 6046687007,head 9bb10014)。建议批准:它把一份出货中的技能从「说错」改回「说对」,净减 24 字节。同文件的下一张卡 #22123(--from 10 低于支持底线 16 的过时示例)排在本 PR 之后。

你要做的(一个动作):以授权账号 APPROVE 本 PR(或亲手合入)。PR 保持 draft;获批后本席翻 ready、挂 auto-merge 入队并跟到 MERGED。

akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 9, 2026
…e support floor (objectstack-ai#22145)

Fixes objectstack-ai#22123

Clause-②: no

The published upgrade skill's "several majors late" paragraph examples
`os migrate meta --from 10`. The chain refuses every `--from` below
`MIGRATION_SUPPORT_FLOOR = 16`
(`packages/spec/src/migrations/registry.ts:79`) with
`MigrationFloorError` — exit 1, `--json` `error: unsupported_from_major`
— and the same file's failure-mode row already says so. This PR rewords
the paragraph so its point (lateness is the designed-for case) is stated
with an example the chain accepts and with the floor in the same breath,
within the file's token ceiling. objectstack-ai#22120 is landed (PR objectstack-ai#22122, same
file); this branch cuts from `origin/main` after it.

## What changed (one file: `skills/objectstack-upgrade/SKILL.md`)

The paragraph under §0 "Establish the FROM major", before:

```text
Arriving several majors late is the designed-for case. `os migrate meta --from 10`
replays every step in order; there is no penalty for lateness and no requirement
to upgrade one major at a time.
```

after:

```text
Arriving several majors late is the designed-for case: `os migrate meta --from 16`
replays every step in order, with no requirement to upgrade one major at a time —
down to the chain's support floor, 16 today. Below the floor, upgrade to it by
the older route first, then run the chain.
```

The example is now the floor itself (the chain's lowest accepted
`--from`, and the spelling every other example in the file already
uses); the floor's value is stated where the reader decides `--from`;
the below-floor case carries the same remedy the failure-mode row and
the CLI's own refusal text give ("Upgrade to protocol 16 by another path
first, then re-run"). "There is no penalty for lateness" is dropped as
restating "the designed-for case". Nothing else in the file states a
`--from` below 16: `grep -rn -F -- '--from 10'
skills/objectstack-upgrade/` is now 0 hits (control: `--from 16` 12 hits
in `SKILL.md`), and `references/examples-upgrade.md` and
`evals/protocol-major-upgrade.json` never carried the example.

## Token ratchet — paid in content, not wrapping

`node scripts/check-skills-token-ratchet.mjs` (tokens = ceil(utf8 bytes
/ 4); ceiling for this file 6193):

| reading | lines | bytes | tokens | headroom |
|:--|--:|--:|--:|--:|
| before (`ef1fcb26a2`, the file as objectstack-ai#22122 left it) | 484 | 24748 | 6187
| 6 |
| after (`3e842f9fd8`) | 485 | 24736 | 6184 | 9 |

Whole package (the ten `skills/*/SKILL.md`): 4407 → 4408 lines, 52575 →
52572 tokens; the ratchet's authored-bundle total 144050 → 144047
(shipped tree 154836 → 154833). Diff: +6 / −5 lines. Gate line at
`3e842f9fd8`: `✓ check-skills-token-ratchet:
skills/objectstack-upgrade/SKILL.md is 6184 tokens (ceiling 6193;
headroom 9).`

The paragraph grew by +94 bytes (197 → 291). That is paid by deleting
two clauses the same file states beside them — no rule, failure-mode row
or command among them, and no line re-wrapped:

1. the trailing comment on `mkdir -p .upgrade` in §0's "Make the work
reviewable" block ("every artifact this skill produces lands here", −57
bytes) — the paragraph directly under the block opens "The `.upgrade/`
directory is the deliverable's workspace" and lists the artifacts;
2. the clause "which is exactly what the table above points at" closing
§2.1's "Not reachable from a consumer project" callout (−49 bytes) — the
callout's sentence already names `spec-changes.json` and the chain's
`--json` output, which are two rows of that table.

Net −12 bytes.

## Scope held

- One file, as the claim's file surface allows. No `packages/spec/**`,
no `packages/cli/**`, no `content/docs/**`;
`references/examples-upgrade.md` and `evals/protocol-major-upgrade.json`
untouched (neither carries the example; the eval's `must_contain` is `os
migrate meta --from 16`, which still holds). Frontmatter unchanged:
`check:skill-docs` and `check:skill-refs` green ("Skill docs in sync",
"9 generated files in sync").
- The floor's value is written as "16 today" the way the file already
writes `--from 16`, `^17` and "The 16 → 17 crossing"; the frontmatter's
`compatibility` line keeps the general statement ("the chain replays
from the spec's `MIGRATION_SUPPORT_FLOOR`"), and §2.1's reading prints
`supportFloor` from the installed spec. When the floor moves, this
sentence is one of the file's dated numbers, not the only one.

## Gates

Derived with `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` on this tree (24 families at `ef1fcb26a2`
and again at `3e842f9fd8`; identical to the PM's path-derived list). All
24 run, exit 0 each, recorded beside the printed command and reconciled
with `--ran` ("24 derived, 24 run, 0 NOT-MEASURED, 0 UNRUN", a derived
zero). `check:doc-formula-expressions` first exited 3 (prerequisite:
`@objectstack/formula` and `@objectstack/lint` not built) and was re-run
green after the prescribed build under the verify lock. Beyond the
derived list, also green: `check:skill-top-level-keys`,
`check:skill-frame-freshness`, `check:skill-refs`. NOT MEASURED:
`check:skill-examples` (prerequisite: the client SDK dist is not built;
outside the derived list, and this diff touches no SDK example).

## Acceptance notes

- Observation, not changed and not a card: neither this skill nor the
CLI's refusal text names what "the older route" / "another path" is for
a project below the floor (install the spec major that still reaches it
and run its chain, then continue from 16). The refusal is loud and the
remedy's direction is stated; naming the route is a product sentence for
the maintainer, not a drift fix. Carrier: none.

## 维护者速读(草稿)

**改了什么**:只改一份对外发布的技能文件 `skills/objectstack-upgrade/SKILL.md` 的一个段落。原文以
`os migrate meta --from 10` 作为"晚几个大版本再升级也没关系"的示例,但迁移链的支持下限是
16(`MIGRATION_SUPPORT_FLOOR = 16`),`--from 10` 会被 CLI
直接拒绝(`MigrationFloorError`,退出码 1)。现在示例改为链接受的 `--from 16`,同一句话里说明下限(今天是
16),并告诉低于下限的项目先用旧路线升到 16 再跑链——与同一文件故障表里已有的那一行一致。

**为什么改**:这份技能随每次 `npx skills add` 整包进入客户项目的 agent
上下文;一句教人运行会被拒绝的命令的话,是发布面上的错误句子(NORTH-STAR「优先级」第 4 条)。CLI
的拒绝挡住了实际伤害,但文字不改就一直是错的。

**风险与代价(含回滚)**:纯文本改动,无代码、无 changeset、无生成物变化。token 棘轮:6187 → 6184(上限
6193),净减 12 字节,靠删除两处紧邻处已有陈述的重复子句支付(一处代码块尾注,一处收尾从句),未删任何规则、故障行或命令,未折行。回滚即
revert 本 PR 的一个提交。注意:下限值"16"写成了具体数字,与文件里其它写死的 `--from 16`、`^17`
同样随下限移动而需要更新;frontmatter 与 §2.1 的读数保留了通用表述。

**席位意见**:(留空,席位定稿)

**你要做的**:`skills/**` 为 Tier H 受管面:请以授权账号 APPROVE 一次,或亲手合入;本 PR 保持
draft,不由 agent 翻 ready。

---
_Generated by [Claude
Code](https://claude.ai/code/session_01CXydFDyiQwNbGFkmwrcRQq)_

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 9, 2026
…does: lists the edits, `--write` applies the proven sites (objectstack-ai#22199)

Fixes objectstack-ai#22144

Clause-②: no

Two published lines in the objectstack-data skill said `os migrate meta
--from 16` strips the retired index keys `type` and `partial` ("to strip
them automatically" at `rules/indexing.md:31`, "strips them" at
`SKILL.md:377`). Neither was true of the tool: the default run writes no
authored source file, and `--write` rewrites only the sites it can
prove. Both lines now carry the house sentence. The closing enumeration
of every `os migrate meta` sentence in `skills/**` is below; every other
hit reads true and is left byte-for-byte alone.

Family: objectstack-ai#22120 is landed (PR objectstack-ai#22122, the upgrade skill) and objectstack-ai#22123 is
landed (PR objectstack-ai#22145, the `--from 10` example); this PR is the family's
closing card. objectstack-ai#9591 (PR objectstack-ai#22142, spec lane) is referenced only: it moves
the spec tombstones to the same sentence and holds the class pin.

## The tool, re-taken on this tree (`0aa5205228`, cut from `origin/main`
at `1e5d322c1e`)

- `packages/cli/src/commands/migrate/meta.ts:762-769`, the flag
declaration, verbatim:

  ```ts
  write: Flags.boolean({
    description:
'Rewrite the authored source files in place for each mechanical change
traced to one literal in one '
+ 'project file; every other change is listed with the reason it was not
written. Never writes the '
      + 'manual (semantic) changes.',
    default: false,
    exclusive: ['stored'],
  }),
  ```

- `packages/cli/src/utils/authored-source-codemod.ts` (module header):
`--write` writes a diff change only when its path leads, through the
authored modules' syntax, to ONE object or array literal in ONE project
file and the loaded value agrees with that literal; anything else is
refused by a named reason and stays on the list for the author.
- `packages/spec/src/migrations/registry.ts:79`: `export const
MIGRATION_SUPPORT_FLOOR = 16;` — `applyMetaMigrations` throws
`MigrationFloorError` for any `--from` under it (`meta.ts:713-716`).
- `packages/spec/src/conversions/registry.ts:3906`: `id:
'object-index-type-partial-removed'` — the 16 → 17 step carries the
conversion, so "lists the mechanical edits" is true of these two keys
specifically, not only in general.
- House sentence (`packages/spec/src/shared/retired-key.ts`: "It must be
TRUE of the tool"). The tombstones on `main` close with "Run `os migrate
meta --from 16` to list the mechanical edits for existing sources; apply
them by hand." (`packages/spec/src/data/object.zod.ts:558,567`); PR
objectstack-ai#22142 moves that to "… `--write` applies the ones it can prove, and you
apply the rest by hand."; the upgrade skill says "By default `os migrate
meta` rewrites no source file"
(`skills/objectstack-upgrade/SKILL.md:153`) and "`--write` … rewrite the
proven sites in place" (`:132`). No third vocabulary is introduced here.

## The two edits

`skills/objectstack-data/rules/indexing.md:31-33` (headroom 1058 tokens
before):

- before: "… run `os migrate meta --from 16` to strip them
automatically. What to do instead is the subject of "Access methods and
partial indexes" below."
- after: "… run `os migrate meta --from 16` to list the mechanical
edits; `--write` applies the ones it can prove, and you apply the rest
by hand. What to do instead is the subject of "Access methods and
partial indexes" below."

`skills/objectstack-data/SKILL.md:377-378` (ceiling 6128, headroom 0
before — the shortest form that stays true):

- before: "Both are now a `tsc` error and a parse error; `os migrate
meta --from 16` strips them. Access methods and partial predicates are
database-layer migrations."
- after: "Both are now a `tsc` error and a parse error; `os migrate meta
--from 16` lists the edits; `--write` applies the ones it can prove, the
rest by hand."

### Token ratchet payment (`node
scripts/check-skills-token-ratchet.mjs`, `ceil(utf8 bytes / 4)` per
file)

The new sentence in `SKILL.md` costs 63 bytes over the old one. It is
paid by deleting one clause the same section restates — never by
re-wrapping, never by touching the ceiling:

- deleted (`SKILL.md:377-378`): "Access methods and partial predicates
are database-layer migrations." (70 bytes with its leading space);
- where its content survives, same file: `SKILL.md:387-388` "See
rules/indexing.md for composite indexes, unique scope, and how to build
partial / gin / gist indexes at the database layer." and `SKILL.md:44`
"Index Strategy (rules/indexing.md) — btree/gin/gist/fulltext, composite
indexes, partial indexes"; and in `rules/indexing.md` § "Access methods
and partial indexes", the section the retirement callout points at;
- net: 24512 → 24506 bytes, 6128 → 6127 tokens, headroom 0 → 1. The
ceiling row in `scripts/check-skills-token-ratchet.mjs` is not touched
(see Acceptance notes).

## `skills/**` readings — lines, and tokens as the ratchet counts them

| File | Before | After |
|:--|:--|:--|
| `skills/objectstack-data/SKILL.md` | 469 lines · 24512 bytes · 6128
tokens (ceiling 6128, headroom 0) | 469 lines · 24506 bytes · 6127
tokens (headroom 1) |
| `skills/objectstack-data/rules/indexing.md` | 229 lines · 8729 bytes ·
2183 tokens (ceiling 3241) | 230 lines · 8805 bytes · 2202 tokens |
| Whole package — every `skills/**/SKILL.md` summed | 4408 lines ·
210279 bytes | 4408 lines · 210273 bytes |
| Whole shipped bundle — the ratchet's own "bundle total" line | 154833
tokens (the gate run in a detached worktree at `1e5d322c1e`) | 154851
tokens (+18: −1 and +19) |

No eval under `skills/objectstack-data/evals/` quotes either sentence
(`grep -rn strip skills/objectstack-data/evals/` — 0 hits). Frontmatter
untouched; `check:skill-docs` and `check:skill-refs` both report in
sync.

## Closing enumeration — every `os migrate meta` sentence in `skills/**`

`git grep -n "os migrate meta" -- skills/` on `0aa5205228`: 30 hits in 5
files (`objectstack-data/SKILL.md` 1,
`objectstack-data/rules/indexing.md` 1, `objectstack-upgrade/SKILL.md`
21, `objectstack-upgrade/evals/protocol-major-upgrade.json` 5,
`objectstack-upgrade/references/examples-upgrade.md` 2) — the same lines
as on `origin/main`, since only the two data-skill lines moved. A
widened `git grep -n "migrate meta" -- skills/` adds one bare hit
(`objectstack-upgrade/SKILL.md:466`), judged too. Three tests per hit:
(a) the default run lists and writes no source; (b) `--write` rewrites
only the provable sites; (c) `--from N` is at or above
`MIGRATION_SUPPORT_FLOOR` (16). A line is changed only when it is false.

| File:line | Sentence (abridged) | Reading | Changed? |
|:--|:--|:--|:--|
| `objectstack-data/SKILL.md:377` | "`os migrate meta --from 16` strips
them." | FALSE on (a) and (b): the default run strips nothing; `--write`
strips only the proven sites | yes |
| `objectstack-data/rules/indexing.md:31` | "run `os migrate meta --from
16` to strip them automatically." | FALSE on (a) and (b) | yes |
| `objectstack-upgrade/SKILL.md:64-66` | Quickstart: `--from 16 --step`,
`--from 16 --json`, `--from 16 --out …`, under the comment "replay the
chain (writes only --out; --write also rewrites the sites it can prove)"
| true: (a) listing runs, `--out` is a snapshot and not a source; (b)
stated; (c) 16 | no |
| `objectstack-upgrade/SKILL.md:74` | "`os migrate meta --from 17
--json` — `applied` must be []" | true: the replay from the target
major; (c) 17 ≥ 16 | no |
| `objectstack-upgrade/SKILL.md:103` | "`os migrate meta --from 16`
replays every step in order … down to the chain's support floor, 16
today" | true: (c) the floor constant is 16 | no |
| `objectstack-upgrade/SKILL.md:124-133` | "What `os migrate meta`
actually does" and its command block (`--out` "write the canonicalized
stack", `--write` "rewrite the proven sites in place") | true on (a),
(b), (c) | no |
| `objectstack-upgrade/SKILL.md:153` | "By default `os migrate meta`
rewrites no source file … `--write` rewrites in place each edit it can
trace to one literal in one project file, lists every other with the
reason …" | true: matches the flag description | no |
| `objectstack-upgrade/SKILL.md:177-179` | `--stored` preview, `--stored
--type …` narrowing, `--stored --apply --yes` | true of the stored pass
(`write` is `exclusive: ['stored']`; `--apply` is the only writer there)
| no |
| `objectstack-upgrade/SKILL.md:215` | "`os migrate meta --from N
--json` → `.specChanges`" | true: the JSON face carries `specChanges`
(`meta.ts:881,928`); N is bounded by the floor sentence at `:103` | no |
| `objectstack-upgrade/SKILL.md:313` | "`os migrate meta --from 16
--json`" piped into `node -e`, reading `.todos` | true: (a) a listing
run; `todos` is the key (`meta.ts:459`) | no |
| `objectstack-upgrade/SKILL.md:404` | "`os migrate meta --from
TARGET_MAJOR --json` — `applied` must be []" (the placeholder is spelled
out here) | true: (a); (c) by construction | no |
| `objectstack-upgrade/SKILL.md:449` | "`os migrate meta --stored
--json` — 0 = every row canonical, 1 = work left" | true of the stored
pass: read-only, exit 1 while rows are pending (`storedMigrationClean`)
| no |
| `objectstack-upgrade/SKILL.md:466` | "`migrate meta` reports changes,
but the files are unchanged — working as designed, the default run only
lists — pass `--write`, or port the printed edits by hand" | true on (a)
and (b) | no |
| `objectstack-upgrade/evals/protocol-major-upgrade.json:7` | "`--step`,
`--json`, and `--out …` — the command rewrites nothing on disk, so port
the printed edits into the sources" | true of the invoked forms (none
carries `--write`; `--out` writes a new snapshot and rewrites no source)
| no |
| `objectstack-upgrade/evals/protocol-major-upgrade.json:10,20` |
`must_contain` token lists | not behaviour claims | no |
| `objectstack-upgrade/evals/protocol-major-upgrade.json:17` | "runs `os
migrate meta --from AUTHORED_MAJOR` to attribute the site to its
`conversionId`, ports `requiredWhen` …" (placeholder spelled out) |
true: (a) the listing attributes; the port is by hand | no |
| `objectstack-upgrade/evals/protocol-major-upgrade.json:37` | "Replays
`os migrate meta --from 17 --json` and reads `applied` … runs `os
migrate meta --stored` (read-only) then `--stored --apply --yes`" | true
on (a), (c), and of the stored pass | no |
| `objectstack-upgrade/references/examples-upgrade.md:57` | "Ported into
sources from `os migrate meta --out`." | true: hand-ported from the
snapshot | no |
| `objectstack-upgrade/references/examples-upgrade.md:79` | "`os migrate
meta --stored --apply` — rows rehydrate correctly today; this makes it
durable." | true of the stored pass | no |

## Local verification (tree `0aa5205228`; every exit code captured
before any pipe)

Derived with `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` (no paths; the tool took the changeset from
the merge-base itself): 24 commands. Run after the final commit as one
union with the tool's own loop idiom, recorded as `cmd :: exit N`, and
reconciled: `dispatch-gates --ran` — "24 derived, 24 run, 0
NOT-MEASURED, 0 UNRUN". All 24 exit 0:

`check-ci-filter-parity` · `check-closing-keyword-parity` (+
`--self-test`) · `check-comment-mask-corpus` · `check-doc-route-spelling
--advisory` (+ `--self-test`) · `check-skills-token-ratchet` (+
`--self-test`) · `@objectstack/lint check:doc-formula-expressions` ·
`@objectstack/spec check:skill-docs` · `check:agent-test-spelling` ·
`check:corpus-claim-drift` · `check:cross-package-test-inputs` ·
`check:doc-authoring` · `check:driver-memory-census` ·
`check:gitlink-declared` · `check:nul-bytes` ·
`check:pm-governed-merges` · `check:refd-timer-probe` ·
`check:role-word` · `check:skill-compatibility` ·
`check:skill-frame-sync` · `check:skill-identifier-liveness` ·
`check:watch-hint-literal`.

- `check:doc-formula-expressions` first answered exit 3 (PREREQUISITE
NOT MET — `@objectstack/formula` and `@objectstack/lint` were unbuilt;
nothing measured). After `pnpm exec turbo run build
--filter=@objectstack/formula --filter=@objectstack/lint
--concurrency=2` behind `scripts/pm/os-verify-lock.sh` (VERDICT
command-exit 0; held 92 s, waited 0 s) it exits 0.
- Also run, outside the derivation: `pnpm --filter @objectstack/spec run
check:skill-refs` exit 0 ("9 generated files in sync"); `pnpm
check:skill-top-level-keys` exit 0.
- NOT MEASURED locally, by design: the type-check lanes, the Test Core
shards, the 11 wide-population families and the 51 roster families the
derivation names are CI's; the 15 pending-changeset families do not
apply (no changeset — this diff publishes nothing from any released
package; label `skip-changeset`).

### Reverse verification of the ratchet payment (one-off; committed
first; `scripts/ablation-replace.mjs`)

- Attempt 1 was a no-op by the tool's own count check (the anchor was a
substring of its replacement, anchor count 1 → 1): refused and restored,
nothing measured.
- Attempt 2, re-anchored on "prove, the rest by hand." with the deleted
clause re-added: mutation landed on disk (blob `7220a34363d3` →
`efe8f6825326`); `node scripts/check-skills-token-ratchet.mjs` exit 1 —
"`skills/objectstack-data/SKILL.md` is 6144 tokens; the ratchet ceiling
is 6128 (over by 16)". Restore proven: blob == HEAD `7220a34363d3`, `git
diff HEAD` empty, `git status --porcelain` empty.

## Acceptance notes

- The class pin
`packages/spec/src/shared/retired-key-migrate-sentence.test.ts`
(`WITHDRAWN_CLAIM`) matches only "to rewrite … automatically" and the
"rewrites (it for you / existing sources / authored sources / your
sources / your source files)" spellings — no "strip" — so it never saw
these two lines, and nothing in this PR is implied to be covered by it.
Widening it is spec-lane work and the file is held by PR objectstack-ai#22142; it is
not touched here. Carrier: the `domain:spec` seat, via the skills seat's
relay.
- `skills/objectstack-data/SKILL.md` now sits 1 token under its
unchanged 6128 ceiling. Lowering the ceiling to 6127 is legitimate per
the gate's own header and outside this card's file surface. Carrier: the
next PR that touches `scripts/check-skills-token-ratchet.mjs`, or the
skills seat.
- Tier H (`skills/**`): this PR stays draft and lands on an authorized
APPROVED review or the maintainer's hand; no seat flips it ready.

## 维护者速读(草稿)

**改了什么**:objectstack-data
技能里两句话(`rules/indexing.md:31`、`SKILL.md:377`)原本说 `os migrate meta --from
16` 会"自动剥掉"/"剥掉"已退役的索引键 `type` 和 `partial`。改成工具的真实行为:默认只列出机械修改;`--write`
只落它能证明的那些站点;其余由作者手工完成。顺带把 `skills/**` 里所有 `os migrate meta`
句子逐条核对了一遍(上表),其余均属实,一字未动。

**为什么改**:这是对外发布的技能包(`npx skills add` 原样装进客户项目),读到这句的 AI
作者会以为源码已被清理,把退役键留在原地,直到 `tsc` 或解析报错才发现。`docs/NORTH-STAR.md` 优先级第 4
条:发布面上的错句就是产品缺陷;`retired-key.ts` 的内部裁决是"这句话必须对工具为真"。

**风险与代价(含回滚)**:纯文本改动,不碰代码、不碰 spec、不发包、无 changeset。`SKILL.md` 已顶在 token
上限,新句子靠删掉同一段里被下文重述的一句付账(内容仍在 `SKILL.md:387-388` 与
`rules/indexing.md`)。回滚 = revert 这一个提交。

**席位意见**:

**你要做的**:确认两句新措辞与 PR objectstack-ai#22142 正在采用的 house sentence 一致;同意则 APPROVE,由席位落地。

---
_Generated by [Claude
Code](https://claude.ai/code/session_01CXydFDyiQwNbGFkmwrcRQq)_

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 9, 2026
…its it can prove, you apply the rest (objectstack-ai#22142)

Fixes objectstack-ai#9591
Clause-②: no (prescription text only; no input's accept or reject result
changes)

This is the spec-lane half of the card: the shared retirement sentence
names `--write`, and the class-wide pin moves in the same PR. The
codemod itself landed in PR objectstack-ai#22108 (`a959493cdf`).

## The sentence

Before (the objectstack-ai#9529 wording):

```text
Run `os migrate meta --from N` to list the mechanical edits for existing sources; apply them by hand.
```

After:

```text
Run `os migrate meta --from N` to list the mechanical edits for existing sources; `--write` applies the ones it can prove, and you apply the rest by hand.
```

The one allowed two-clause variant (a conversion that covers only part
of a value) carries the same clause: `… to list the mechanical edits for
the X case; --write applies the ones it can prove, and
WHAT-HAPPENS-TO-THE-REST.` Its two members are dashboard
`compareTo.offset` and the script node's `config.actionType`.

**Checked against the tool on `main` (`51290bca`).**
`packages/cli/src/commands/migrate/meta.ts` declares `write:
Flags.boolean({ … default: false, exclusive: ['stored'] })`. Its help
text says it rewrites the authored sources in place "for each mechanical
change traced to one literal in one project file; every other change is
listed with the reason it was not written". Without the flag the run
writes only the `--out` snapshot. The wording satisfies every ruling
that binds it:

- **Triage `6045697201`.** The sentence never says "rewrite existing
sources automatically" unqualified ("the ones it can prove"), and it
names `--write` because the default run still only lists.
- **"It must be TRUE of the tool."** Every clause is a property of the
command, read from `meta.ts`.
- **"One antecedent."** "existing sources" still names one thing. "The
ones" can only be edits, because an edit is what gets applied. The key's
fate stays in the body prose.
- **Vocabulary.** The wording matches PR objectstack-ai#22122's skill text ("lists the
mechanical edits"; `--write` "rewrites in place each edit it can trace
to one literal in one project file, lists every other with the reason it
was not written").

## Where it moved (counted at `017761f0`; the merge of `main` added no
site)

- **161 sentences the pin judges.** `packages/spec/src`: 157 (155 house
form, 2 two-clause). `packages/lint/src`: 1.
`packages/drivers/driver-turso/src`: 3. Every one passes the new
anchors; `apply them by hand` survives only in the pin's own RED
fixtures.
- **Not judged by the pin, moved anyway:**
- `migrations/registry.ts`: 3 sentences, regenerated from the moved
`entries/semantic/18.*.ts` by `gen:migration-registry`.
- The lint `chartConfig.xAxis.field` hint in
`validate-widget-bindings.ts`: a template literal with interpolation
after the sentence, so the pin cannot see it (Acceptance notes).
  - The `retiredKey()` docblock example.
- **Mechanical replacement.** A script replaced the tail `apply them by
hand` in 63 files (188 occurrences; old tail left: 0) and printed
per-file before/after counts. Two seams split mid-phrase
(`translation.zod.ts`) and the two two-clause sites were rewritten by
anchored edits that had to hit exactly once.
- **Pins in other test files.** 23 test files asserted the old sentence
verbatim, as string or regex. Each now asserts the new sentence
verbatim, so a revert reds them. The ones that assert only the unchanged
prefix (`form-layout-inline-grid-retired.test.ts`, the turso /
driver-memory `toContain('os migrate meta --from 17')`) are untouched,
because they stay true.
- **Changeset.** `.changeset/9591-retirement-sentence-write.md`: `patch`
for `@objectstack/spec`, `@objectstack/lint` and
`@objectstack/driver-turso`, the three packages whose shipped text
moves.
- **Generated.** `content/docs/references/**`: 32 files (+268/−268) from
`pnpm --filter @objectstack/spec check:generated --fix`; `check:docs`
was the only stale artifact. `check:generated` then exited 0.

## The class pin (`retired-key-migrate-sentence.test.ts`)

- **Anchors.** `HOUSE_AT_MARKER` and `MIXED_AT_MARKER`, and their
markdown twins, require the new clause. The objectstack-ai#9529 sentence, which does
not name `--write`, is now RED. Two further spellings are RED: one that
names `--write` without the qualification, and one that qualifies it but
leaves the rest unowned.
- **Withdrawn claim.** `WITHDRAWN_CLAIM` is unchanged: the unqualified
automatic-rewrite claim stays a hard RED everywhere. A new non-vacuity
case proves neither legal shape trips it.
- **Truth anchor (new).** The pin reads `os migrate meta`'s own flag
table. A `write` boolean flag must exist and must have `default: false`,
the two facts the sentence rests on. The read is covered by
`@objectstack/spec`'s existing `packages/**/*.ts` cross-package
declaration.
- **Corpus widened by one root.** `packages/drivers/driver-turso/src`
joins, on objectstack-ai#7030's terms. Its three `turso` config tombstones carry the
house sentence, and their docblock defers to `retired-key.ts`, but the
pin never walked them. Without this, a rewording leaves them behind with
every assertion green. The lint-only anti-vacuity case now covers each
widened corpus.
- **Header and docblock.** The pin header and the `retired-key.ts`
module docblock record the new sentence and why. The "the claim may be
restored" note is gone, replaced by what was restored and how far.

**Reverse verification**, run from committed HEAD `017761f0` through
`scripts/ablation-replace.mjs` (each anchor hit as declared and was
restored to a blob equal to HEAD with `git diff HEAD` empty). Expected
direction: red.

| Mutation | Result |
|:--|:--|
| A. the three `turso.zod.ts` sentences back to the objectstack-ai#9529 wording
(anchor ×3→0, blob `e25cca4d5508`→`a05fe3f09733`) | 3 failed / 12
passed, naming `driver-turso:spec/turso.zod.ts:63`, `:75`, `:82` |
| B. `meta.ts` `write` flag `default: false` → `true` (blob
`c036012c63c9`→`71acff21ef8c`) | 1 failed / 14 passed: "the sentence is
TRUE of the command it names" |
| C. `meta.ts` flag renamed `write` → `inPlace` (blob
`c036012c63c9`→`29a8a9402284`) | 1 failed / 14 passed: "os migrate meta
declares no `write` boolean flag" |

Under the old corpora, mutation A would have stayed green, because
driver-turso was in no corpus.

## One bounded fix on the same sentences: `CHATTER_POSITION_RETIRED`

The three `record:chatter` / `record:discussion` `position` value
prescriptions (`'sidebar'`, `'inline'`, `'drawer'`, in
`ui/component.zod.ts`) told the author to run a bare `os migrate meta`.
The command refuses that with `Missing required flag --from` (`meta.ts`
`run()`, the `flags.from === undefined` branch). The conversion is
`record-chatter-position-vocabulary`, `toMajor: 18`, so they now name
`--from 17`. They therefore join the pin's judged set in house form, and
the "(registered under protocol major 18)" aside goes. The fix qualifies
as bounded: the same sentence class, a mechanical change to an
already-pinned form, a file inside this claim's surface, and the same
gate family. No test pinned the old text.

## Governed surface: `.claude/skills/spec-property-retirement/SKILL.md`
(Tier S)

The pin requires the retirement playbook to teach both shapes
(`SKILL_HOUSE_TEMPLATE` and `SKILL_MIXED_TEMPLATE` must match its
convention 5). So changing the sentence forces the playbook edit, and
this PR lands as Tier S. Convention 5 now carries the two new templates.
Its note that the command "never writes a source file" was made false by
PR objectstack-ai#22108, so it is deleted. Line count 337 → 337 (ceiling 337), with
every line within the 120-byte budget: `node
scripts/pm/check-skill-line-ratchet.mjs` exits 0. ⛔ No published
`skills/**` file changes: PR objectstack-ai#22122 owns
`skills/objectstack-upgrade/SKILL.md`, and no published skill carries
the sentence (`git grep` count 0).

## 维护者速读(草稿)

**改了什么**:所有退役键报错末尾那句统一提示,从「运行 `os migrate meta --from N`
列出机械修改,然后手工改」改为「……列出机械修改;`--write` 会写入它能证明的那些,其余你手工改」。共 161 处被 pin
判定的报错文案(含 2 处两从句变体),外加生成的 registry 3 处、lint 模板字符串 1 处;其中 3 处原先写成不带
`--from` 的命令(该命令会直接拒绝),一并改正。守这句话的 pin 同步更新,并新增一条断言:CLI 必须真有 `--write`
且默认不写。

**为什么改**:`--write` 已随 PR objectstack-ai#22108 落地,旧句只说「手工改」,低估了工具;但 `--write`
只写能证明的站点,所以不能说「自动重写源文件」。新句两头都如实。

**风险与代价(含回滚)**:纯文案,不改任何 schema、键、类型、导出或错误码;解析结果不变。依赖旧整句原文匹配的调用方会失配(仓内 23
个测试已同步);前缀「…for existing sources;」不变。回滚即 revert 本 PR。在途的兄弟 PR
若新增处方仍用旧句,会被 pin 打红,后落地者改用新句。

**席位意见**:

**你要做的**:无需操作;本 PR 触 `.claude/**`(Tier S),由席位按合同审查记录落地。

## Verification (branch base `51290bca`; final head `f9ca14d548`)

- `pnpm --filter @objectstack/spec build`: VERDICT command-exit 0.
`check:generated --fix` regenerated the one stale artifact;
`check:generated` then exited 0 (15 of 15 current), and again in the
gate run at `f9ca14d548`.
- spec `vitest run --project local`: Test Files 623 passed (623), Tests
18613 passed, 1 todo, at `017761f0`.
- spec `vitest run --project repo` (53 files incl. the class pin): 53
passed (53), Tests 903 passed (903), at `017761f0`.
- `@objectstack/driver-turso` `vitest run`: Test Files 88 passed (88),
Tests 2373 passed, 33 skipped, at `017761f0` (after `pnpm
--workspace-concurrency=2 --filter '@objectstack/lint...' --filter
'@objectstack/driver-turso...' build`; the first run, before that build,
could not resolve unbuilt dependencies and is NOT MEASURED, not red).
- `typecheck` for spec (`tsc --noEmit` + `check:scripts-typecheck` +
`check:test-typecheck`), lint and driver-turso: exit 0 at `017761f0`.
- **After merging `main`** (`033e5c536d`: objectstack-ai#22122, objectstack-ai#22127, objectstack-ai#22106) as
`f9ca14d548`, with no conflict (objectstack-ai#22127 also edits
`validate-expressions.ts`): the class pin 15 passed (15);
`@objectstack/lint` `vitest run`: Test Files 123 passed (123), Tests
5688 passed (5688); lint `typecheck` exit 0.
- `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` at `f9ca14d548` derives 124 commands (the
claim-time 79 plus 45). All 124 exit 0 at `f9ca14d548`. `--ran`
reconciliation: 124 derived, 124 run, 0 NOT-MEASURED, a zero derived
from the recorded exit codes. (At `017761f0`, five gates first answered
exit 3, PREREQUISITE NOT MET: one shallow-clone fixture and four that
need unbuilt dists. The clone was deepened as the gate asked, and all
five are green in the `f9ca14d548` run.)
- `node scripts/pm/check-skill-line-ratchet.mjs`: exit 0; the playbook
is 337 lines (ceiling 337), and no line is over 120 bytes.
- eslint, narrowed: `pnpm exec eslint --no-inline-config --format json`
over the 66 changed `.ts` files reports 66 files, 0 errors and 0
warnings. The population is `eslint.config.mjs`'s
`**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}` glob, which excludes the changed
`.md` / `.mdx` files. The config never enables type-aware linting (its
own comment at `:326`–`:328`), so this diff cannot move any untouched
file's verdict. The repo-wide `pnpm lint` is CI's.

## Siblings in flight

objectstack-ai#21982, PR objectstack-ai#22094 (objectstack-ai#13458) and PR objectstack-ai#22103 (objectstack-ai#5082) each add prescriptions
with today's sentence. Whichever lands after this one carries the new
sentence; the class pin reds it at that merge otherwise. Whichever of
those lands first, this branch merges `main` before landing.

## Acceptance notes

- **Hand-written docs still use the old sentence**
(`content/docs/automation/flows.mdx` ×2,
`protocol/objectql/query-syntax.mdx`, `data-modeling/queries.mdx`,
`protocol/objectui/actions.mdx`, `ui/apps.mdx` ×2). Each is the page's
own advice, not a quoted error, and still true of the default run; each
undersells `--write`. They are `domain:devx` pages outside this claim,
so they are not touched here.
- **QA checklist item `cli.migrate-meta-codemod`**
(`docs/qa/platform-checklist/areas/cli.json`). Its RESTART CHECK fired
when PR objectstack-ai#22108 added `--write`, and the item still asserts a print-only
command. Its step 1 greps for the objectstack-ai#9529 sentence verbatim and now finds
none. Re-authoring the item belongs to the checklist author, not this
PR.
- **Comments that say the default run "lists the mechanical edits"**
stay as they are, because they are still true: the
`migrations/registry.ts` migration notes (outside the pin's scope by
design) and the `conversions/registry.ts` comments.
- **The lint `chartConfig.xAxis.field` hint**
(`validate-widget-bindings.ts`) moved, but it remains invisible to the
class pin: a template literal, with `suggestName(…)` and the suppress
hint interpolated after the sentence.
- **A published skill still claims an automatic strip.**
`skills/objectstack-data/rules/indexing.md:31` says "run `os migrate
meta --from 16` to strip them automatically", and
`skills/objectstack-data/SKILL.md:377` says the command "strips them".
The default run strips nothing from sources, and `--write` strips only
what it can prove. `WITHDRAWN_CLAIM` has no strip spelling, so the pin
cannot see this. Widening it here would red `main` on a Tier H file this
PR may not touch, so it is reported to the PM for the skills lane.
- **The `config.actionType` two-clause tail** ("the stub and marker
values are removed") is unchanged in substance; only the `--write`
clause was inserted before it.


## Patch round 1 (written by the PM seat from the dev's report
`6052088494`)

- **Merge:** `origin/main` `ef1fcb26a2` (PR objectstack-ai#22103) was merged through
`os-regen-merge.sh` as `feca6b5ace`. The three reference pages both
sides had changed (`api/metadata`, `data/object`, `system/migration`)
were regenerated from the merged tree as `98e2f6e373`. That brings back
objectstack-ai#22103's `unique?: false | 'global' | 'organization'` rows, which the
driver had dropped.
- **objectstack-ai#22103's sites:** the merge brought two non-test sites with the old
tail and one test that asserts it verbatim. All three carry the new
sentence at `b9d6e82619`:
- `packages/spec/src/data/object.zod.ts`
(`DECLARED_INDEX_BARE_TRUE_RETIRED`);
- `packages/lint/src/data-model-rules.ts` (the
`unique-unscoped-declared-index` fix text);
  - `unique-scope-message.test.ts`.

The pin now judges 163 sentences: `spec` 158 (156 house + 2 two-clause),
`lint` 2, `driver-turso` 3.
- **The pin's blind spot:** the `data-model-rules.ts` sentence sat in a
template literal, which the judge cannot read (escaped backticks), so it
was never judged. It is now plain-quoted, as in
`validate-expressions.ts`, and the pin's Mechanism paragraph records
that template literals are invisible to the scan. Ablation D (that
sentence back to the old tail) gives 3 failed / 12 passed, naming
`lint:data-model-rules.ts:463`. `validate-widget-bindings.ts` stays the
one template-literal site the pin cannot judge (an Acceptance note).
- **Verification at `b9d6e82619`:**
  - spec `--project local`: 623 files / 18,619 tests;
  - spec `--project repo`: 53 / 903;
  - lint: 123 / 5,689;
  - driver-turso: 88 / 2,373;
  - typecheck: exit 0 for all three packages;
  - `dispatch-gates --ran`: 124 derived / 124 run / 0 NOT-MEASURED;
  - CI: 33 success, 2 expected skips.


## Patch round 2 (written by the PM seat from the dev's report
`6053397952`; claim revised `6052335087`)

- **Why:** PR objectstack-ai#22094 (objectstack-ai#13458, `fec87e7e07`) landed first with a
two-clause prescription lacking the `--write` clause, which this PR's
class pin refuses. The sibling rule here ("whichever lands later carries
the new sentence") puts the edit in this PR.
- **Merge:** `origin/main` `959c209d56` was merged through
`os-regen-merge.sh` as `3b6335b9be`, with no hand-written conflict.
`content/docs/references/api/protocol.mdx` was regenerated as
`c40b3babd7`.
- **The edit** (`fe3af5642c`, 4 files beyond the merge):
- `packages/spec/src/kernel/manifest.zod.ts`
`PLUGIN_PERMISSIONS_LIST_FORM` now closes with "Run `os migrate meta
--from 17` to list the mechanical edits for the package manifest case;
`--write` applies the ones it can prove, and a granted-permission record
is not a source it reads." It keeps objectstack-ai#13458's own second clause and adds
the house `--write` clause, the seat's wording.
- Its verbatim pin `manifest-permissions-string-list.test.ts` moved with
it.
- The changeset's two-clause bullet now names three members and says
"before their second clause".
- **The two-clause variant now has three members:** dashboard
`compareTo.offset`, the script node's `config.actionType`, and the
package manifest `permissions` case. The pin judges 164 sentences (spec
159 = 156 house + 3 two-clause; lint 2; driver-turso 3) with 0 bad
sites.
- **Verification at `fe3af5642c`:**
  - spec `--project local`: 625 files / 18,661 tests;
  - spec `--project repo`: 53 / 903;
  - class pin: 15 / 15, and the manifest pin: 17 / 17;
  - `dispatch-gates --ran`: 124 / 124 / 0 NOT-MEASURED;
  - CI: 33 success, 2 expected skips.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01LAi5BVvQNiYzepSAcsoFLK)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

skills(objectstack-upgrade): the upgrade skill says os migrate meta "writes nothing but --out", which --write (PR #22108) now makes incomplete

3 participants