Repository navigation
docs(skills): objectstack-upgrade names os migrate meta --write beside the default run - #22122
Conversation
…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
Contract reviewServed-tier: Inputs: card #22120 (body and thread), the diff of ① Derived judgments
② Semver levelNo package touched; ③ Boundary flags
Implemented-by: VERDICT: PASS |
维护者速读(终稿)改了什么:只改一份对外发布的技能 为什么改:客户项目的 agent 整包加载这份技能;读到「只写 风险与代价(含回滚):纯文本,无代码、无 changeset、无生成物(frontmatter 未动, 席位意见:ACCEPT;席内契约复核 PASS(记录 6046687007,head 你要做的(一个动作):以授权账号 APPROVE 本 PR(或亲手合入)。PR 保持 draft;获批后本席翻 ready、挂 auto-merge 入队并跟到 MERGED。 |
…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>
…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>
…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>
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). Sinceos migrate meta --writelanded (a959493cdf), that is true only of the default run. This PR makes every sentence inskills/objectstack-upgrade/SKILL.mdthat states what the command writes true of both routes, within the skill's token ceiling.What changed (one file:
skills/objectstack-upgrade/SKILL.md)os migrate meta --from 16 --write # rewrite the proven sites in place.os migrate metarewrites no source file. It lists the mechanical edits and writes only the--outJSON 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.migrate metareports 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."--applyrefused / 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--outand, with--write, the sources".Every claim is read from
packages/cli/src/commands/migrate/meta.tsatdb4c45b8c3(flag description andexclusive: ['stored'];WriteOutcome.status=written | restored | unwritten;printWriteOutcomelists each unwritten site as "not written [kind]: reason";this.exit(1)wheneverwrite.status !== 'written') and from thepackages/cli/src/utils/authored-source-codemod.tsmodule 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--writeare both described; the text does not say what--outdoes on a run with nothing to migrate and does not describe the semantic-notice list (both are in flight onmeta.tsin #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):db4c45b8c3)9bb10014)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:
--outrecheck 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;--storedsubsection — the failure-mode row "--applyrefused / stored-only flag rejected" carries the same fact with its fix, and the preceding "--storedtakes no--from" keeps the other direction.Scope held
packages/spec/**(the retirement sentence inretired-key.tsis feat(cli):os migrate meta --write— the AST codemod that rewrites authored sources for the mechanicalappliedset (v18) #9591's spec-lane remainder; this text uses its vocabulary — "lists the mechanical edits" — so the two read consistently once that lands). Nocontent/docs/**(devx lane under feat(cli): os migrate meta --write — write the chain's mechanical edits into the authored sources #22108). No generated listing: frontmatter unchanged,check:skill-docsgreen ("Skill docs in sync").references/examples-upgrade.md:57("Ported into sources fromos migrate meta --out") andevals/protocol-major-upgrade.json(must_containincludes--out; the eval's expected answer describes the default route, which still rewrites no source).Gates
Derived with
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackfrom the changeset at9bb10014(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-expressionsfirst exited 3 (prerequisite:@objectstack/lintnot built) and was re-run after the prescribed build.Acceptance notes
SKILL.md:103says "os migrate meta --from 10replays every step in order", butMIGRATION_SUPPORT_FLOOR = 16(packages/spec/src/migrations/registry.ts:79), so that command refuses withMigrationFloorError/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.evals/protocol-major-upgrade.jsoneval 1expected_outputsays "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