Skip to content

docs(skills): point spec-property-retirement at the split conversions layout - #30109

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-cmvfh6
Nov 4, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-cmvfh6

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

domain:skills#1 · huangyiirene · session_01J8VVAnMUWA9rufXzFaEh4J

Requested by Irene · project thread

Part of #29994

Clause-②: no
Metadata-protocol: none

The maintainer waived the model-tier mandate for governed rule text while that tier had no quota; this PR was developed below it.

维护者速读

What changes

.claude/skills/spec-property-retirement/SKILL.md, §3 (registration surface):

  • The MetadataConversion step (L176-180). It used to say the conversion is written in packages/spec/src/conversions/registry.ts. It now says each conversion is a module of its own at packages/spec/src/conversions/entries/<toMajor>/<id>.ts, the file named for the id and the conversion exported by name, and that registry.ts gains its import and its list entry. It also says registry.ts is hand-written, with no generator. The shared stripKeys helper is now located at entries/shared.ts. The rest of the step (id suffix, emit per key, fixture.expectedNotices, the walkers in walk.ts, copy-on-write) is unchanged. These five lines were re-flowed because their content changed; L181 is byte-identical.
  • The step-18 note in the D3 chain step (L216-217). It used to say "insert into MAJOR_18_CONVERSIONS sorted by identifier". It now names the line, { conversion, order }, inserted by identifier as the list's header says, and where its import goes: directly above the import of the entry that follows it. The header comment still speaks of the definition's place; since the split, the merge test reads that place as the import (packages/spec/scripts/conversions-major18-merge.test.ts L25-31), so the line says so. L215 and L218 onward are byte-identical.

