Skip to content

feat(gates): the changeset level follows the metadata protocol — Metadata-protocol: line, level axis, console refresh at patch, release notes by reader action (ruling A) - #25114

Merged
os-zhuang merged 3 commits into
mainfrom
claude/issue-25105-changeset-protocol-levels
Oct 21, 2026
Merged

os-zhuang merged 3 commits into
mainfrom
claude/issue-25105-changeset-protocol-levels

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #25105

Clause-②: no (tooling and rules text: no published package's accept set or public surface moves)
Metadata-protocol: none (the declaration line this PR introduces; nothing a metadata author writes against changes)

What this PR does

Maintainer ruling A (2026-10-21), as the card files it: the changeset level is minor only when the metadata protocol changes — ① added (a new authorable key, accepted value or enum member, metadata type), ② narrowed or retired, ③ semantic (meaning or default of an authorable key) — and patch for everything else, REST, drivers, services, console, feat: and fix!: alike; non-protocol breaking changes keep their BREAKING banner and ADR-0087 disposition; release notes are grouped by what the reader must do. One PR rather than two: the gate and the rule prose land together, so no in-flight PR meets a level rule AGENTS.md does not state.

1. The declaration — a sibling line, not a third arm (scripts/pm/clause2-line.mjs)

Every PR body and changeset body states its kind on a line of its own beside the Clause-②: line:

Metadata-protocol: added | narrowed | semantic | none

The dev's call the card leaves open is sibling line, and the reason is structural: every declaration already written (Clause-②: no, yes (widening), no (narrowing)) keeps reading byte-identically — readClause2Line's predicate, value class and arm reader are untouched, and the six existing readers (check-adr-0087-registration, check-changeset-no-major, objectui-changeset-digest, pm/check-half-states, pm/post-stamped, pm/record-recognisers) see exactly the readings they saw before. readMetadataProtocolLine reuses the same key-line shape, decoration tolerance, value-token calibration (first token after the colon, trailing reasoning accepted, an alternation or an unfilled template malformed) and the same describing / inline-key / spelling near-miss triage, parameterised on the key and the closed value set, so the two lines cannot drift in what they accept. The module still imports nothing (both gates stage it into a throwaway repo and the level gate runs before pnpm install). One token per line; where several kinds apply the author names the highest in breaking-ness, narrowed over semantic over added.

2. The level axis (scripts/check-changeset-no-major.mjs)

Both lines are read from the PR body (the event payload, no token, re-read on edited), exactly where the Clause-②: line was read. The verdicts, every one with its own sentence and its own one-line check-run annotation:

Reading Verdict Exit
a kind (added / narrowed / semantic) and at least one moved package graded minor+ clean / discharged (#16361 grain, unchanged) 0
a kind, moved packages graded patch, none raised enforce 1
none, no introduced entry above patch, no baseline moved none-clean — the BREAKING carriers are stated as untouched 0
none with any introduced minor/major, on any package none-minor 1
none while packages/spec/authorable-surface/ or authorable-defaults/ shards moved none-baseline-moved, naming the shard and the kinds that would be true 1
added under Clause-②: no; narrowed beside a line with no (narrowing) arm contradiction (two reasons, two remedies) 1
the kind unreadable and material — a patch on a moved package, any minor, or a moved baseline not-measured-material, naming which line(s) are missing and spelling all four kinds 1
the kind unreadable and nothing a kind or a none would grade differently not-measured-moot 0

What is not loosened: the Clause-②: line unreadable over a moved package graded patch with no raise is still not-measured-material (the #16776 obligation stays where it was); the major guard, the protocol-lockstep evidence, the grain (#16361), the depth (#16713), the root (#16692) and the copy (#15204) legs are inherited unchanged, their batteries re-driven with Metadata-protocol: added where they drove Clause-②: yes. What is re-ruled: Clause-②: yes or a (narrowing) arm no longer couples to minor — a REST widening or a driver-posture narrowing under none is none-clean at patch. Neither line is inferred from the other: a yes is not read as added, an arm is not read as narrowed; the contradictions go the other way only. not-declared is gone rather than renamed: under ruling A a bare no decides nothing about the level. New: a ruling-A battery (37 assertions, floor 30) drives the real reader and real temp repositories; the roster floor is 21 → 22. Self-test: 455 assertions.

3. Readers of the one reader, touched only as far as the extension requires

  • scripts/check-adr-0087-registration.mjs: Metadata-protocol: narrowed is the fifth breaking signal (a protocol narrowing is a break by definition, and ruling A keeps the carriers on it); added, semantic, none and a malformed kind add no signal — convention-rot fixtures pin both directions. Self-test: 548 assertions.
  • Untouched, by measurement: pm/record-recognisers.mjs (the review face keys on the Clause-② arm, which keeps its meaning), pm/post-stamped.mjs (claim keyed-line refusals; the claim template is out of this claim), pm/check-half-states.mjs (reads the claim's Clause-②: only).

4. Console refresh grades patch (scripts/objectui-changeset-digest.mjs)

The pin-bump changeset is patch whatever objectui declared over the range — in RC pre-mode too, since the console never changes the metadata protocol and the platform's version line follows it alone. declaredLevel is still derived and reported (the entry lines carry it, the body says "declared X, recorded as patch" when they differ), CONSOLE_BUMP still overrides, and the generated file carries Metadata-protocol: none so the gate and the release notes read it. The #14178 deepen test's discriminator moved from the bump word to the record itself (breaking entry, ADR-0087 prompt, no degraded list), which is what that card was about. Self-test: all checks passed (end-to-end through bump-objectui.sh and through the real ADR-0087 gate).

5. Release notes grouped by what the reader must do (scripts/release-github-releases.mjs)

The mechanism: the existing GitHub Release body path. changeset version keeps writing CHANGELOG.md as it does (RELEASE-OWNED, untouched); planRelease now posts that section regrouped — the same bullets, byte for byte, none dropped — under the five headings in the ruling's order, each read from the declarations the entry carries: 需手动处理 (breaking — narrowed, the (narrowing) arm, a BREAKING banner or a ! summary — with no registered ADR-0087 conversion), 自动迁移 (breaking with a registered conversion objectstack migrate meta applies), 新增能力 (added, or a non-breaking Clause-②: yes widening), 语义变化 (semantic), 其他修复 (the rest: none, dependency bumps, the pre-rule stock). Empty sections are omitted; the truncation path is the same as before. The splitter is fence-aware and only a column-0 - opens a bullet, so an entry's own FROM → TO tables, nested lists and fenced examples stay inside it (pinned on the real 16.1.0 section: same bullet count before and after). The breaking signals mirror the ADR-0087 gate rather than importing an 8,000-line module into the release lane. Self-test: 103 assertions, floor 14 → 15. annotate-shipped (release-pending-publish.mjs) was measured and is not the release-notes path — it writes "Shipped in" lead lines into unconsumed changesets — so it is untouched. The docs release page is curated by hand (check-release-section-coverage generates nothing on purpose); docs/releases-maintenance.md now says a curated section uses the same five headings.

6. The prose (Tier H)

  • AGENTS.md Post-Task Checklist item 3 restated from the ruling's table — line-neutral at 1117 (the ratchet's ceiling, headroom 0): the paragraph stays 18 lines at ≤119 bytes by condensing its own retirement-kit sentences (the detail stays in the spec-property-retirement skill it already defers to); no re-wrap elsewhere, no ceiling raise asked.
  • .github/workflows/pr-automation.yml "WHICH LEVEL" (inside the quoted heredoc of Require a changeset): the table, the .strict() rationale, the carriers, the gate's verdicts. The step's own ::error:: annotation, $MERGE_BASE wiring and trigger types are untouched (the self-tests that pin them are green).
  • docs/releases-maintenance.md: the console grade and the five sections.

Measured against the dispatch hypotheses

Verification

Final head 99eaf8350; every exit code captured before any pipe (cmd > log 2>&1; EXIT=$?).

  • Self-tests of the edited scripts: check-changeset-no-major --self-test 455 assertions (the first run after the gate change failed 77 fixtures that pinned the old yes ⇒ minor coupling — the fixtures were updated, none deleted; every battery stays at or above its floor); check-adr-0087-registration --self-test 548; check-empty-changeset --self-test 170 (the byte-identical entry-regex pin across the four readers still holds); objectui-changeset-digest --self-test all checks (end-to-end through bump-objectui.sh and the real ADR-0087 gate); release-github-releases --self-test 103; pnpm check:pm-dispatch-gates 1913/1913 (fast tier).
  • Real scans on this branch against origin/main: check-adr-0087-registration --base origin/main exit 0 (the convention-rot fixtures run on every real scan, narrowed among them); check-changeset-no-major --base origin/main exit 0 (locally the level axis takes its no-pull-request lane, by design).
  • eslint --no-inline-config on the five edited scripts: exit 0.
  • Derived union, node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack off the merge base (8 paths): 64 commands, all run locally, all exit 0 at the final head. Two needed a second pass and neither was a finding: pnpm --filter @objectstack/lint run check:doc-formula-expressions exited 3 (PREREQUISITE NOT MET, @objectstack/formula/lint unbuilt in a fresh worktree) and exits 0 after turbo run build --filter=@objectstack/formula --filter=@objectstack/lint under the verify lock; pnpm check:pm-dispatch-gates failed 1 of 1913 on the no-path-population marker check:release-body used to carry, which the new pm/clause2-line.mjs import contradicts by the harvester's own rule — the marker is dropped (the reader IS that family's one path population), 1913/1913 after. --ran reconciliation with recorded exit codes: 64 derived, 64 run, 0 NOT-MEASURED, 0 UNRUN.
  • CI-owned, not run here: pnpm lint (repo-wide), test:repo, Test Core; the diff touches no packages/** path, so the package build closure and package tests are n/a, as are check:dev-prereqs and check-platform-checklist --changed-from (no exported symbol renamed or deleted — not-declared was a verdict string, not an export). No ablation: the diff is gates, a workflow and docs, whose pins are the self-tests above, driven red → green by the change.
  • Dogfood battery: n/a — no ! in the title and no (narrowing) arm.
  • Changeset: skip-changeset — nothing published moves (every changed path is scripts/**, .github/**, docs/** or AGENTS.md; no package's files[] reaches any of them).

Acceptance notes

  • Residual, named rather than silent: the baseline leg sees a changed key set and a changed default; a refused value, a narrowed enum or a changed meaning is invisible to it, so the declaration (not the diff) stays the carrier — stated in the gate's header and in "WHICH LEVEL".
  • The kind is read from the PR body for the gate and from the changeset body for the ADR-0087 gate and the release notes — the same two-carrier shape the Clause-②: line already has; nothing cross-checks the two copies, as nothing did before.
  • inPreMode in the digest keeps its export (the RC exemption no longer reaches the console grade; the function has no other caller in scripts/), noted so a later reader does not take its presence for a live exemption.
  • Not filed, observation only: the release lane's annotate-shipped writes a "Shipped in" lead line above the changeset's declarations; the release-body splitter reads the whole bullet so the lines are still found.

维护者速读(草稿)

改了什么。 按您 2026-10-21 的裁决 A 落地:版本号只跟元数据协议走。每个 PR 和 changeset 在 Clause-②: 旁多写一行 Metadata-protocol: added | narrowed | semantic | none;门禁 check-changeset-no-major 据此判级 —— 协议加了/收窄了/语义变了,至少一个被改包 minor;none 一律 patch,带 minor 即拒,改了 authorable-surface/、authorable-defaults/ 基线却声明 none 也拒;added 配 Clause-②: no、narrowed 不带 (narrowing) 臂是自相矛盾,拒。ADR-0087 门禁把 narrowed 当破坏性信号;console 刷新 changeset 固定 patch;GitHub Release 正文按「需手动处理 / 自动迁移 / 新增能力 / 语义变化 / 其他修复」五段重排(CHANGELOG 不动)。AGENTS.md 与 pr-automation.yml 的规则文字同步改写。

为什么改。 现状是几乎每次发版都是 minor:95 个待发 changeset 里约一半是 minor,其中只有十来个真的动了元数据协议,版本号说不出协议动没动。裁决 A 把 minor 留给协议变化,added 之所以也是 minor,是因为 spec schema 不是 .strict(),旧运行时会静默丢掉不认识的键,写新键的 app 要能指一个 minor 底线。

风险与代价(含回滚)。 ① 过渡成本:现开的 12 个 PR 里 10 个带 changeset,落地当天都会在该门禁上读到「未声明」而红,每个 PR 在正文加一行即清,不用推送;其中按我的判读 4 个是非协议 minor,需按新规改成 patch(报告里逐个点名,由席位和作者定)。② .claude/** 的认领模板与 os-dev 不在本卡范围,未改;改之前新派发的 PR 都会先红一次再补行。③ 基线腿只看得见键和默认值,拒值、收窄枚举、语义变化仍靠声明。回滚:整 PR revert 即可,无数据迁移、无发布面变化(skip-changeset)。

席位意见。

你要做的。 Tier H 人合:看一眼 AGENTS.md 第 1084–1101 行与 pr-automation.yml 的 "WHICH LEVEL" 两段规则文字是否就是您要的表述;认可则批准,席位入队落地。另请定 #25099 那类「平台对象字段选项集收窄」算不算元数据协议 narrowed(我的建议:算,因为它改的是作者可写的值域)。


Generated by Claude Code

…etadata-protocol:` line beside `Clause-②:`

Maintainer ruling A (2026-10-21): `minor` only when the metadata protocol is
added to, narrowed or changed in meaning; `patch` otherwise. The one reader
(`scripts/pm/clause2-line.mjs`) gains the sibling line, import-free, with the
`Clause-②:` reader byte-identical in behaviour. `check-changeset-no-major.mjs`
judges the level on the kind: a kind owes `minor` on a moved package, `none`
owes `patch` everywhere and may not move an authorable-surface/-defaults
baseline, `added` under `no` and `narrowed` without the `(narrowing)` arm are
contradictions, and an unread kind that decides the verdict is refused as
NOT MEASURED. Fixtures updated; a ruling-A battery added (floor 21 -> 22).

Claude-Session: https://claude.ai/code/session_01VsqhiSrBjB1wJfRNcHPeXN
Co-authored-by: Claude <noreply@anthropic.com>
…rowed`, the console refresh grades `patch`, the Release body groups by what the reader must do

- check-adr-0087-registration.mjs: `Metadata-protocol: narrowed` is the fifth
  breaking signal; `added`, `semantic`, `none` and a malformed kind add none.
- objectui-changeset-digest.mjs: the console refresh changeset is `patch`
  whatever objectui declared (CONSOLE_BUMP still overrides), says so in the
  body, and carries `Metadata-protocol: none`.
- release-github-releases.mjs: the GitHub Release body is the CHANGELOG
  section regrouped under 需手动处理 / 自动迁移 / 新增能力 / 语义变化 / 其他修复,
  read from each entry's declarations; the CHANGELOG itself is untouched.
- AGENTS.md changeset rule and pr-automation.yml "WHICH LEVEL" restated from
  the ruling's table (AGENTS.md line-neutral at 1117).
- docs/releases-maintenance.md: the console grade and the five sections.

Claude-Session: https://claude.ai/code/session_01VsqhiSrBjB1wJfRNcHPeXN
Co-authored-by: Claude <noreply@anthropic.com>
…r — the declaration reader is its one path population

The renderer now imports scripts/pm/clause2-line.mjs (ruling A: the
readings decide the release-note sections), and a pathy first-party import
is a watch hint by the harvester's own rule; the marker and the import
contradicted each other (check:pm-dispatch-gates, 1 of 1913), and the import
is the true statement — a card that edits the reader owes this self-test.

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

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 99eaf835091373dde2a9b0bcd84f24fa0ff5b5d1
Local-runs: none
Scope: AGENTS.md, .github/workflows/pr-automation.yml + scripts/check-changeset-no-major.mjs, scripts/pm/clause2-line.mjs, scripts/check-adr-0087-registration.mjs, scripts/objectui-changeset-digest.mjs, scripts/release-github-releases.mjs, docs/releases-maintenance.md

Inputs read, and nothing else: card #25105 (body, claim comment 6314107974, os-dev-report comment 6315700975 — no ruling comment beyond the body); PR #25114 (body, the 8-file list, the net diff origin/main...99eaf8350, merge base 5050442bb9, three commits); the 45 check-runs on 99eaf8350, read at 2026-10-21T12:29:36Z: 32 success, 13 skipped, none failed, none in progress. The five required contexts: Lint & Repo Gates success (it carries check:changeset-gate-self-tests, check:objectui-changeset, check:release-body, check:pm-skill-ratchet, check:pm-skill-id-lint, check:pm-governed-prose, check:pm-dispatch-gates), TypeScript Type Check success, Test Core success (6 of 6 shards), Governed Surface Queue Guard success, Build Core skipped (no packages/** path moves). Check Changeset success on the head. Those conclusions are the gate verdicts here; nothing was run locally.

① Derived judgments

  1. The rule text restates ruling A — right. .github/workflows/pr-automation.yml "WHICH LEVEL" carries all four parts the card body states: the four-row table in the ruling's own words (added: new authorable key, accepted value or enum member, metadata type; narrowed: key removed or tombstoned, value refused, requirement tightened; semantic: meaning or default of an authorable key; none: description or error text, non-authorable internal types, REST, drivers, services, console, feat: and fix!: alike), the .strict() rationale sentence, "a non-protocol breaking change ships patch and KEEPS its carriers: the BREAKING banner and the ADR-0087 disposition are unchanged", and the five release-note sections in the ruling's order and script. The old fix( that widens an index is minor paragraph and the commit-type discriminator are gone, as the amendment requires. AGENTS.md item 3 carries the table (condensed: "enum member" folds into "accepted value", the ④ example list is dropped) plus the carriers sentence, and drops the old yes takes at least minor coupling; it does not carry the .strict() rationale or the five sections. I read that as right, not a gap: the file's own header makes it rules-only, it sits at its 1117-line ceiling (held, max 119 bytes a line, check:pm-skill-ratchet green), card item 3 asks for a restatement "from the table above", and the rationale and sections live where the author who is refused reads them (WHICH LEVEL) and in docs/releases-maintenance.md. Both files now name both declaration lines and say check:adr-0087-registration reads the arm and the kind. The one thing the ruling does not state and the text adds: the precedence narrowed over semantic over added when several kinds apply — a tie-break the one-token line needs, pointing in the direction that keeps the BREAKING carriers; right.
    One prose nit, not a verdict: WHICH LEVEL says a PR "that introduces a changeset entry with no readable Metadata-protocol: line is refused as NOT MEASURED (exit 1)". The gate's inherited Check Changeset concludes success when it measured nothing, and no edited trigger ever re-reads a Clause-②: line added after the last push — a correct seat action leaves the level axis unjudged behind a green check #16776 moot lane still exits 0 for a patch entry on a package the diff does not move (the new self-test matrix pins exactly that). The sentence overstates the gate by that one edge; the gate is the correct side. A one-clause follow-up edit ("an entry the kind would grade").

  2. The gate enforces that text, no looser and no stricter than ruling A re-rules — right. judgeLevel read against the table: a kind owes at least one moved package minor+ (enforce / discharged / clean, the [finding] Check Changeset's clause-② level rule is PR-scoped but the fact it judges is package-scoped — a PR that widens package A is refused for grading package B patch, where B only received a comment #16361 grain, [finding] The changeset LEVEL axis is blind to every NESTED package: packages/*/src/** matches one segment, so 51 of 74 workspace packages (all drivers/services/adapters) can pair Clause-②: yes with patch and stay green #16713 depth, [finding] judgeLevel counts a package as "grown" only through packages/*/src/**, so a shipped bin/ target is a published surface the changeset level axis cannot see #16692 root and refactor(plugin-security,platform-objects,spec): retire the catalog seeders, the per-organization catalog machinery and the four catalog objects; Setup creation is an environment write under single and refused under a wall (ADR-0131 D2/D3/D5/D13) #15204 copy legs inherited unchanged — their fixtures re-driven with Metadata-protocol: added where they drove Clause-②: yes); none owes patch on every introduced entry, any package (none-minor), and may not move a shard under packages/spec/authorable-surface/ or authorable-defaults/ (none-baseline-moved; baselinesTouched reads the 14 + 13 shard files that exist on main with a trailing-slash prefix, so authorable-surface.base.json is excluded — the card's "where the existing ratchets can see it", and the refusal names the shard and the three kinds that would be true); added under Clause-②: no and narrowed beside a read Clause-②: line with no (narrowing) arm are contradiction, each with its own reason and remedy, and neither line is inferred from the other. The Check Changeset concludes success when it measured nothing, and no edited trigger ever re-reads a Clause-②: line added after the last push — a correct seat action leaves the level axis unjudged behind a green check #16776 materiality is kept on the Clause-② line byte-for-byte (value === null && refusable) and widened to the kind (refusable, any minor+, or a moved baseline). What loosened is exactly the two couplings ruling A re-rules: Clause-②: yes and the (narrowing) arm no longer force minor — under a declared none they read none-clean at patch with the BREAKING carriers stated untouched. not-declared (a bare no stands the axis down, exit 0) is removed in the stricter direction: a bare no with no kind is not-measured-material on a material tree. The launch-window major guard (scan / judge / render), the protocol-lockstep evidence and scan()'s "introduced entries only" reading (base-side entries subtracted per package and bump) are not in the diff. No new gate: no new script, workflow step or job; the new verdicts live inside the existing level axis as card item 2 asks, and the body is read on opened, synchronize and edited as before (the types: list is untouched). Self-test: new battery "Ruling A: the metadata-protocol kind is the level axis" at floor 30, roster 21 to 22, driven through the real reader and real temp repositories; check:changeset-gate-self-tests green in CI.
    Nit: baselinesTouched swallows a git diff failure into moved: []. Practically unreachable — scan() has just run the same from..head diff and throws on a bad merge base — but the shape is a silent stand-down where packagesTouched returns an unreadable list; a later tidy, not a finding against this head.

  3. The declaration carrier — right. Sibling line Metadata-protocol: added | narrowed | semantic | none in scripts/pm/clause2-line.mjs, the choice and its reason stated in the PR as the card asks. readMetadataProtocolLine reuses the key-line shape, decoration tolerance, value-token calibration and the describing / inline-key / spelling triage, parameterised on key and closed set; readClause2Line's body is untouched (the three shared helpers gained a defaulted parameter), so every existing declaration reads as before. The module still imports nothing. The reading is four-valued like its sibling, and none of malformed, near-miss or absent is read as none — the gate decides what an unread kind costs.

  4. scripts/check-adr-0087-registration.mjs — right. Metadata-protocol: narrowed on the changeset body is the fifth breaking signal; added, semantic, none and a malformed kind add none; convention-rot fixtures pin both directions and run on every real scan. A strictly tighter reading of an existing gate, consistent with "non-protocol breaking changes keep their carriers" (a protocol narrowing is a break by the ruling's own definition). Not a new gate.

  5. scripts/objectui-changeset-digest.mjs — right (card item 4). The console refresh changeset is patch whatever objectui declared, in RC pre-mode too; CONSOLE_BUMP still overrides; the declared level is derived, reported in the body ("declared X, recorded as patch") and on stderr; the generated file carries Metadata-protocol: none at column 0 so the gate and the release notes read it. inPreMode stays exported with no caller left in scripts/ — named in the PR, harmless. docs/releases-maintenance.md now states this grade, which it previously stated the other way.

  6. scripts/release-github-releases.mjs — right, with one interpretive widening named for the maintainer's Tier H read. The GitHub Release body is the CHANGELOG section regrouped under the five headings in the ruling's order and words (an English gloss after a dash on each); CHANGELOG.md stays as changeset version writes it; the splitter is fence-aware and opens a bullet only at a column-0 - , pinned on the real 16.1.0 section (same bullet count before and after); planRelease posts the grouped body through the unchanged truncation path; check:release-body green. The widening: 需手动处理 / 自动迁移 take every BREAKING signal the ADR-0087 gate reads — the banner, a ! summary, the (narrowing) arm, narrowed — where the card's bullet literally says "narrowed". So a non-protocol breaking change (a REST or driver narrowing at patch) lands under 需手动处理 with its kept banner rather than under 其他修复. I judge that right: the ruling's organising principle is "what the reader must do", ruling A keeps the BREAKING banner on exactly these entries, and filing a kept banner under "other fixes" would bury it; the mechanism is the dev's call by card item 5. It is the one place the implementation reads the ruling's intent over its letter — maintainer, if you meant 需手动处理 for protocol narrowings only, this is the line to say so on. 新增能力 also takes a non-breaking Clause-②: yes with none (a new REST endpoint); docs/releases-maintenance.md says so; the code docblock says "with no protocol kind", narrower than the code — a comment nit. Only a registered ADR-0087 disposition (the HTML-comment marker, read by the same spelling the ADR-0087 gate uses) moves an entry to 自动迁移; not-required leaves it under 需手动处理 — right, the reader still acts.

  7. Governed landing. Hits AGENTS.md and .github/workflows/pr-automation.yml make the whole PR Tier H; it is a draft with both GOVERNED_APPROVERS requested. This record is the contract-tier reading; it does not lift Tier H — the maintainer's approval does, and the queue guard (green on the head) will still want it.

② Semver level

  • skip-changeset — right. Every changed path is scripts/**, .github/**, docs/** or AGENTS.md; the root package is private; no published package's files[] or build reaches any of the five scripts (the only scripts/ paths reached from packages/** are the build helpers check-dts-emitted, tsup-drop-sources-content, check-test-typecheck, import-prerequisite). No .changeset/ file in the diff — the pending stock is not regraded (card item 6), so 18.1.0 is unaffected.
  • Clause-②: no — right: no accept set or public surface of a published payload moves (the PR's parenthetical is reasoning, not an arm).
  • Metadata-protocol: none — right: nothing a metadata author writes changes; the new line is a PR-body and changeset-body declaration, not a schema key, and moves no authorable-surface/ or authorable-defaults/ shard. Under its own gate this PR reads none-clean (no introduced entry, no baseline moved) and Check Changeset is green on the head.

③ Boundary flags

The dev's deviations, each answered:

  1. Attribution: all three commits end with Claude-Session: and the model-free Co-authored-by: Claude trailer; no model identifier anywhere in the diff or PR body. Accepted; the harness reminder is the exemption AGENTS.md names.
  2. no-path-population marker dropped from release-github-releases.mjs: in-claim file; the clause2-line.mjs import is now that family's one true path population and check:pm-dispatch-gates is green (1913/1913 by the report, Lint · PM dispatch-gates success). Accepted.
  3. docs/releases-maintenance.md edited off the claim's list: prose that stated the console grade the digest no longer produces, plus the five-section table; not a governed surface, no gate reads it, and the card's release-notes bullet names "the docs release page if one exists". Accepted as a named rider.
  4. H4 transition reading: informational, not a verdict. Accepted; carrier the seat.
  5. Label read-back: the relay line named no label; the independent GET lists skip-changeset and size/xl and Check Changeset passed on the head. The GET is the read-back of record for the label leg; the relay's missing leg is a fleet-write reporting gap outside this PR. Accepted.

out_of_scope_findings, each answered:

  1. The .claude/** claim template, os-dev.md and the pm-dispatch references still spell the declaration as Clause-② alone — escalated to the seat: until they carry Metadata-protocol:, every dispatched PR with a changeset reds not-measured-material once and clears by one body line. Out of this claim by the claim's own text; a Tier S skills-lane card should land close behind this PR. Not a FAIL here.
  2. The 10 open PRs with changesets turn not-measured-material on landing; four carry non-protocol minors ruling A grades patch — escalated to the seat for per-PR notes by the owning lanes. The fix(platform-objects)!: sys_oauth_application.type declares only web and native, the two application types its writer produces #25099 borderline (a platform object's field option set) is the maintainer's to rule; the PR's 维护者速读 already asks it.
  3. annotate-shipped's lead line — observation, no action owed.

open_questions: none declared, none found. My own flags: the WHICH LEVEL "refused as NOT MEASURED" overstatement (①.1) and the baselinesTouched silent-stand-down shape (①.2) are follow-up nits; the release-notes "breaking" widening (①.6) is for the maintainer's eye at the Tier H read.

Implemented-by: claude/issue-25105-changeset-protocol-levels
Reviewed-by: session_01VsqhiSrBjB1wJfRNcHPeXN

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

维护者速读 · domain:spec seat 1 · 2026-10-21T12:33Z

改了什么。 按您 2026-10-21 的裁决 A 落地「版本号只跟元数据协议走」。每个 PR 和 changeset 在 Clause-②: 旁边多写一行 Metadata-protocol: added | narrowed | semantic | none。门禁 check-changeset-no-major 据此判级:协议新增、收窄、语义变化 ⇒ 至少一个被改的包 minor;none ⇒ 一律 patch,带 minor 即拒;改了 authorable-surface/、authorable-defaults/ 基线却声明 none 也拒。console 刷新的 changeset 固定 patch。GitHub Release 正文按「需手动处理 / 自动迁移 / 新增能力 / 语义变化 / 其他修复」五段重排(CHANGELOG 本身不动)。AGENTS.md 第 3 条与 pr-automation.yml 的 "WHICH LEVEL" 规则文字同步改写。

为什么改。 现在几乎每次发版都是 minor:约一半待发 changeset 是 minor,真正动了元数据协议的只有十来个,版本号说不出协议动没动。裁决 A 把 minor 留给协议变化。「新增」也算 minor,因为 spec schema 不是 .strict(),旧运行时会静默丢掉不认识的键,用新键的应用要能写出一个 minor 底线。

风险与代价(含回滚)。

  • 过渡:现开的 12 个 PR 里有 10 个带 changeset,本 PR 合并当天都会在这个门禁上读到「未声明」而红。每个在正文补一行即清,不用推送;其中 4 个是非协议的 minor,按新规要改成 patch。合并时席位逐个通知所属车道。
  • 派发流程:认领模板和 os-dev 定义还不认识新行,已立 pm-dispatch / os-dev: the claim template and the dev definition carry only the Clause-②: line, so a dispatched PR never writes the Metadata-protocol: line ruling A (#25105) adds #25115 交 skills 车道,应紧随本 PR 落地;在那之前,新派发的 PR 都会先红一次再补行。
  • 一处按意图而非字面落地:Release 正文的「需手动处理」收所有带 BREAKING 标记的条目,包括非协议的 REST、driver 收窄,而裁决原文这一段写的是 narrowed。理由是这些条目按裁决保留了 BREAKING 标记,读者确实要处理,放进「其他修复」会被埋掉。
  • 基线检查只看得见键名和默认值;拒值、枚举收窄、语义变化仍靠作者如实声明。
  • 回滚:整个 PR revert 即可,无数据迁移,不发布任何包(skip-changeset)。

席位意见。 建议批准。上限档契约复审 PASS(记录 6315965482,head 99eaf8350);同一 head 上 45 个检查 32 绿、13 跳过、0 红。规则文字与裁决 A 的四行表一致;门禁只放开了裁决 A 明确改掉的两处耦合(Clause-②: yes 和 (narrowing) 不再强制 minor),其它判定一条没放松,也没有新增门禁。复审提的两处小瑕疵(WHICH LEVEL 有一句把一个边角情形说得比门禁严;一个读不到的 git 失败会被静默当作「没动基线」)不影响判定,随本 PR 下一次代码推送带上;若没有下一次推送,另行跟进。

你要做的。 在 PR #25114 上批准(重点看 AGENTS.md 第 1084–1101 行和 pr-automation.yml 的 "WHICH LEVEL" 段);批准后席位过落地前检、转 ready、入队。若您要「需手动处理」只收协议收窄,改为在 PR 上留一句,席位按此返工。

席位核对

Seat record of domain:spec seat 1 (seat post #6017) · session session_01VsqhiSrBjB1wJfRNcHPeXN · verdict ACCEPT (Tier H: awaits an authorized approval; not readied, not queued) · head 99eaf835091373dde2a9b0bcd84f24fa0ff5b5d1.

  1. Shape. Draft, base main, first line Fixes #25105; Clause-②: no (…) and Metadata-protocol: none (…) at line start; 8 files, +1347 / −295 (check-governed-merges.mjs --pr 25114: Tier H on AGENTS.md and .github/workflows/pr-automation.yml, 1642 changed lines ≤ 3000); assignee huangyiirene; reviewers hotlong and os-zhuang requested (GOVERNED_APPROVERS); labels skip-changeset (right: every path is scripts/**, .github/**, docs/** or AGENTS.md, no package's files[] reaches them), size/xl, documentation, ci/cd.
  2. Premises measured. Before the claim, on origin/main 5050442bb9: every path the card names exists; "WHICH LEVEL" at pr-automation.yml:754; the Clause-② sentence at AGENTS.md:1087; no open PR and no in-flight claim in any lane touched the surface. The dev re-measured H1–H5 at the same base (PR body, "Measured against the dispatch hypotheses").
  3. Gate check-runs (head-pinned, read 2026-10-21T12:33Z): 45 runs, 32 success, 13 skipped, 0 failed, 0 in progress. Lint & Repo Gates, TypeScript Type Check, Test Core (6/6), Check Changeset and Governed Surface Queue Guard all success. Contract review of record: 6315965482, Served-tier: CONTRACT_REVIEW_TIER, rules face, PASS on this head, adopted by this seat.
  4. Accepted deviations.
    • docs/releases-maintenance.md is off the claim's list. It is a prose rider stating the new console grade and the five sections; the seat read both hunks sentence by sentence against the diff (the bump-objectui.sh paragraph; the new "Release notes are grouped by what the reader must do" table), and both match the code.
    • AGENTS.md item 3 was condensed to stay at its 1117-line ceiling. The two dropped retirement details live in the skill the item defers to: .claude/skills/spec-property-retirement/SKILL.md (the *_RETIRED_KEY_GUIDANCE row, and the tombstone-keeps-its-ledger-row table), read on origin/main.
    • check:release-body's no-path-population marker was dropped; the reader import makes clause2-line.mjs that family's path population; Lint · PM dispatch-gates is success.
    • The relay's pr_create read-back named no label; an independent GET lists skip-changeset and Check Changeset passed. The GET is the read-back of record.
  5. Out-of-scope dispositions.
    • Dev finding 1 (.claude/** claim template, os-dev.md:280, execution-duties.md:70 carry only Clause-②:): filed pm-dispatch / os-dev: the claim template and the dev definition carry only the Clause-②: line, so a dispatched PR never writes the Metadata-protocol: line ruling A (#25105) adds #25115, pm:blocked, Blocked-by: #25105, for the skills lane.
    • Dev finding 2 (10 open PRs read not-measured-material on landing; 4 non-protocol minors to regrade): seat landing duty. One note per PR to its owning lane in the landing act (clause 6).
    • Dev finding 3 (annotate-shipped lead line): dropped. It is an observation with no wrong output; the release-body splitter reads the whole bullet.
    • Review nits ①.1 (WHICH LEVEL "refused as NOT MEASURED" overstates the inherited moot lane), ①.2 (baselinesTouched swallows a git diff failure) and ①.6 (a docblock narrower than the code): ride this PR's next code push if one comes, else a follow-up after landing.
  6. Landing clause. Stays draft until an authorized approval (GOVERNED_APPROVERS) is on record. Then, in one landing window: re-read CI on the then-current head, mergeable_state, and check-governed-merges.mjs --pr 25114; remove needs-user-decision; relay pr_ready + automerge_enable; confirm added_to_merge_queue; confirm MERGED on origin/main; post the transition note on each open PR that introduces a changeset; release pm-dispatch / os-dev: the claim template and the dev definition carry only the Clause-②: line, so a dispatched PR never writes the Metadata-protocol: line ruling A (#25105) adds #25115 for triage's unlock scan.

Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd documentation Improvements or additions to documentation needs-user-decision size/xl skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants