Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions .claude/skills/spec-property-retirement/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,10 +210,12 @@ conversion 是消费者跟的;D3 条目是升级方的 agent 读的。三个都
「没有 alias 窗口,故意的」;对**默认值翻转**,只有确知输入早于翻转的 seam 才可重
放,其余按 id 退订 `excludeConversionIds` —— `app-hidden-to-unpublished` 在 artifact
门即如此。
- [ ] **一步 D3 链**,在 `packages/spec/src/migrations/registry.ts`;`conversion.toMajor` **必须等于**该步的
major。**18 步**:conversion 只进 `conversions/registry.ts` 的 `MAJOR_18_CONVERSIONS`,照其头注按
标识符排序插入,本步 `conversionIds` 由它派生;`rationale` 只加一个 `STEP18_RATIONALE` 片段,
按其头注插在你 D3 semantic id 的排序位;尾部追加被两处头注点名的 merge 测试拒收。
- [ ] **一步 D3 链**,在 `packages/spec/src/migrations/registry.ts.template`(`registry.ts` 由
`gen:migration-registry` 从它与 `entries/` 整文件生成并被 git 忽略,在 `registry.ts` 里手改不会
进提交);`conversion.toMajor` **必须等于**该步的 major。**18 步**:conversion 只进
`conversions/registry.ts` 的 `MAJOR_18_CONVERSIONS`,照其头注按标识符排序插入,本步 `conversionIds`
由它派生;`rationale` 只加一个 `STEP18_RATIONALE` 片段,按其头注插在你 D3 semantic id 的排序
位;尾部追加被两处头注点名的 merge 测试拒收。
**更早的步**:id 加进 `MIGRATIONS_BY_MAJOR[N].conversionIds`,扩写该步 `rationale`。⚠ 没有东西
直接断言「每个 conversion 都接进了某一步」,拼错的 id 在 replay 时被**静默跳过**;
chain-replay 测试抓得到它,只因为没接线的 fixture 永远到不了自己的 `after`。
Expand Down Expand Up @@ -257,7 +259,7 @@ conversion 是消费者跟的;D3 条目是升级方的 agent 读的。三个都
无墓碑删键 → 该行**消失**,这是门 (a) 的绊线,所以同一个 PR 里有意删掉它)与
`json-schema.manifest/<category>.json`。#5837 起两者都按 category 分片 —— 门
禁把整个目录读成一个集合,退役流程不变;变的只是那一行住在哪个文件。然后
`gen:spec-changes`、`gen:upgrade-guide`、`gen:api-surface`、`gen:docs`。
`gen:api-surface`、`gen:docs`;新条目跑 `gen:migration-registry`;D4 投影不提交,本地不重生成。
「改了 X → 重新生成 Y」的表见 AGENTS.md。
- [ ] **Forms** —— 从 `packages/spec/src/**/*.form.ts` 剪掉 `{ field: '<key>' }` 输
入。未强制能力的表单输入是假合规的 UI 半边。原位留一行注释。
Expand Down Expand Up @@ -332,6 +334,4 @@ pnpm check:i18n && pnpm --filter @objectstack/spec test
- **截断的 grep 漏掉编写者。** `| head -8` 藏掉了 `SKILL.md` 的 `defineSkill` 示
例;`examples/` 有三处 `template: true`,不是一处。先按上下文找文件,再进文件里
grep 键 —— 且把 `tsc` 和门禁当权威清扫器。
- **只改 conversion 的 `summary` 也会过期。** 那个字符串被逐字抄进
`spec-changes.json` 的 `to` 字段与 upgrade guide 的表行,所以纯散文改动也要
`gen:spec-changes` + `gen:upgrade-guide`,和任何别的改动一样。
- **只改 conversion 的 `summary` 不欠重生成。** 它逐字进两条 D4 投影,都不提交;PR 渲染其 diff。
9 changes: 5 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -703,7 +703,7 @@ Root also exports: `defineStack`, `composeStacks`, `defineView`, `defineApp`, `d

### Touched `packages/spec`? Regenerate its artifacts BEFORE pushing

`packages/spec` has **eight** checked-in generated artifacts, each with its own CI
`packages/spec` has **six** checked-in generated artifacts, each with its own CI
gate. All of them live in one job — `TypeScript Type Check` in `lint.yml`, which is
required and has no paths filter, so no gate can go dormant on the PR that breaks it.
That job runs its gates **sequentially**, so the first stale artifact masks every one
Expand All @@ -714,7 +714,7 @@ behind it. Match the change to the gate and regenerate up front:
| A `.describe()` / TSDoc on any schema | `check:docs` | `gen:schema && gen:docs` |
| A public export (added / removed / renamed) | `check:api-surface` | `gen:api-surface` |
| An authorable key on a metadata schema | `check:authorable-surface` | `gen:schema` |
| An ADR-0087 conversion / migration registry | `check:spec-changes`, `check:upgrade-guide` | `gen:spec-changes`, `gen:upgrade-guide` |
| An ADR-0087 conversion / migration registry entry | `check:spec-changes`, `check:upgrade-guide` — in memory, red only when the registries do not project | nothing to commit — `gen:migration-registry` (install, build and test run it too) writes the gitignored `registry.ts` |
| A `SKILL.md` (frontmatter or body) | `check:skill-docs`, `check:skill-refs` | `gen:skill-docs`, `gen:skill-refs` |
| The react-blocks contract | `check:react-blocks` | `gen:react-blocks` |

Expand Down Expand Up @@ -758,8 +758,9 @@ Principles the wrapper encodes (its own output is the authority on detail):
a name, with accepted cases in the shrink-only, hand-edited
`dual-source-exports.baseline.json`.

Two generators have **no** gate at all — `gen:openapi` and `gen:sbom`. Nothing verifies
their output is current; the wrapper reports that each run rather than staying silent.
Three generators have **no** gate at all. Nothing verifies `gen:openapi` and `gen:sbom`
are current, and `gen:migration-registry` writes gitignored output that install, build and
test regenerate; the wrapper reports all three each run rather than staying silent.

---
## Context Routing — apply the right role per path
Expand Down
48 changes: 27 additions & 21 deletions docs/adr/0087-metadata-protocol-upgrade-contract.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ADR-0087: Metadata protocol upgrades for AI consumers — conversion over notification, executable migrations, machine-verifiable upgrades

**Status**: Accepted (2026-07-04, #2582) · trued up to as-built 2026-07-15 (see Addendum) · **Amended** (2026-09-30, #20390 ruling A — the artifact-ingestion window decides per entry by each retired conversion's `retiredAfter`; see the amendment note under the 2026-09-13 addendum's window bullet) · **Amended** (2026-10-09, #22449 ruling B′ — the two D4 projections, the per-major section of `spec-changes.json` and the protocol upgrade guide, are generated at publish rather than committed; see the amendment note under D4, which says what has landed and which cards carry the rest, and the struck clauses in the P2 phase-status bullet)
**Status**: Accepted (2026-07-04, #2582) · trued up to as-built 2026-07-15 (see Addendum) · **Amended** (2026-09-30, #20390 ruling A — the artifact-ingestion window decides per entry by each retired conversion's `retiredAfter`; see the amendment note under the 2026-09-13 addendum's window bullet) · **Amended** (2026-10-09, #22449 ruling B′ — the two D4 projections, the per-major section of `spec-changes.json` and the protocol upgrade guide, are generated at publish rather than committed; see the amendment note under D4, which says what is generated where and where each part landed, and the struck clauses in the P2 phase-status bullet)
**Deciders**: ObjectStack Protocol Architects
**Builds on**: [ADR-0059](./0059-third-party-backward-compatibility-gates.md) (layered backward-compat gates — this ADR is its consumer-facing sequel), [ADR-0078](./0078-no-silently-inert-metadata.md) (no declarable-but-unenforced metadata — the un-checked `engines.protocol` is exactly this class), [ADR-0025](./0025-plugin-package-distribution.md) (§3.2 `engines.protocol` / `engines.platform` compatibility ranges, §3.10 #3 protocol-first check order), [ADR-0033](./0033-ai-assisted-metadata-authoring.md) (the authoring population this ADR designs for), [ADR-0049](./0049-no-unenforced-security-properties.md) (enforce-or-remove), [ADR-0054](./0054-runtime-proof-for-authorable-surface.md) (prove-it-runs), AGENTS.md Prime Directive #12 (contract-first, no consumer-side dialect fallbacks — §"Why the conversion layer does not violate PD #12" draws the line)
**Consumers**: `@objectstack/spec` (protocol version constant, conversion layer, deprecation/change registries), `@objectstack/cli` (`validate`, `doctor`, `migrate meta`), the runtime metadata loader (handshake + conversion), `@objectstack/mcp` (the AI-native change/migration surface), `@objectstack/create-objectstack`, the Release workflow, and every third-party consumer — whose maintainer is assumed to be an **AI agent**
Expand Down Expand Up @@ -205,7 +205,9 @@ chain** — the database-migration model applied to metadata source files.
> **Amended 2026-08-06 (#5837), paths only.** `api-surface.json` became the directory
> `api-surface/` (one shard per entry point) and ships in the npm artifact as such; the
> release diff reads whichever layout the previously published tarball carried. `spec-changes.json`
> is deliberately unsharded — it is keyed by version, which has never been a conflict surface.
> was left deliberately unsharded — it is keyed by version, which was never a conflict surface.
> That call was about merging its committed copy, which #22485 deleted (PR #22638): nothing
> merges it any more.

> **Amended 2026-10-09 (#22449, ruling B′) — where the two projections live.** Ruling
> `6078203801` on #22449, letter B′, which the maintainer answered 「同意」 at
Expand Down Expand Up @@ -255,20 +257,23 @@ chain** — the database-migration model applied to metadata source files.
> artifact when it is non-empty. That step is red only when a side cannot be
> generated, never because of the size of a diff.
>
> **The ruled end state, and the cards that still carry it.** Not landed when this
> note was written, and stated here as ruled rather than as done:
> **The ruled end state, landed** (trued up 2026-10-11). Not landed when this note was
> first written; both remaining cards have landed since, each deleting one committed copy
> and its `merge=os-regen` route in `.gitattributes`:
>
> - condition (2), the guide's public address and the pointer stub at
> `docs/protocol-upgrade-guide.md` — #22483;
> - the deletion of the committed copies (the per-major projection in
> `packages/spec/spec-changes.json`, the guide body at
> `docs/protocol-upgrade-guide.md`) and of their two `merge=os-regen` routes in
> `.gitattributes` — #22485, which lands last.
> - condition (2) — #22483, landed as `514bf3c101` (PR #22556). The guide's public
> address is the docs site, one page per protocol major, generated at docs build into
> the gitignored `content/docs/protocol-upgrade/`. `docs/protocol-upgrade-guide.md`
> stays at its path as a hand-written pointer stub naming that address; the guide body
> it used to hold left git with its route;
> - the committed `packages/spec/spec-changes.json` and its route — #22485, landed last
> as `46064dfa03` (PR #22638).
>
> Until #22485 lands the committed copies stay in the tree, and the generators'
> no-flag mode (`gen:spec-changes`, `gen:upgrade-guide`) still writes them, but
> `check:spec-changes` and `check:upgrade-guide` no longer compare them: they can go
> stale, and the copies that ship are the record. Condition (3) is cloud's half and
> No committed copy of either projection remains. `packages/spec/spec-changes.json` and
> `packages/spec/protocol-upgrade-guide.md` are gitignored paths the publish lane writes
> for `files[]` to ship, and the generators' no-flag mode writes only untracked output
> (`gen:spec-changes` that `spec-changes.json`, `gen:upgrade-guide` the docs pages), so a
> registry entry's pull request regenerates neither. Condition (3) is cloud's half and
> lives in that repository (objectstack-ai/cloud#2750); this record governs the
> framework half.
>
Expand All @@ -281,7 +286,7 @@ chain** — the database-migration model applied to metadata source files.
> generators the publish lane runs, and the registry entries those projections are
> computed from stay committed, reviewable source. What leaves git is the committed
> aggregate and the re-sync laps it cost. The 2026-08-06 note above ("deliberately
> unsharded") is about that committed copy, which #22485 removes.
> unsharded") is about that committed copy, which #22485 removed.

- The Release workflow diffs the current `api-surface.json` against the previously
published one (reusing the ADR-0059 §3 gate artifact instead of discarding it),
Expand Down Expand Up @@ -428,18 +433,19 @@ scaling framework-side execution when the consumers can execute themselves.
**amended 2026-10-09** — it is generated from the registries at publish, as
`protocol-upgrade-guide.md` inside the npm artifact and on the GitHub Release,
and in memory, with its diff rendered, at pull request;
`docs/protocol-upgrade-guide.md` is ruled to become a pointer stub naming the
guide's public address, which #22483 carries.
`docs/protocol-upgrade-guide.md` became a pointer stub naming the guide's
public address, the docs site's per-major pages (#22483, PR #22556).

> **Amended (2026-10-09) — the projections are generated at publish, not
> committed.** Provenance: ruling `6078203801` on #22449 (letter B′; maintainer
> 「同意」), which supersedes the 2026-08-10 #6957 ruling's rejection of option B
> and keeps its reason, the review diff, as its condition (1); the generation
> half landed as `5b12503c31` (PR #22533, card #22482). The struck clauses
> describe committed copies drift-checked in CI. Those copies stay in the tree
> until #22485 deletes them, and `check:spec-changes` / `check:upgrade-guide`
> no longer compare them. The amendment note under D4 is the full record:
> what is generated where, and what #22483 and #22485 still carry.
> describe committed copies drift-checked in CI. Those copies are gone — the
> guide's at #22483 (PR #22556), `spec-changes.json`'s at #22485 (PR #22638) —
> and `check:spec-changes` / `check:upgrade-guide` generate in memory and
> compare nothing. The amendment note under D4 is the full record: what is
> generated where, and where each part landed.
- **P3 — MCP tools + RC discipline (D6): deferred, evidence-gated** (as the ADR
Boundaries intended): built when external-consumer demand justifies the
operational surface. Nothing else in this ADR depends on it.
Expand Down
Loading