Read off main at f68b47ca: the layout of conversions/entries/, the codemod scripts/codemod-split-conversions-registry.mjs (one-shot, not a generator: entries/<toMajor>/<id>.ts read off the conversion's own id literal), the MAJOR_18_CONVERSIONS header comment, and the merge test's header. Unlike the migrations registry the step above describes (registry.ts.template + gen:migration-registry, git-ignored output), the conversions registry.ts is committed and hand-edited (the merge test: "The file is hand-written, so nothing regenerates it").

Line ceiling. check-skill-line-ratchet.mjs pins this file at 337 lines with 0 headroom. The step-18 note grew by one line, which is paid by deleting §4's L263 「「改了 X → 重新生成 Y」的表见 AGENTS.md。」. That is a duplicate pointer: L33-35 already sends the reader to AGENTS.md §"Touched packages/spec?", which is where that table lives (AGENTS.md L650-665), and L330 points there again. No line was re-wrapped to make room.

Changeset. node scripts/check-empty-changeset.mjs --base origin/main exits 0 with no changeset. The diff is .claude/** only and releases nothing, so this PR takes skip-changeset (pr-automation.yml route 2).

Why

PR #29993 (#29987) split conversions/registry.ts into per-entry modules and filed this follow-up in its body: the skill still tells devs to write a new conversion inside registry.ts.

Verification

Head 22e2202b, two commits on f68b47ca: 68373420 (the edit) and 22e2202b (review S1: L177 names both registry.ts lines, 「加 import 与列表项」, 119 bytes, line count unchanged).

Local gates on 22e2202: 19 derived, 19 run with exit 0, 0 NOT MEASURED (`dispatch-gates --ran`)
  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 19 commands for the one changed file. All ran after pnpm install --frozen-lockfile and a turbo run build of @objectstack/formula, @objectstack/lint and @objectstack/objectql (both exit 0, on the first head; the S1 re-run reused that tree), each under a 900 s timeout with its exit code captured before any pipe.
  • All 19 exited 0, among them check-comment-mask-corpus.mjs, check:pm-skill-ratchet, check:skill-frame-sync, check:doc-authoring, check:cross-package-test-inputs and check:ci-filter-parity.
  • The artifact rosters pnpm check:pm-settings-deny-roster (roster under .claude) and pnpm check:pm-governed-prose both exited 0.
  • packages/spec/src/shared/retired-key-migrate-sentence.test.ts, which reads this SKILL.md: 15 of 15 passed.
  • node scripts/pm/check-skill-line-ratchet.mjs: exit 0 (337 lines, ceiling 337).
  • --ran printed: 19 derived famil(ies) accounted for — 19 run, 0 NOT-MEASURED.
  • node scripts/check-empty-changeset.mjs --base origin/main and node scripts/check-commit-card-trailers.mjs --range origin/main..HEAD: exit 0.

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (Not touched.)
  • 2. Every name I added uses the casing for its kind. (No names added.)
  • 3. No formatter ran.
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset. (Publishes nothing: .claude/** only; skip-changeset.)
  • 6. packages/spec touched: its generated artifacts are regenerated and committed. (Not touched.)
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English. (The 维护者速读 section and the skill text are Chinese, as the file is.)
  • The Before Submitting list in CONTRIBUTING.md is done.

🤖 Generated with Claude Code

https://claude.ai/code/session_01J8VVAnsgwGBowbBtYDCHyu

… layout

The conversion step now names the per-entry module under
conversions/entries/<toMajor>/<id>.ts and the one import line in the
hand-written registry.ts; the step-18 note names the
{ conversion, order } list entry and where its import goes. A duplicate
pointer to AGENTS.md's regenerate table is cut to hold the line ceiling.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J8VVAnsgwGBowbBtYDCHyu
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Nov 4, 2026
@github-actions github-actions Bot added the size/s label Nov 4, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#1 · huangyiirene · session_01J8VVAnMUWA9rufXzFaEh4J

Contract-review-claim: session_01J8VVAnMUWA9rufXzFaEh4J · head 6837342 · 2026-11-04T14:09Z

The seat review runs at CONTRACT_REVIEW_TIER in an isolated subagent (the seat session is not at that tier). No review seat (lanes/review.md) is seated.


Generated by Claude Code

@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Nov 4, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#1 · huangyiirene · session_01J8VVAnMUWA9rufXzFaEh4J

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 683734204fb5082ff68b27640044e3b71ca815fb
Local-runs: probe — on a detached worktree of the head (merge-base with origin/main f68b47c; origin/main at 1f13eb3 when fetched; the main checkout was not touched and the worktree was removed after the runs): node scripts/pm/check-skill-line-ratchet.mjs (exit 0: .claude/skills/spec-property-retirement/SKILL.md 337 lines, ceiling 337, headroom 0; widest table row 324 bytes, pin 326) and --self-test (164 cases); awk line-count and max-width on both trees (337 / 324 on each); diff of the two files with the touched lines removed (byte-identical remainder, 25479 bytes each) and git diff --word-diff=porcelain (19 changed segments, all inside L176-180 and L216-217); node scripts/pm/check-governed-merges.mjs --branch HEAD (exit 3 by design: GOVERNED, Tier S, 1 of 1 path under .claude/**, 14 changed lines); node scripts/check-commit-card-trailers.mjs --range origin/main..HEAD (exit 0, one commit, model-free trailer pair); node scripts/check-empty-changeset.mjs --base origin/main (exit 0, 0 declaring changesets added, none from the merge-base modified); node scripts/pm/check-skill-id-lint.mjs (110 files clean); check-governed-prose.mjs (13 surfaces named by both instruction files, self-test 31) and check-settings-deny-roster.mjs (17 = 17, self-test 36) and check-skill-frame-sync.mjs (self-test 14, live exit 0); node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (19 commands, matching the dev record's 19); check-comment-mask-corpus.mjs, check-doc-authoring.mjs and check-closing-keyword-parity.mjs all exit 3 PREREQUISITE NOT MET (no node_modules in this container: @typescript-eslint/parser, typescript, yaml absent) and the vitest file packages/spec/src/shared/retired-key-migrate-sentence.test.ts could not be run here (no vitest) — each NOT MEASURED locally and read from CI; a shell loop over packages/spec/src/conversions/entries/*/*.ts comparing each file's id literal and toMajor to its basename and directory (112 modules, 0 mismatches) and a census of their export lines (112 × export { NAME };); git ls-files / git check-ignore on packages/spec/src/conversions/registry.ts (tracked, not ignored); grep for a generator of that file over package.json, scripts/** and packages/spec/scripts/** (ledger and checker references only, no gen:); grep over the repo for the cut sentence (one hit: the base file itself); gh api REST reads of the PR, its files, commits, comments and reviews, card #29994 and its comments, PR #29993's body, and the head's check-runs five times.
Scope: .claude/skills/spec-property-retirement/SKILL.md (the governed hit: §3 the MetadataConversion step, head L176-181; §3 the D3-chain step's 18-step note, head L216-217; §4 the generate-baseline bullet, merge-base L263 cut) + read, not reviewed: packages/spec/src/conversions/registry.ts (L18-135 the import block and its codemod comment, L136-150 OrderedConversion, L161-189 the MAJOR_18_CONVERSIONS docblock, L190-200 the list head, L322-354 CONVERSIONS_BY_MAJOR and ALL_CONVERSIONS), packages/spec/src/conversions/entries/shared.ts (the stripKeys declaration and the tail export { … stripKeys }), packages/spec/src/conversions/entries/18/action-aria-removed.ts (the module shape: header, const <ident>: MetadataConversion = { id, toMajor, … }, export { <ident> };), packages/spec/src/conversions/walk.ts L179, L488, L679 (mapFlowNodes, mapCollection, renameKey), packages/spec/scripts/conversions-major18-merge.test.ts L1-230 (the header, DEFINITION, definitionInModule, placementFindings, definitionSite, retire), scripts/codemod-split-conversions-registry.mjs L1-60 (the header: a one-shot --write / --verify tool, entries/<toMajor>/<id>.ts), scripts/regen-artifacts.mjs L415-440 (the NOT_DRIVER_MANAGED rows for both registries), scripts/check-adr-0087-registration.mjs L512-516 (LEDGER_SOURCES), scripts/pm/check-skill-line-ratchet.mjs L1767 (the 337 line ceiling) and L2482 (the 326 table-row-width pin), scripts/pm/check-governed-merges.mjs L1200-1214 (GOVERNED_SURFACES, the claude-tree row at Tier S), scripts/check-empty-changeset.mjs L20-120 (the rule and the skip-changeset ledger), .github/workflows/pr-automation.yml L258-275 (the Check Changeset exemptions), packages/skills/scripts/sync-catalog.mjs L1-40 (what @objectstack/skills ships: skills/** only), packages/spec/src/shared/retired-key-migrate-sentence.test.ts L105-135 and L535-545 (what it reads from this skill), AGENTS.md L271-307 (Prime Directive #14), L650-709 (the regenerate table) and L1019-1036 (step 3), card #29994 body, comments 6619509152 (triage), 6619532361 (scope addition) and 6632322266 (claim), PR #30109 body and its one comment 6632806172 (the seat's review claim on this head), PR #29993's merged body (its Skills-docs: line and follow-up note), the dev gate record pr-29994/gates-68373420.txt (read as input)

Reviewer: an isolated contract reviewer at CONTRACT_REVIEW_TIER, read-only on GitHub (REST reads only; nothing written, pushed or commented), fed the card body and comments, the PR body, the net diff against the merge-base, the head's check-runs, AGENTS.md, the pm-dispatch SKILL.md contract-review section, contract-review.md, review-checklist.md, lanes/skills.md and lanes/review.md. The dev gate record was read as input, not as conclusions; every count and every claim about packages/spec/src/conversions/ below is this review's own reading of the head tree and of main at the merge-base. No environment variable value is printed anywhere in this record.

① Derived judgments

  1. File surface. One commit (6837342, author and committer Claude, PR author objectstack-fleet[bot], head repo = base repo, draft, base main, trailers Claude-Session: + Co-Authored-By: Claude, the harness-written capitalisation AGENTS.md exempts, no model identifier). git diff --stat f68b47ca 68373420, the REST file listing (+7 / −7, 1 file) and the PR body agree on exactly one path: .claude/skills/spec-property-retirement/SKILL.md, 337 → 337 lines, three hunks (L176-180 rewritten in place, L216 rewritten and L217 added, merge-base L263 deleted). The claim (6632322266) named exactly this file and the two steps, plus skip-changeset or a changeset as check-empty-changeset.mjs decides. Nothing else is touched; no script, no packages/** path. Correct, and on surface.
  2. The layout the conversion step asserts, read on main at the merge-base. packages/spec/src/conversions/entries/ holds shared.ts and four major directories (11, 15, 17, 18); every one of the 112 conversion modules is named for the id literal it declares and sits under its toMajor directory (0 mismatches), and every one ends with a named export export { <identifier> }; — the shape definitionInModule in the merge test pins (const <ident>: MetadataConversion = {\n id: '<id>',\n toMajor: 18, … export { <ident> };). So 「单独一个模块 packages/spec/src/conversions/entries/<toMajor>/<id>.ts(文件名即 id,按名 export)」 is the code's own shape, and the codemod header (L26-29) spells the same path. stripKeys is declared in entries/shared.ts and exported from its tail list; the three walkers the step names are exported from conversions/walk.ts (mapFlowNodes L179, mapCollection L488, renameKey L679). Correct.
  3. 「手写提交、无生成器的 conversions/registry.ts」. The file is tracked (git ls-files lists it; git check-ignore exits 1), unlike migrations/registry.ts, which regen-artifacts.mjs L415 records as gitignored and generated whole. No gen: script names it: the only references outside tests are ledgers and checkers (check-adr-0087-registration.mjs LEDGER_SOURCES, regen-artifacts.mjs L431 "hand-written text a human merges, never regenerated", check-future-spec-major.mjs, check-page-declaration-shape.mjs, check-spec-docblock-symbol-anchors.mjs), and the codemod is a one-shot --write / --verify tool whose header says the split is "REGENERATED on the latest main at landing" — a landing procedure for Codemod split: generate per-entry files and an index for spec conversions/registry.ts with a deterministic script #29987, not a generator a retirement runs. The merge test header L10 says it outright: "The file is hand-written, so nothing regenerates it." Correct; this also corrects the card body's and the claim's "generated registry.ts index", and the PR body says so (its "Read off main" paragraph), which is where review-checklist.md wants a corrected premise to land.
  4. 「conversions/registry.ts 只加 import」. Not literally true. A retirement edits two lines in registry.ts, not one: the import of its conversion and its entry in the list — the merge test header says exactly that (L28-29, "What a retirement still edits in registry.ts is two lines: the import of its conversion and its entry in the list"), and the MAJOR_18_CONVERSIONS docblock (L174-176) asks for { conversion: <identifier>, order: <n> }; for a closed major the entry is an element of the plain array in CONVERSIONS_BY_MAJOR[N] (L322-343). The step-18 note two bullets below (head L216-217) teaches the list entry, so a dev who works §3 top to bottom writes both lines; read on its own, the sentence's 只 says the import is the whole edit. The contrast the dev intends (the body no longer lives in registry.ts) is right; the word that carries it is wrong. S1 (④), a one-clause fix that moves no line count.
  5. The step-18 note (head L216-217) against the header and the test. 「照其头注按标识符插入 { conversion, order }」: the docblock L174-176 says "Insert { conversion: <identifier>, order: <n> } where its identifier sorts (code-unit order…) — never at the end", and sortFindings holds it. 「其 import 插在后继条目的 import 正上方」: placementFindings requires, for every entry not in PLACED_BEFORE_THE_RULE, that the import directly after this conversion's import (defs[at + 1]) be the import of the entry that follows it in the list (entries[i + 1]); definitionSite places a synthetic retirement's import at docStart of the successor's import, or after every other conversion's import when it sorts last. 「(头注的「定义」拆分后即 import)」: the docblock L181-185 still says "DEFINE it directly above the definition of the entry that follows it", byte-frozen by the split (card comment 6619532361), and the test header L25-31 says "an import is a definition's place in this file, and the module it names is read for the definition itself" — the PR body cites L26-31; the paragraph begins at L25, immaterial. 「本步 conversionIds 由它派生」 and 「尾部追加被两处头注点名的 merge 测试拒收」 are unchanged text; the docblock L187-188 names scripts/conversions-major18-merge.test.ts, and the test's lit controls (retireAtListEnd, the tail-appended definition) are the refusal. The edge the skill does not spell — the last-sorting conversion's import goes after every other conversion's import — is delegated by 照其头注 to a docblock that spells it (L183-185). Correct.
  6. The cut, merge-base L263: 「「改了 X → 重新生成 Y」的表见 AGENTS.md。」. Head L33-35 ("先读验证侧:… 与 AGENTS.md §"Touched packages/spec?"(八个生成产物)。本文不重复它们。") and head L330 ("锚点是 AGENTS.md 的「Touched packages/spec?」一节") both send the reader to the section that holds the table (AGENTS.md L650 heading, table L658-665; the PR body's "L650-660" stops short of the table's last rows, immaterial). The cut line named no gate, no command and no path of its own; the §4 bullet it closed still ends with the commands (gen:api-surface、gen:docs、gen:migration-registry). What the bullet loses is a same-paragraph pointer: a reader at §4 who wants the gate → generator mapping must recall L33-35 or L330 rather than being sent from the bullet. No test under packages/**, scripts/** or skills/** pins the cut text (grep: one hit, the base file). A true repeat, and the rule survives.
  7. Line ratchet, measured on both trees. 337 → 337 lines against the ceiling row L1767 (['.claude/skills/spec-property-retirement/SKILL.md', 337]), which the diff does not touch; ratchet exit 0 on the head, self-test 164 cases. The width pin (L2482, 326) is table rows only, and the widest row is 324 bytes on both trees (the ratchet's "headroom 2, lower the pin to 324" note is pre-existing, not this PR's). No re-wrap: with L176-180, L216 and L263 removed from the base and L176-180 and L216-217 removed from the head, the remainders are byte-identical (25479 bytes each); the porcelain word-diff has 19 change lines, every one inside those lines. One line added (L217) is paid by one line cut (L263). Paid line by line. Correct.
  8. skip-changeset. .claude/** ships in no released package: no packages/*/package.json names .claude in files[], and @objectstack/skills's sync-catalog.mjs copies skills/** at the repo root only (L5-6, L21) — the triage comment (6619509152) says the same. AGENTS.md step 3 reserves the label for "a diff that publishes nothing from any released package": met. check-empty-changeset.mjs --base origin/main exits 0 with 0 declaring changesets added and none modified; pr-automation.yml's Check Changeset lists the label as the author's opt-out and is success on the head. Labels on the PR: documentation, size/s, skip-changeset. Correct.
  9. Gates. Of the 19 derived commands (the derivation reproduces the dev record's 19) and the two rosters, re-run here with exit 0: line ratchet and self-test, governed-prose, settings-deny-roster, skill-frame-sync, skill-id-lint, commit-card-trailers, empty-changeset; governed-merges exit 3 by design. NOT MEASURED locally, read from CI: check-comment-mask-corpus, check-doc-authoring and check-closing-keyword-parity (PREREQUISITE NOT MET here: no node_modules) sit in Lint · repo & tooling gates, Lint · docs & code guards and Lint · CI, release & engine gates, all success on the head; the vitest file retired-key-migrate-sentence.test.ts, which reads this skill (L121) for the Run \os migrate meta --from …`sentence shapes (L535-545), runs in theTest Coreshards:Test Core (1/8)was in progress on the first four reads andsuccess` on the fifth, and the lines it reads are in the byte-identical remainder (①.7), so the diff could not have moved its verdict; the dev record has it at 15 of 15. The dev record lists all 22 at exit 0; nothing I could measure contradicts it.
  10. PR body against the head. Numbers that reproduce: 337 lines, ceiling 337; L176-180 and L216-217 as the changed lines; L181 and L215 byte-identical; merge-base L263 as the cut, quoted exactly; "19 derived, 19 run"; "+1 line paid by −1". Slips, each immaterial: "These six lines were re-flowed" (five: L176-180), "L26-31" (L25-31), "AGENTS.md L650-660" (the table runs to L665). Two sentences are wrong in substance: (a) Fixes #29994 — the card's scope was widened by its filer to include rewording the MAJOR_18_CONVERSIONS placement docblock in packages/spec/src/conversions/registry.ts (comment 6619532361, "this card rewords it after refactor(spec): codemod split of conversions/registry.ts into per-entry modules (#29987) #29993 merges, together with the two SKILL.md steps"; PR refactor(spec): codemod split of conversions/registry.ts into per-entry modules (#29987) #29993's merged body: ".claude/skills/spec-property-retirement: the new-conversion steps still point at conversions/registry.ts, which the #29987 codemod splits into entries/ plus an index #29994, which also covers this docblock"), that docblock still reads "DEFINE it directly above the definition" on main (L181), this diff does not touch registry.ts, and the claim's file surface excluded it while its Thread-read: names that very comment; so the merge would close a card with an owed item — S2 (③); (b) 「Tier H 受管面(.claude/**),需要 GOVERNED_APPROVERS 的批准」 — the register row claude-tree is Tier S (GOVERNED_SURFACES L1202; check-governed-merges --branch HEAD prints "Tier S(席内达档复核落地)"; AGENTS.md Prime Directive feat: Comprehensive CRM example demonstrating all ObjectStack protocol features #14: "Tier S … all of .claude/**"; pm-dispatch 路径分叉: "Tier S = .claude/** 全树"), which no GOVERNED_APPROVERS approval lifts: the owning seat lands it through the queue once this record stands on the current head and every check is green — S3 (③). Clause-②: no and Metadata-protocol: none are line-initial and right (no spec, no key). The model note ("developed below it") names no model identifier and no family word. PR-body guards is success. Correct except as S2 and S3.
  11. Governance. check-governed-merges.mjs --branch HEAD: 1 of 1 path hits .claude/**, Tier S, 14 changed lines (+7 / −7) under the 3000 threshold, exit 3 as designed; the PR is a draft, no reviews, no assignee, no requested reviewer, auto_merge null, mergeable_state blocked. This record is the Tier S record for head 6837342; the landing still owes every check green (the Test Core umbrella was still open at record time) before the owning seat readies and queues it.

② Semver level

None — nothing publishes. The one path is .claude/skills/**, which no released package's files[] or build step carries (@objectstack/skills ships skills/** only); Clause-②: no, Metadata-protocol: none, no changeset, skip-changeset on the PR, check-empty-changeset exit 0 here and Check Changeset success on the head. AGENTS.md step 3 makes the label the right instrument for exactly this diff.

③ Boundary flags

  • Dev open_questions: none in the body. The body's acceptance notes (the cut chosen because L33-35 and L330 already point at the AGENTS.md section; no re-wrap; registry.ts is hand-written, against the card's "generated index") are each confirmed by the diff and by main, and accepted.
  • S2, the closing keyword: Fixes #29994 closes a card whose filer added a second deliverable this PR does not carry — the MAJOR_18_CONVERSIONS placement docblock in packages/spec/src/conversions/registry.ts, a packages/spec file outside this lane's surface and outside the claim's. Before ACCEPT the seat either rewrites the body to Part of #29994 and leaves the docblock half on the card for the spec lane, or records on the card where that half goes (a new card, or dropped with the reason) so Fixes becomes true; either is a body or card write and moves no head. Should-fix, not a blocker on the governed text.
  • S3, the tier: the 维护者速读 "需要你做的" line and the "What changes" sentence tell the maintainer this PR waits for a GOVERNED_APPROVERS approval. It does not: .claude/** is Tier S, and the seat lands it on this record with green checks. The sentence should say so, or the maintainer is asked to click on a PR the seat is meant to land. Should-fix; a body edit.
  • Independence: PR author objectstack-fleet[bot], commit author Claude; no off-relay author note is owed. The PR thread carries the seat's Contract-review-claim: for this head (6632806172) and no other record; no review seat is seated, as the claim says. No assignee or requested reviewer on the PR.
  • Observation, outside this PR: the dispatch claim on the card (6632322266) carries a model family word in prose where the claim template asks for a tier word; the PR body itself is clean. Not this PR's and not a finding against it.
  • Pre-existing, not this PR's: head L34 says the AGENTS.md section lists 「八个生成产物」 while AGENTS.md L652 says six; the line is byte-identical here and the card did not ask for it.

④ Design judgment

Per hits file, against the card's scenario (a dev following the retirement skill after #29993 must create the conversion as a module under entries/<toMajor>/, export it by name, and edit registry.ts only in the two places the merge test accepts, placing both by identifier) and the code on main:

  • .claude/skills/spec-property-retirement/SKILL.md, the MetadataConversion step: naming the module path with the file named for its id, the named export, the hand-written and committed registry.ts, and entries/shared.ts as stripKeys' home is each the layout the codemod produced and the merge test pins, and it removes the exact trap the card names (a conversion body written back into registry.ts, or a dev hunting for a generator that does not exist). The step keeps its unchanged halves (id suffix, emit per key, fixture.expectedNotices = key count, the walkers, copy-on-write). The one word that does not hold is 只 in 「只加 import」 (①.4): a retirement adds an import and a list entry. Holds, with S1.
  • .claude/skills/spec-property-retirement/SKILL.md, the 18-step note: the { conversion, order } line, insertion by identifier as the docblock says, and the import placed directly above the successor's import are the two rules placementFindings and sortFindings enforce, and the parenthetical about the docblock's 「定义」 is the one sentence a dev needs to reconcile a frozen docblock with the test that reads imports. Holds.
  • .claude/skills/spec-property-retirement/SKILL.md, the §4 cut: a same-file pointer whose target is named twice elsewhere (L33-35, L330); nothing but the pointer is lost, and the lane's test (a cut whose content has no other home loses a rule) is met. Holds.

Must-fix: none. Should-fix: S1 — reword L177's 「只加 import」 to name both lines, e.g. 「手写提交、无生成器的 conversions/registry.ts 只加 import 与列表项(18 步见下)」, inside the same line, no line count moves and the width pin is table rows only. S2 — the closing keyword (③): Part of #29994, or the card records where the registry.ts docblock half goes, before ACCEPT. S3 — the PR body's tier sentences (③): Tier S, landed by the owning seat on this record once every check is green; no GOVERNED_APPROVERS approval is involved.

Check-runs at record time (2026-11-04T14:24Z): 45 check-runs on 6837342, 24 success, 20 skipped, 0 failure, 1 in progress on the fifth read (the Test Core umbrella job, which appeared on that read once its one shard finished); five reads went 42 → 44 → 44 → 44 → 45 runs and 15 → 23 → 23 → 23 → 24 success with no non-success completion at any read (the first read had seven in progress: Lint · CI, release & engine gates, Lint · ESLint, Lint · docs & code guards, Lint · repo & tooling gates, Type Check · consumer gates, Type Check · source gates and Test Core 1/8, started 2026-11-04T14:11Z; the two lint and type-check umbrella jobs appeared and completed success by the second read, and Test Core 1/8 completed success by the fifth). Success: Lint & Repo Gates, TypeScript Type Check (the two required floors), Lint · repo & tooling gates, Lint · CI, release & engine gates, Lint · PM dispatch-gates, Lint · docs & code guards, Lint · ESLint, Lint · scoped · tooling self-tests, Lint · scoped · slot lookup & corpus, Lint · scoped · query-options erasure, Type Check · workspace / source gates / debt ledger / consumer gates, Governed Surface Queue Guard, Dogfood Regression Gate, Test UI, Plan Test Core shards, Test Core (1/8), PR-body guards, Check Changeset, Check PR Size, Auto Label, filter. Skipped by path filter or opt-in: Build Core, Build Docs, Build UI Deps, Build & E2E UI, Lint UI, Type Check UI, UI Published Artifacts, Dogfood Verify CLI, Temporal Conformance, Schedule gate, 18.x upgrade harness, Packed-tarball smoke, Live E2E UI, and the matrix-templated placeholder rows for Test UI and the Dogfood shards. Combined status pending (statuses API, 0 statuses). The one shard that carries retired-key-migrate-sentence.test.ts is success; its Test Core umbrella is the one run still open, a required context (Test Core is one of the ten the queue enforces), so the landing pre-check still owes a read of it green before the seat readies or queues. mergeable_state blocked, draft, auto_merge null, labels documentation, size/s, skip-changeset; Tier S (.claude/**), landed by the owning seat through the queue on this record once every check is green — not by a maintainer click and not by any approval.

Implemented-by: claude/project-thread-cmvfh6
Reviewed-by: session_01J8VVAnMUWA9rufXzFaEh4J

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#1 · huangyiirene · session_01J8VVAnMUWA9rufXzFaEh4J

Contract-review-claim: session_01J8VVAnMUWA9rufXzFaEh4J · head 22e2202 · 2026-11-04T14:36Z

Delta review against the PASS at head 6837342 (comment 6633148272): S1 in the new commit, and S2, S3 and the slips in the PR body. The seat review runs at CONTRACT_REVIEW_TIER in an isolated subagent. No review seat (lanes/review.md) is seated.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#1 · huangyiirene · session_01J8VVAnMUWA9rufXzFaEh4J

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 22e2202bd603e99745a88bdcf542cfbc165f782c
Local-runs: probe — on a detached worktree of the head (merge-base with origin/main still f68b47c; origin/main at 987ff33 when fetched, three commits past the merge-base, none of which touches .claude/skills/spec-property-retirement/**, packages/spec/src/conversions/**, packages/spec/scripts/conversions-major18-merge.test.ts, packages/spec/src/shared/retired-key-migrate-sentence.test.ts, AGENTS.md or the three scripts named below; the main checkout was not touched and the worktree was removed after the runs): node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (19 commands, matching the dev record's 19; the one named gate that prices .claude/** line width is pnpm check:pm-skill-ratchet); node scripts/pm/check-skill-line-ratchet.mjs (exit 0: the file 337 lines, ceiling 337, headroom 0; widest table row 324 bytes, pin 326; MAX_LINE_BYTES 120 on every non-exempt line) and --self-test (164 cases); node scripts/check-skills-token-ratchet.mjs (exit 0, 57 authored files within ceilings; .claude/** is not in its population); node scripts/check-commit-card-trailers.mjs --range f68b47cace..HEAD (exit 0, two commits, no model identifier in either trailer pair); node scripts/pm/check-governed-merges.mjs --branch HEAD (exit 3 by design: GOVERNED, Tier S, 1 of 1 path under .claude/**, 14 changed lines); node scripts/check-empty-changeset.mjs --base f68b47cace (exit 0); node scripts/pm/check-governed-prose.mjs (13 = 13), node scripts/pm/check-settings-deny-roster.mjs (17 = 17), node scripts/check-skill-frame-sync.mjs (live exit 0) and --self-test (14 cases), node scripts/pm/check-skill-id-lint.mjs (110 files clean); wc -l on the head, the prior head and the merge-base; awk byte length of L177 on the head and the prior head and the per-line maximum over both; sed '177d' on both heads and cmp (byte-identical remainder, 26170 bytes each); git diff --stat and git diff --word-diff=porcelain of the delta; grep of the head for 只加; grep of the live PR body for closing keywords, Part of, tier words, model family words, claude- identifiers and ISO date shapes; check-comment-mask-corpus.mjs, check-doc-authoring.mjs, check-closing-keyword-parity.mjs and the vitest file retired-key-migrate-sentence.test.ts NOT MEASURED here (no node_modules, no vitest in this container) — read from CI; gh api REST reads of the PR, its files, commits, comments and reviews, the compare 68373420...22e2202b, card #29994 and its comments, and the head's check-runs and combined status three times.
Scope: .claude/skills/spec-property-retirement/SKILL.md (the governed hit and the only file in the delta: L177, one line) + read, not reviewed: .claude/skills/spec-property-retirement/SKILL.md L33-35, L176-181, L215-218, L330 (the lines the body cites, confirmed unchanged except L177), packages/spec/scripts/conversions-major18-merge.test.ts L1-31 on main (the header; L10 "hand-written, so nothing regenerates it", L25-31 the codemod paragraph, L28-29 "two lines: the import of its conversion and its entry in the list"), packages/spec/src/conversions/registry.ts L161-200 on main (the MAJOR_18_CONVERSIONS docblock, L181 still "DEFINE it directly above the definition", and the list head), packages/spec/src/conversions/entries/ (shared.ts and 11, 15, 17, 18; 95 modules under 18), git ls-files / git check-ignore on registry.ts (tracked, not ignored), AGENTS.md L271-307 (Prime Directive #14, the Tier S paragraph) and L650-665 (the "Touched packages/spec?" heading and its table, last row L665), scripts/pm/check-governed-merges.mjs L1200-1214 (GOVERNED_SURFACES; the claude-tree row Tier S with tierRepos: ['objectstack']), scripts/pm/check-skill-line-ratchet.mjs L186-200 (why 120 bytes), L2714 (MAX_LINE_BYTES = 120), L2717-2726 (EXEMPTION_CLASSES: fence, table, heading, frontmatter, anchored directive, multi-line quotation, blockquote, unbreakable), package.json L67 and L76 (check:skill-frame-sync and check:pm-skill-ratchet each run --self-test then live), card #29994 body and comments 6619509152, 6619532361, 6632322266, PR #30109 body (live) and comments 6632806172, 6633148272 (the full review being continued), 6633443351 (the seat's claim on this head), the dev gate record pr-29994/gates-22e2202b.txt (read as input: 22 lines, all :: exit 0)
Delta review of 6633148272 on 6837342

Reviewer: the same isolated contract reviewer at CONTRACT_REVIEW_TIER, read-only on GitHub (REST reads only; nothing written, pushed or commented). The full review on 6837342 (PASS, should-fixes S1, S2 and S3, three immaterial slips) stands as read; this record judges only the delta 68373420..22e2202b and the PR body as it now reads. The dev gate record was read as input, not as conclusions; every count below is this review's own reading of the head tree and of main. No environment variable value is printed anywhere in this record.

① Derived judgments

  1. Delta surface. One new commit (22e2202, author and committer Claude, subject "docs(skills): name both registry.ts lines in the conversion step", trailers Co-Authored-By: Claude <noreply@anthropic.com> + Claude-Session:, no model identifier; check-commit-card-trailers exit 0 over both commits). git diff --stat 68373420 22e2202b: one file, .claude/skills/spec-property-retirement/SKILL.md, +1 / −1; the REST compare agrees (one commit, one file, one hunk at L174-180). The porcelain word-diff has exactly two changed segments, both inside L177: 「手写提交、无生成器的」 → 「手写、无生成器的」 and 「只加 import。」 → 「加 import 与列表项。」. With L177 removed from both heads the files are byte-identical (26170 bytes each): nothing else changed, no other line moved or re-wrapped. The cumulative diff against the merge-base is still +7 / −7 in the one file, so every figure the full review measured on L176-181, L215-218 and the L263 cut stands. Exactly the S1 reword, and on surface.
  2. S1, the sentence. Head L177 now reads 「 (文件名即 id,按名 export);手写、无生成器的 conversions/registry.ts 加 import 与列表项。」. Against main: the merge test header L28-29 says "What a retirement still edits in registry.ts is two lines: the import of its conversion and its entry in the list", and the MAJOR_18_CONVERSIONS docblock L174-176 asks for the { conversion: <identifier>, order: <n> } entry — so 「加 import 与列表项」 names both lines and nothing more; 只 is gone (the only 只加 left in the file is L218's rationale clause, unchanged and true). 「手写、无生成器」: the file is tracked (git ls-files lists it, check-ignore exits 1), the header L10 says "hand-written, so nothing regenerates it", and no gen: script names it (the full review's ①.3). The word 「提交」 was dropped to pay the bytes; the contrast it carried (committed source, not a generated output) survives in 「无生成器」 and the body's "Read off main" paragraph still states it in full. The entries layout the line's first half names is unchanged on main (entries/shared.ts + 11, 15, 17, 18; file named for the id, exported by name). Resolved, and true.
  3. Width and count. L177 is an indented list-continuation prose line, so it is subject to the ratchet's MAX_LINE_BYTES rule (120 bytes, L2714; the exemption set is structural — fence, table row, heading, front matter, anchored directive, multi-line quotation, blockquote, unbreakable — and L177 is none of them). Measured: 119 bytes on the head (115 on the prior head), headroom 1; the dev's "119 bytes" reproduces. File 337 lines on the head, the prior head and the merge-base, against the ceiling 337 (headroom 0, as the body says). The widest line in the file is 324 bytes at L86 on both heads, a table row, under the table-row pin 326; the lines over 120 bytes (L86-88, L101, L114, L123-124) are all table rows. check-skill-line-ratchet exit 0 on the head, self-test 164. check-skills-token-ratchet exit 0 and not this file's gate (.claude/** is outside its population, as the full review's ②'s packaging reading implies: nothing here ships). dispatch-gates --commands derives 19 for the one path, the same 19 the dev record lists, and pnpm check:pm-skill-ratchet (self-test then live) is the one among them that prices .claude/** width. Correct.
  4. S2, the closing keyword. The body's line 6 reads Part of #29994; a grep for fix, fixes, fixed, close, closes, closed, resolve, resolves, resolved finds nothing, so no closing keyword remains and the merge leaves the card open. The 速读's 「需要你做的」 bullet also says where the other half goes: 「registry.ts 里 MAJOR_18_CONVERSIONS 头注的改写留在 .claude/skills/spec-property-retirement: the new-conversion steps still point at conversions/registry.ts, which the #29987 codemod splits into entries/ plus an index #29994 上,归 spec 车道。」 — the docblock half the filer added (6619532361) stays on the card for the spec lane, which is the first of the two branches S2 offered (the card itself needs no new comment under that branch). On main the docblock L181 still reads "DEFINE it directly above the definition", so the half is indeed still owed, and this PR does not touch registry.ts. PR-body guards (claim · duplicate · Part-of · single-writer path) is success on both rows. Resolved.
  5. S3, the tier. The only tier sentences in the body are the 速读's 「风险与回滚:只改内部技能文字(Tier S,不进 @objectstack/skills)」 and 「需要你做的:无。.claude/** 属 Tier S,由所属席位在合同评审记录与全绿检查之上经队列落地,不需要 GOVERNED_APPROVERS 批准。」; no "Tier H" and no request for an approval remains anywhere in the body. Against the register: GOVERNED_SURFACES L1202, the claude-tree row, prefix: '.claude/', tier: GOVERNED_TIER_S, tierRepos: ['objectstack'] (this repo); check-governed-merges --branch HEAD prints "Tier S(席内达档复核落地)… the OWNING seat lands this PR through the queue once its thread (or its card) carries a ## Contract review record for the CURRENT head … and every check green"; AGENTS.md Prime Directive feat: Comprehensive CRM example demonstrating all ObjectStack protocol features #14 L290-293: "Tier S (席内达档复核落地: all of .claude/**): those four lift once the PR thread or its card carries a ## Contract review record for the PR's current head with Served-tier: CONTRACT_REVIEW_TIER and a PASS verdict and every check is green — the owning seat then lands it through the queue". The body's sentence states each of those three conditions (record, green checks, queue) and the negative (no GOVERNED_APPROVERS approval) exactly as the directive and the checker do. Resolved.
  6. The slips. "These five lines were re-flowed" (L176-180: five, correct). "conversions-major18-merge.test.ts L25-31": the [#29987 codemod] paragraph begins at L25 ("// [Codemod split: generate per-entry files and an index for spec conversions/registry.ts with a deterministic script #29987 codemod] Since the codemod split…") and ends at L31 ("…is read for the definition itself."), correct. "AGENTS.md L650-665": the heading is L650 and the table's last row (react-blocks) is L665, correct. All three fixed.
  7. Every other number in the body against the head. "Head 22e2202b, two commits on f68b47ca: 68373420 … and 22e2202b" — REST lists exactly these two commits and the merge-base is f68b47c. "L177 names both registry.ts lines, 「加 import 与列表项」, 119 bytes, line count unchanged" (①.2-3). 速读: 「行数仍 337(上限 337)」, 「先看:L176-180 与 L216-217;… §4 L263」 (the full review's ①.7 and this record's ①.1: the remainder outside L177 is unchanged, so those lines are the same bytes as reviewed); 「手写、无生成器的 registry.ts 加 import 与列表项」 is a faithful short quotation of L177. "What changes": "registry.ts gains its import and its list entry" and "registry.ts is hand-written, with no generator" now match the new L177 word for word in sense; "L181 is byte-identical", "L215 and L218 onward are byte-identical" hold. "Local gates on 22e2202: 19 derived, 19 run with exit 0, 0 NOT MEASURED" — 19 derived here; the dev record has 22 lines at exit 0 (19 + the two rosters + the vitest file) and "the S1 re-run reused that tree" is sound for a one-line prose delta in .claude/**. "337 lines, ceiling 337" (①.3). "15 of 15 passed" for retired-key-migrate-sentence.test.ts: NOT MEASURED here; the lines it reads (the full review's ①.9) are in the unchanged remainder and Test Core (1/8) is success on the head. Human Minimum item 5 and the skip-changeset reasoning are unchanged and still true (check-empty-changeset exit 0; Check Changeset success). Correct.
  8. Model note and timestamps. The body's model sentence is unchanged: "The maintainer waived the model-tier mandate for governed rule text while that tier had no quota; this PR was developed below it." — a tier phrase, no family word, no claude-… identifier; the only other matches are the harness's <!-- claude-projects-attribution: … --> marker and the "Claude Code" product name in the footer, neither a model identifier. No ISO date shape (YYYY-MM-DD) appears anywhere in the body. The new commit's message and trailer pair name no model (①.1). Correct.
  9. Head check-runs. Three REST reads: 43 runs (23 success, 19 skipped, 1 in progress: Test Core (1/8)) → 44 (24 success, 19 skipped, 1 in progress: the Test Core umbrella, started 2026-11-04T14:42Z, once its one shard finished) → 44 (25 success, 19 skipped, 0 in progress). No failure, no non-success completion at any read; combined status pending (statuses API, 0 statuses). Success includes the two required floors (Lint & Repo Gates, TypeScript Type Check), every Lint and Type Check lane, Governed Surface Queue Guard, PR-body guards (both rows), Check PR Size, Auto Label, Dogfood Regression Gate, Plan Test Core shards, Test Core (1/8), Test Core, Test UI, filter. Skipped by path filter or opt-in: Build Core, Build Docs, Build UI Deps, Build & E2E UI, Lint UI, Type Check UI, UI Published Artifacts, Dogfood Verify CLI, Temporal Conformance, Schedule gate, 18.x upgrade harness, Packed-tarball smoke, Live E2E UI, the matrix-templated placeholder rows, and the second rows of Check Changeset, Check PR Size and Auto Label. Every check on the head is complete and green. Correct.
  10. Review state on this head. The PR thread carries the seat's Contract-review-claim: for 22e2202 (6633443351) and no record on this head; reviews: 0; draft, auto_merge null, mergeable_state blocked, no assignee or requested reviewer, labels documentation, size/s, skip-changeset; head repo = base repo; PR author objectstack-fleet[bot]. Tier S (.claude/**): this record, on the current head with every check green, is what Prime Directive feat: Comprehensive CRM example demonstrating all ObjectStack protocol features #14 asks for before the owning seat readies and queues it.

② Semver level

Unchanged: none — nothing publishes. The delta is one prose line in .claude/skills/**, which no released package carries; Clause-②: no, Metadata-protocol: none, no changeset, skip-changeset on the PR, check-empty-changeset exit 0 here and Check Changeset success on the head.

③ Boundary flags

  • S1 of 6633148272: resolved — L177 names the import and the list entry, true against the merge test header L28-29 and the docblock, 119 bytes under the 120-byte line rule, 337 lines, nothing else moved (①.1-3).
  • S2 of 6633148272: resolved — Part of #29994, no closing keyword, and the body records that the MAJOR_18_CONVERSIONS docblock half stays on .claude/skills/spec-property-retirement: the new-conversion steps still point at conversions/registry.ts, which the #29987 codemod splits into entries/ plus an index #29994 for the spec lane (①.4). The card stays open after the merge with that half owed; routing it is the dispatcher's, not this PR's.
  • S3 of 6633148272: resolved — both tier sentences say Tier S, landed by the owning seat on the record with green checks through the queue, no GOVERNED_APPROVERS approval (①.5).
  • The three slips: fixed (①.6).
  • Dev open_questions: none in the body. Reading note, not a finding: the first commit's message (6837342) still says "the one import line in the hand-written registry.ts"; the governed text and the PR body now say both lines, and a commit message is neither.
  • Independence: PR author objectstack-fleet[bot], commit author Claude on both commits; no off-relay author note is owed. No review seat is seated (6633443351); no assignee, requested reviewer or review on the PR.
  • Carried from the full review, outside this PR: the dispatch claim on the card (6632322266) still carries a model family word and a "Tier H … GOVERNED_APPROVERS" sentence; the PR body is clean on both. Pre-existing and not asked: head L34's 「八个生成产物」 against AGENTS.md L652's "six".

④ Design judgment

Per hits file, for the delta only:

  • .claude/skills/spec-property-retirement/SKILL.md L177: the cheapest true sentence — one clause, same line, no re-wrap, no line count or table-row move — and it now says what the merge test enforces: a retirement adds a module and edits registry.ts in exactly two places. Dropping 「提交」 to stay under 120 bytes loses nothing a dev acts on, since 「无生成器」 already rules out a generated file and the step-18 note two bullets below teaches where both lines go. Holds.

Must-fix: none. Should-fix: none remaining — S1, S2, S3 and the three slips of 6633148272 are all resolved on this head and in the live body.

Check-runs at record time: 44 check-runs on 22e2202, 25 success, 19 skipped, 0 failure, 0 in progress on the third read (the first two reads had one in progress each: Test Core (1/8), then the Test Core umbrella, both since success); combined status pending (0 statuses). mergeable_state blocked, draft, auto_merge null, labels documentation, size/s, skip-changeset; Tier S (.claude/**), landed by the owning seat through the queue on this record with every check green — not by a maintainer click and not by any approval.

Implemented-by: claude/project-thread-cmvfh6
Reviewed-by: session_01J8VVAnMUWA9rufXzFaEh4J

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#1 · huangyiirene · session_01J8VVAnMUWA9rufXzFaEh4J

维护者速读

席位核对 — ACCEPT · domain:skills#1 · head 22e2202bd603e99745a88bdcf542cfbc165f782c

  1. 形态: draft → main,Part of #29994(无关闭关键字);作者 objectstack-fleet[bot];唯一文件在认领面内;skip-changeset 经 check-empty-changeset 判定成立。
  2. 前提实测: 复核逐条对过 main 上的拆分布局(112 个 entry 模块、entries/shared.ts、手写的 registry.ts)与合并测试头注 L28-29。
  3. 门禁(钉 head 22e2202b): check-runs success 25 / skipped 19,无失败、无进行中;mergeable_state clean。派生门禁 19 条全过(--ran 记录在 PR 正文)。check-governed-merges --pr 30109:Tier S,一条 .claude/** 命中,14 行。
  4. 接受的偏差: 开发在低于规定档位的模型上进行,依维护者原话(无额度时豁免)。
  5. 范围外去向: MAJOR_18_CONVERSIONS 注释块改写留在 .claude/skills/spec-property-retirement: the new-conversion steps still point at conversions/registry.ts, which the #29987 codemod splits into entries/ plus an index #29994,合入后按部分落地处理,转 spec 车道。
  6. 落地条款: Tier S。本席同轮经中继 pr_ready + automerge_enable 入队,跟到 MERGED。

Generated by Claude Code

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

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant