Skip to content

docs(pm): a suspended seat account is a dead session — takeover, re-rendered records, retriage, durable state - #19795

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-19777-suspended-account-protocol
Sep 23, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-19777-suspended-account-protocol

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #19777

Clause-②: no

What changed

Three one-line edits, one per reference file, each paid in place under the file's check-skill-line-ratchet.mjs ceiling (241 / 469 / 91 — counts unchanged, every line ≤ 120 bytes, no re-wrap). Base origin/main at 2cf9db7.

file · section line added line paid
dispatch-runbook.md 〈接手中断的 dev(worktree 接手协议)〉 - 账号封禁即死会话:接手者重认领,消失的记录(认领/复核/裁决)按正文行在当前 head 重出。 folded - PM 会话已死 ⛔ 不推导其 dev 已死;跨账号活性只认 GitHub 产出读数。 and - 死后恢复查三态:远程(\git ls-remote`)、仅容器磁盘、真没了。into- PM 会话已死 ⛔ 不推导其 dev 已死;死后恢复查三态:远程 `ls-remote`、仅容器磁盘、真没了。` — the dropped clause 跨账号活性只认 GitHub 产出读数 is line 135 of the same file (〈Routine 传输〉 block)
platform-readings.md 〈API 配额〉 - 封号隐藏其全部评论、无 timeline 事件;assignee、PR、正文存活;分诊没了挂 \pm:retriage`。` replaced the older suspension reading in the same block, - 用户类写绑作者:封号则其评论与卡按作者 404,标签/状态/标题/正文存活。 — the director's measurement (104 cards, zero comment_deleted events; assignee, PRs and bodies persist) supersedes it; the ⇒ 耐久算按令牌类重算… line under it stands
seat-post-protocol.md 〈活性、写侧刷新与收班〉 - 耐久状态只住正文或以 App 写入;正文守中继上限 60,000 字节,超大贴由半状态巡查浮出。 folded - 超大贴由半状态巡查的 report-only 项浮出,⛔ 不等读取失败。 (〈贴的四段与派生视图〉) into it

Not touched: contract-review.md :13 / :19 (#19061 step ④), SKILL.md, os-dev.md, AGENTS.md; no gate, hook, ratchet or roster.

Landing

Tier S (.claude/**) at CONTRACT_REVIEW_TIER: the director seat renders the ## Contract review record on this head and lands it through the queue. skip-changeset — nothing published from any released package; the label is the seat's to apply (this dispatch forbids label writes by the dev). The two serial PRs the card named (#19737, #19771) are on main; nothing to rebase.

Verification

Gates derived by node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands on the edited tree at d192977; each run alone, exit captured before any pipe (cmd > log 2>&1; ec=$?).

command exit verdict line
node scripts/check-closing-keyword-parity.mjs 0 • packages/spec/CHANGELOG.md -- 6080503 bytes exceeds the sweep's 2097152-byte cutoff for UNREGISTERED files
node scripts/check-closing-keyword-parity.mjs --self-test 0 ✓ check-closing-keyword-parity --self-test: 40 assertions, 5 mutations of the shipped parsers each driven to r
node scripts/check-comment-mask-corpus.mjs 0 ✓ comment-mask corpus sweep [scripts/js-comment-mask.mjs]: 7015 files, 0 disagree, 0 unparseable, 89.9s (compa
node scripts/pm/check-governed-queue-guard.mjs --self-test 0 ✓ check-governed-queue-guard self-test: 279 cases pass (register-driven verdicts, the queue/PR event split, la
node scripts/pm/check-harness-current.mjs --self-test 0 check-harness-current --self-test: all 26 cases passed.
pnpm --filter @objectstack/lint run check:doc-formula-expressions 0 #11673).
pnpm check:agent-test-spelling 0 which drives this same sweep RED over a temp tree on disk.
pnpm check:cross-package-test-inputs 0 OK: 29 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob (6 of them
pnpm check:doc-authoring 0 ✓ doc authoring guard: sibling-package prose ids hold the baseline — 819 pinned site(s) across 231 file(s), 90
pnpm check:driver-memory-census 0 check-driver-memory-census: OK — every declaration is ledgered, every ledger entry is live, and every ruled fi
pnpm check:gitlink-declared 0 check-gitlink-declared: OK (9269 index entries -- 0 gitlink(s) at mode 160000; no .gitmodules in the index, so
pnpm check:nul-bytes 0 check-nul-bytes: OK (scanned 9262 text file(s) -- 9262 tracked, 0 untracked-not-ignored; skipped 7 binary; no
pnpm check:pm-governed-merges 0 live: the real generator declared 9 output(s) and certified this tree
pnpm check:pm-half-states 0 ✓ check-half-states self-test: 4897 cases pass. Batteries: H66 released queue card 182/172, H19 judged-set fou
pnpm check:pm-skill-id-lint 0 ✓ check-skill-id-lint: 30 file(s) clean (pattern /#[0-9]{3,}/g).
pnpm check:pm-skill-ratchet 0 ✓ check-skill-line-ratchet: declared cross-file moves: 1, total ceilings down 9 lines.
pnpm check:refd-timer-probe 0 1 code site(s), all inside the approved module, which is present and still reads it.
pnpm check:required-contexts 0 governed-surface-guard.yml:governed-surface-guard → 'Governed Surface Queue Guard'
pnpm check:skill-frame-sync 0 binding sentence present in it; 1 count mention(s) agree; 86 markdown files scanned for undeclared copies.
pnpm check:watch-hint-literal 0 ✓ check-watch-hint-literal: 71 declaration(s) across 4 rostered name(s) -- ROOT_DIR_WATCH_HINTS 47, ROOT_FILE_

Also run on this head, named by the dispatch and outside the derived list: pnpm check:skill-frame-freshness exit 0 · pnpm check:pm-governed-prose exit 0. The doc-formula gate measured only after @objectstack/lint and its closure were built through scripts/pm/os-verify-lock.sh (VERDICT command-exit 0). --ran reconciliation: ✓ dispatch-gates --ran: 20 derived famil(ies) accounted for — 20 run, 0 NOT-MEASURED (a DERIVED zero — all 20 recorded an exit code and none of them is 3).


Generated by Claude Code

…endered records, retriage, durable state

dispatch-runbook.md 〈接手中断的 dev〉: an account suspension is the
dead-session case; the taker re-claims and re-renders any vanished
record (claim / review / ruling) on the current head from the body's
lines. Paid in place by folding the "PM session dead does not imply
its dev is dead" line with the three-state recovery line beside it;
the cross-account liveness clause it dropped is stated by the
Routine-transport section above.

platform-readings.md 〈API 配额〉: a suspension hides the account's
comments fleet-wide with no timeline event; assignee, PRs and bodies
persist; pm:retriage is the remedy for vanished triage. It replaces
the older suspension reading in the same block (comments and cards
404 by author, labels / state / title / body survive), which the
director's measurement supersedes.

seat-post-protocol.md 〈活性、写侧刷新与收班〉: durable state lives in
the body or is written as the App; the body stays under the relay's
60,000-byte cap. The oversized-post line from the four-sections block
is folded into it.

Claude-Session: https://claude.ai/code/session_01XPUwWGnmky4PXPofNsf2w7
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/xs documentation Improvements or additions to documentation labels Sep 23, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: d1929771823128314ed27cb6e136e2866031cb70

① Derived judgments

Rendered in-seat by the director seat (summon #27, session_0129ZpnaBcYZZ51rCvQiXg6C; served model read from get_session at 2026-09-23T06:33Z: the fable tier, CONTRACT_REVIEW_TIER). The contract is #19777's 「Ruled shape for the skills lane」: three one-line edits, one per reference file, each paid in place under the file's ceiling; ⛔ no new gate / hook / ratchet / roster; Clause-②: no; skip-changeset; one PR Fixes the card. Read against the fetched head d192977 (one commit on origin/main 2cf9db7c, merge-base clean), ⛔ not against the report.

  • (a) Three files, +4 / −4, ceilings 241 / 469 / 91 held — right. git diff 2cf9db7c d192977 --numstat: dispatch-runbook.md 2/2, platform-readings.md 1/1, seat-post-protocol.md 1/1; line counts at the head 241 / 469 / 91; longest added line 120 bytes. Nothing under contract-review.md, SKILL.md, os-dev.md or AGENTS.md.
  • (b) The three added lines state the ruling, not more — right. dispatch-runbook.md 〈接手中断的 dev〉 「账号封禁即死会话:接手者重认领,消失的记录(认领/复核/裁决)按正文行在当前 head 重出」 = ruling items 1 + 2; platform-readings.md 〈API 配额〉 「封号隐藏其全部评论、无 timeline 事件;assignee、PR、正文存活;分诊没了挂 pm:retriage」 = the seat's 03:03Z measurement + item 3; seat-post-protocol.md 〈活性、写侧刷新与收班〉 「耐久状态只住正文或以 App 写入;正文守中继上限 60,000 字节,超大贴由半状态巡查浮出」 = item 4 with the relay cap. Item 5 (⛔ which accounts) is rightly absent.
  • (c) The three paid lines lose no rule — right. dispatch-runbook.md: 「PM 会话已死 ⛔ 不推导其 dev 已死」 and 「死后恢复查三态」 folded into one line; the dropped clause 「跨账号活性只认 GitHub 产出读数」 stands at :135 of the same file (re-read by the seat: 「跨账号接班活性只认 GitHub 产出读数」). platform-readings.md: the replaced line 「封号则其评论与卡按作者 404,标签/状态/标题/正文存活」 was the older reading of the same event, contradicted by the measurement (cards and bodies persist) — a correction, not a drop. seat-post-protocol.md: 「超大贴由半状态巡查的 report-only 项浮出」 folded into the new line's tail (the instrument named; the section moved from 〈贴的四段〉 to 〈活性〉, where the write-side rule lives).
  • (d) Gates and CI — right. Re-run by the seat on a detached checkout of the head: check-skill-line-ratchet 0 (「total ceilings down 9 lines」, no raise), check:pm-skill-id-lint 0 (30 files), check:pm-governed-prose 0, check:skill-frame-sync 0, check:skill-frame-freshness 0, check:nul-bytes 0 (9,262 files); check-governed-merges.mjs --pr 19795 exit 3 GOVERNED — Tier S on all 3 paths, 8 changed lines; check-expected-skips.mjs --pr 19795 NOT MEASURED in this seat's checkout (yaml module absent) — CI's own Lint & Repo Gates is green. Check runs on d192977: 17 success, 3 skipped (the sharded core tests, temporal conformance, packed-tarball smoke — the roster's opt-ins), 0 failed. The dev's --ran reconciliation reads 20 / 20 with a derived zero.

Readings outside the contract, recorded, ⛔ not this PR's: (i) the dev's report comment 5789851366 is the personal account's, not the bot's — its tools ran from the session's claude/issue-19739-… checkout, which predates PR #19780's fail-closed transport; the durable records of this card (claim, this review, ACCEPT, closure) are the App's, which is what the ruling asks. (ii) platform-readings.md :166–170 (the 停用 block) still carries the earlier 「销毁其名下 PR、卡与评论」 reading; the readings owner discriminates the two dated readings in a later line — noted on the card by the dev, not filed.

② Verdict

Reviewed-by: session_0129ZpnaBcYZZ51rCvQiXg6C

VERDICT: PASS — lands through the queue after ACCEPT on the card; skip-changeset applied by the seat with pr_ready + automerge_enable (SQUASH) in one relay request.


Generated by Claude Code

@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 23, 2026
@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 23, 2026 06:35
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 23, 2026
Merged via the queue into main with commit de4ed33 Sep 23, 2026
31 of 32 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-19777-suspended-account-protocol branch September 23, 2026 06:53
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 28, 2026
…an ADR-0112 envelope (objectstack-ai#19798)

Fixes objectstack-ai#19785

Clause-②: no (narrowing)

`defineStack(config, { strict: false })` now refuses a non-array
`objects`, and an `objects` array holding a non-object entry, with the
ADR-0112 envelope: `StackSchemaInvalidError`, `code:
'STACK_SCHEMA_INVALID'`, `status: 422`, and the zod issue at `path:
['objects']` (non-array) or one per entry at `path: ['objects', index]`
with `expected: 'object'` (non-object entry). Before, a number or a
string raised a bare `TypeError: config.objects.map is not a function`
from `mergeActionsIntoObjects`, and a `null` entry raised one reading
`actions` off it. No new error code; the error class stays module-local.

## Refuse or skip: the card's first question

**Refuse.** Here is what `DefineStackOptions.strict` declares for
`false`: "validation is skipped for maximum flexibility (e.g., when
views reference objects provided by other plugins). Use this ONLY when
you need to bypass validation for advanced use cases." It promises that
validation is skipped: cross-references and schema detail. It does not
promise to accept, or quietly skip, a shape the action merge cannot
read. No ADR is cited there. So this is not a contract-direction
question, and the objectstack-ai#18239 ruling (`5690859601`) applies one door earlier:
「an ADR-0112 envelope error (closed `error.code`, status), ⛔ not a bare
`TypeError` and ⛔ not a skip」.

## What changed

`packages/spec/src/stack.zod.ts`, inside `mergeActionsIntoObjects` only:

1. **Array guard.** Any `objects` that is present and not an array is
refused. `undefined` is the only non-array that is not malformed,
because the key is absent. This is the line objectstack-ai#19783 drew for
`mergeObjects`. It includes the falsy values `null`, `''`, `0` and
`false`, which used to be handed on untouched and then refused one call
later by `composeStacks` with this same code. Refusing `5` while passing
`0` would repeat the silent-skip row objectstack-ai#19783 removed. The map form (`{
name: { ... } }`) is normalized to an array before the merge and is
still accepted.
2. **Non-object entries (rework round 1).** An entry that is not an
object (`null`, `7`, a string, an array) is refused with the same
envelope: one zod issue per such entry at `['objects', index]`,
`expected: 'object'` (read from `z.array(z.looseObject({}))`, re-rooted
at `objects`, which is the strict parse's own answer for the shape). The
message names each position. Round 0 handed such an entry on untouched.
The contract review showed that this widens the accept set under a
`(narrowing)` declaration: the card's own `[null, obj]` repro threw
before and would have succeeded. It would also let the next consumer,
plugin-object registration in `packages/objectql/src/engine.ts` (one
loop in one warn-level catch), silently drop every object after the bad
entry. Now every row narrows.

Measured on the base `3f9e2eaa1c`, against the built `dist`:

| `objects` under `strict: false` | before | after |
| :-- | :-- | :-- |
| `5`, `'abc'` | `TypeError: config.objects.map is not a function`,
`code`/`status` undefined | `STACK_SCHEMA_INVALID`, 422 |
| `null`, `''`, `0`, `false` | returned untouched |
`STACK_SCHEMA_INVALID`, 422 |
| `[null, obj]` | `TypeError: Cannot read properties of null (reading
'actions')` | `STACK_SCHEMA_INVALID`, 422, issue at `['objects', 0]` |
| `[obj, 7]` | returned with the entry in place |
`STACK_SCHEMA_INVALID`, 422, issue at `['objects', 1]` |
| map form, array, absent | accepted | accepted (unchanged) |

## Tests

- New file:
`packages/spec/src/define-stack-non-strict-objects-shape-refusal.test.ts`.
It has 6 non-array refusal rows asserting `code` + `status` + the issue
(`path`, `invalid_type`, `expected: 'array'`). Two entry refusal rows
cover `[null, obj, 7]`, with issues exactly at `['objects', 0]` and
`['objects', 2]`, all `invalid_type` / `expected: 'object'`, plus
`[null, obj]`. Controls: array, map form and absent `objects` are all
accepted with the bound action merged, and the strict door raises the
same code for the same shape.
- `packages/spec/src/compose-stacks-objects-shape-refusal.test.ts`
(objectstack-ai#19783's file): its four falsy rows reached composition through
`strict: false`. That door now refuses those shapes itself, so the rows
now reach composition as hand-built stacks. They keep the same values
and the same assertions. This is a fixture re-route, and it is outside
the claim's declared file surface. I am declaring it here; the contract
review accepted it.
- **Ablation, round 0** (commit `d3959b7b79`): `stack.zod.ts` restored
from the base, with the new file run against it: `Tests 7 failed | 4
passed (11)`. The refusal rows failed with `expected undefined to be
'STACK_SCHEMA_INVALID'` and the entry row with a bare `TypeError`.
Restored, `git diff HEAD` empty.
- **Ablation, round 1** (commit `c5a865c769`):
`scripts/ablation-replace.mjs` replaced the entry guard's condition. The
anchor went x1 to x0 and the blob `88f36c9846` became `ac5fbf58be`.
Result: `Tests 2 failed | 10 passed (12)`. Both entry rows failed with
`expected undefined to be 'STACK_SCHEMA_INVALID'` (a bare `TypeError`
reached the assertion), and every other row stayed green. Restore: blob
back to the HEAD blob `88f36c9846`, `git diff HEAD` empty.
- At `c5a865c769` (merge of `origin/main` `fae870352e`):
- `pnpm --filter @objectstack/spec build` + `typecheck`: exit 0. The
test layer compiles, and `test-typecheck-debt.json` held.
  - The two targeted files: 37/37.
- Full spec suite: `Test Files 552 passed (552)`, `Tests 15690 passed |
1 todo`.
- eslint `--no-inline-config` over the 3 touched TS files: 3 files, 0
errors, 0 warnings. Type-aware linting is not enabled in
`eslint.config.mjs`, so this diff cannot move the verdict on any
untouched file.
- `dispatch-gates --commands` derived 82 commands (the same list as
round 0). 80 exit 0. 2 are NOT MEASURED with exit 3 (they need the
whole-workspace build): `check:dual-build-cjs-loads` and
`check:type-check-debt`. `--ran` reconcile: 82 accounted, 0 UNRUN.
- **Round 1b, at `ce893bbf31`** (merge of `origin/main` `0b83e01627`,
which includes objectstack-ai#19794, then the helper routing):
- The targeted files plus objectstack-ai#19794's
`compose-stacks-concat-shape-refusal.test.ts`: 162/162.
  - Spec build + typecheck: exit 0.
- Full spec suite: `Test Files 553 passed (553)`, `Tests 15815 passed |
1 todo`.
- Ablation of the array guard: `Tests 6 failed | 6 passed (12)` (all 6
non-array rows). Ablation of the entry guard: `Tests 2 failed | 10
passed (12)`. Both restores ended at blob == HEAD `ac2a452f57`, `git
diff HEAD` empty.
  - eslint: 3 files, 0 errors.
  - Gates: 82 derived, 80 exit 0, the same 2 NOT MEASURED, 0 UNRUN.
- `origin/main` has since moved one commit, `de4ed33fd5` (docs(pm),
objectstack-ai#19795). It is not merged.

## Acceptance notes

- `class: a`, not fixed here, filed by the seat as objectstack-ai#19799. The same
function still dereferences the other shapes it reads under `strict:
false`: a top-level `actions` of `5` or `'abc'`, or `actions: [null]`,
raises a TypeError from `sortActionsByOrder`.
- Review item 4 is done at `ce893bbf31`. PR objectstack-ai#19794 landed
(`0b83e01627`), so the non-array block now calls its
`describeNonArrayCollection('objects', …)` helper, with no hand-copied
kind/issues code left. Messages are byte-stable for every input that can
reach this door. The helper's one extra branch, `'an object'` for a
plain object, is unreachable here because `normalizeStackInput` turns
the map form into an array first.
- `strict: false` with a `Set` as `objects` still returns without
throwing; the normalizer reads it as a keyless map. This was recorded on
objectstack-ai#19783 and is not part of this card.

---

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 28, 2026
…eview / landing detail relocated into per-phase references, core-rules.md retired (objectstack-ai#19805)

Fixes objectstack-ai#19716

Clause-②: no

## 维护者速读(草稿)

**改了什么**:PM 协议主文 `.claude/skills/pm-dispatch/SKILL.md` 从 815 行压到 319
行,只留原则——红线、优先级、入口与角色(含参数表)、新增的「阶段细则索引」表、全体座位的不变量、状态模型、域车道、受管面的「路径分叉」块、升级与决策(含四轴框架单源)、报告契约、机械守卫索引、模板与表。派发
/ 分诊 / 座位生命周期 / 读数纪律 / 多仓协调 / 入队与落地的细则逐字搬进六份按阶段读的
references(五份新建:`seat-lifecycle.md`、`reading-discipline.md`、`cross-repo-coordination.md`、`triage-duties.md`、`execution-duties.md`;既有的
`landing-operations.md` 吸收「入队与落地」),每份第一行写明哪个座位在哪个 fire
读它。`references/core-rules.md`(SKILL.md 的人读摘要副本,151
行)删除,所有指向它和被搬章节的指针重定向。行数棘轮把 SKILL.md 上限从 819 降到 319,新文件按落地行数入表,棘轮总量下降。

**为什么改**:决策批 objectstack-ai#213 第 4 项,维护者原话「1 2 4 同意」,项目原文「协议主文 819 → 约 300 行(红线 +
状态模型 + 入口 + 索引),派发 / 复核 / 入队细则各成一份按阶段读的 reference;同时删 core-rules.md
双份」。目的是每个座位每次 fire 只读本阶段的文件,不再整本重读 815 行外加 151 行的副本。

**风险与代价(含回滚)**:纯搬迁,无语义变化——被搬的 465 条规则行里 459 条逐字相同,5 条只改了指针拼写(指向新家),1
条自指指针删除;「⛔」行数搬前搬后相等(对合并基点 558 对 558,对派发基点 559 对 559;都不计被删副本自带的 59
行)。代价落在指针:部分 references 的指针行受 120
字节行宽与零余量约束,只写文件名不写章节名(章节标题在新文件里原样保留);两个门禁脚本各改了一处指向路径(自测通过)。SKILL.md 落在
319 行——高于「约 300」但低于 340 上限——多出的部分是 `check:pm-governed-prose` 锚定在
SKILL.md 里的受管面路径分叉块(28 行)加索引表(11 行)。回滚 = revert 本 PR(一个 squash
commit);棘轮上限已降,回滚时抬回需维护者裁决引用。

**席位意见**:(席位填写)

**你要做的**:本 PR 属 Tier S(路径全在 `.claude/**` 与 `scripts/pm/**`),由 skills
席在席内契约复审 PASS 后走队列落地,不需要你点击。若对新文件命名、或 SKILL.md 保留 319 行而非约 300 行有异议,在本
PR 评论一句即可。

## What this PR does

Relocation, not deletion — decision batch objectstack-ai#213 item 4 (maintainer,
verbatim: 「1 2 4 同意」). Every rule line of `SKILL.md` on the base
(`fae870352`, 815 lines) ends up in exactly one place: `SKILL.md` if it
is principle, otherwise one per-phase reference.
`references/core-rules.md` (the declared subset / human digest, 151
lines) is deleted. No rule added, no rule dropped, no gate / hook /
ratchet / roster added; `os-dev.md`, `AGENTS.md` and `skills/**`
untouched.

Six destination files, one commit each (the branch is bisectable by
destination):

| destination | opens with (which seat reads it on which fire) | lines |
|:--|:--|--:|
| `references/execution-duties.md` (new) | 执行席每次 fire
读本文:候选、认领、派发、收集、复核;落地窗口另读 `landing-operations.md`。 | 183 |
| `references/seat-lifecycle.md` (new) | 每个座位每次 fire
读本文:开轮互斥读数之前读〈开轮互斥〉,收班之前读其余各节。 | 96 |
| `references/triage-duties.md` (new) | 分诊席每次 fire 开局读本文;本文是分诊座位的全部职责。 |
120 |
| `references/reading-discipline.md` (new) | 任一座位在取读数、引读数或核验 main
的那一刻读本文,所有 fire、所有阶段。 | 37 |
| `references/cross-repo-coordination.md` (new) | 分诊席每次 fire
路由与查重前读本文;执行席跨仓认领、派发或转移时读本文。 | 51 |
| `references/landing-operations.md` (existing, absorbs 入队与落地) |
执行席在落地窗口(ACCEPT 之后至 MERGED)读本文;入队与落地条款与 A–D 操作细则都在此。 | 69 → 101 |

Why two files beyond the card's suggested shape: 平台读数纪律 and 多仓协调 are
read by BOTH seats (readings are taken on every fire; cross-repo routing
is triage's, cross-repo claiming/dispatch is execution's). Folding
either into one seat's phase file would make the other seat skip it — a
rule dropped in effect — and keeping both in `SKILL.md` lands it at 367
lines, over the 340 ceiling. Each gets a single-section file whose title
is the section heading, so every 〈heading〉 pointer still resolves.

## Section → destination (base line spans of `SKILL.md` at `fae870352`)

| base span | section | destination |
|:--|:--|:--|
| 1–76 | frontmatter, intro, 红线, 优先级 (42–44 rewritten, see pointers),
入口与角色 | stays (SKILL.md) |
| 77–92 | 入口与角色 · the 开轮互斥 bullets | `seat-lifecycle.md` 〈开轮互斥〉 |
| 94–158 | 全体座位的不变量, 状态模型 (table + bullets) | stays |
| 159–194 | 平台读数纪律 | `reading-discipline.md` |
| 195–244 | 多仓协调 | `cross-repo-coordination.md` |
| 245–271 | 域车道 (table + bullets) | stays |
| 272–296 | 座位贴协议 | `seat-lifecycle.md` 〈座位贴协议〉 |
| 297–312 | Epic 子树车道 | `seat-lifecycle.md` 〈Epic 子树车道〉 |
| 313–431 | 分诊座位职责 | `triage-duties.md` |
| 432–435 | 执行座位职责 heading + intro bullet | `execution-duties.md` |
| 436–481 / 482–511 / 512–573 / 574–597 | 候选与批次 / 认领 / 派发 / 收集 |
`execution-duties.md` 〈候选与批次〉〈认领〉〈派发〉〈收集〉 |
| 598–612 | 复核 (the verdict rules) | `execution-duties.md` 〈复核〉 |
| 613–641 | 复核 · **ACCEPT 之后的路径分叉** … 本段只适用本循环派发的 dev PR | stays, under
the new heading `## 路径分叉` — `check:pm-governed-prose` anchors its
SKILL.md region on exactly these two lines, and
`check-expected-skips.mjs`'s self-test pins the 入队资格 line at 637 |
| 642–674 | 入队与落地 | `landing-operations.md` 〈入队与落地〉 (644, a self-pointer
to that very file, dropped) |
| 675–690 / 691–698 / 699–707 | 轮次报告与节奏 / 停止条件与待命 / 断粮与跨墙恢复 |
`seat-lifecycle.md` (收班) |
| 708–815 | 升级与决策 (frame block 737–757 byte-identical, md5
`abff5f852c8fd09b79ec0623439aa4f0`), 报告契约, 机械守卫索引, 模板与表 | stays |

New in `SKILL.md`: `## 阶段细则索引` (a 6-row table: file · which seat reads
it on which fire · sections it carries), one pointer line in 入口与角色 to
〈开轮互斥〉, and the `## 路径分叉` heading. SKILL.md: 815 → 319 lines (≈ 300
target, ≤ 340 ceiling; the excess over 300 is the retained 28-line
governed-prose block plus the 11-line index).

## Proof: relocation, not deletion

Measured by an independent verifier over the working tree against `git
show de4ed33:…` (the merge base; script in the report):

- Moved content lines (non-blank, non-heading, in the spans above):
**465** — **459 verbatim**, **5 rewritten** (pointer retargets inside
moved text, listed below), **1 dropped** (644, the self-pointer), **0
missing**.
- Retained `SKILL.md` lines: **302 / 302** present in their original
order (the three rewritten and the one dropped 优先级 line excepted, listed
below).
- 「⛔」 lines, against the PR's merge base `de4ed33fd`: before **617**
(`SKILL.md` 194 + `references/**` 423, of which `core-rules.md` 59) —
after **558** (`SKILL.md` 68 + `references/**` 490). Excluding the
deleted duplicate's own 59 lines: **558 == 558**. (Against the dispatch
base `fae870352` the same proof reads 618 → 559, the one-line difference
being objectstack-ai#19795's edit to `seat-post-protocol.md`, merged in.)
- Decision frame block: byte-identical, only its line position changed
(`check:skill-frame-sync` and `-freshness` green).

## Pointer retargets (every one in place, same line count, ≤ 120 bytes)

Inside moved text (the 5 rewrites): 89
「三章程(本文、core-rules、本席章程)」→「SKILL.md、阶段 references、本席章程」(the touch-check
set follows where the rules now live; core-rules gone); 183 「(见认领节)」→「(见
execution-duties.md 认领)」; 491 「照抄 模板与表 节」→「照抄 SKILL.md 模板」; 612 「见 升级与决策
节」→「见 SKILL.md 〈升级与决策〉」; 694 「按收集节②」→「按 `execution-duties.md` 收集节②」.

In `SKILL.md`: 42 drops the 「核心条款」 tier from the priority chain (the
file it named is deleted; no new tier introduced); 43 now points at
`references/**` and 〈阶段细则索引〉 instead of `core-rules.md`; 44
「一条规则在本文与核心条款一处改动,另一处同 PR 同改」 dropped — its whole object is the deleted
duplicate; 56 「见 Epic 子树车道 节」→ `references/seat-lifecycle.md` 〈Epic
子树车道〉.

In references (line 3 unless noted): `review-checklist.md` →
`execution-duties.md` 〈复核〉; `compile-surfaces.md` →
`execution-duties.md`; `true-green.md`, `lanes/devx.md`:19,
`lanes/skills.md`:10 → SKILL.md 〈路径分叉〉;
`lanes/{cli,devx,engine,services,skills,spec}.md` → `seat-lifecycle.md`;
`seat-post-protocol.md` → `seat-lifecycle.md` 〈座位贴协议〉;
`lanes/triage.md`:2 → `triage-duties.md`; `instrument-discipline.md`,
`platform-readings.md` → `reading-discipline.md`; `filing-gate.md` →
`triage-duties.md` 与 `reading-discipline.md`; `lanes/hotcrm.md`,
`lanes/ui.md`:3/13/19/21 → `cross-repo-coordination.md`;
`contract-review.md`:3 → `landing-operations.md` 〈入队与落地〉 (lines 13 / 19
byte-identical, objectstack-ai#19061 step ④'s); `external-contributions.md` →
`landing-operations.md`. Where the retargeted line sits at the 120-byte
cap in a headroom-0 file, it cites the file without the 〈heading〉; the
heading itself is unchanged inside the destination.

In scripts: `scripts/pm/dispatch-gates.mjs` 25403 / 26508 — the two
self-test specimens that named `references/core-rules.md` now name
`references/state-machine.md` (an internal references file that exists;
the assertion, "carries no mandate", is unchanged);
`scripts/pm/check-half-states.mjs` 15523 (H65 prescription) plus its two
docblock lines 15104 / 15171 — `SKILL.md 〈分诊座位职责〉` →
`references/triage-duties.md 〈分诊座位职责〉`.

## Ceiling map (`scripts/pm/check-skill-line-ratchet.mjs`), before →
after

| entry | before | after |
|:--|--:|--:|
| `SKILL.md` | 819 | 319 (降恒合法) |
| `references/landing-operations.md` | 69 | 101 — declared CROSS-FILE
MOVE, source `SKILL.md` 819 → 319: +32 against −500, authorised by
objectstack-ai#14685 item 5 (comment 5520452691) |
| `references/core-rules.md` | 151 | row and table-row pin removed with
the file |
| `references/seat-lifecycle.md` · `reading-discipline.md` ·
`cross-repo-coordination.md` · `triage-duties.md` ·
`execution-duties.md` | — | 96 · 37 · 51 · 120 · 183 (new rows at landed
count; table-row pins 0, they carry no table) |
| map total | | down 132 lines (−500 −151 +32 +487); the gate reports
the declared moves' total down 477 |

## Gates (exits captured before any pipe)

All families below ran on `8df1e7e52`; after merging `origin/main`
(`de4ed33fd`, three commits: objectstack-ai#19792, objectstack-ai#19795 and a spec changeset, none
touching `scripts/pm/**`) the ratchet family, `check:pm-skill-id-lint`,
`check:skill-frame-sync`, `check:skill-frame-freshness`,
`check:pm-governed-prose` and `check:nul-bytes` were re-run on the
merged head `f20de6220`, all exit 0, and `--ran` re-reconciled there
(derived list unchanged).

The five the card names: `check:pm-skill-ratchet` exit 0 (`SKILL.md is
319 lines (ceiling 319; headroom 0)`; `declared cross-file moves: 2,
total ceilings down 477 lines`; self-test 157 cases) ·
`check:pm-skill-id-lint` exit 0 (34 files clean) ·
`check:skill-frame-sync` exit 0 (one declared copy, coherent) ·
`check:skill-frame-freshness` exit 0 (current with origin/main) ·
`check:pm-governed-prose` exit 0 (2 instruction surfaces name all 6
governed surfaces).

Derived by `node scripts/pm/dispatch-gates.mjs --repo
objectstack-ai/objectstack --commands` (50 families from the merge-base
change set, 29 paths) and reconciled with `--ran`: `Run reconciliation —
50 derived, 50 run, 0 NOT-MEASURED, 0 UNRUN`, every family recorded with
its exit code (`dispatch-gates --ran` exit 0; the one extra,
`check:skill-frame-freshness`, is outside the derivation). Among them
`check:pm-half-states` exit 0 (4897 cases; the H65 battery reads the
retargeted prescription), `check:pm-governed-merges` exit 0,
`check:pm-expected-skips` exit 0 (its SKILL.md enqueue-bar pin still
holds — the line stayed), `check-governed-queue-guard.mjs --self-test`
exit 0, `check-harness-current.mjs --self-test` exit 0,
`check:doc-authoring` exit 0, `check:nul-bytes` exit 0,
`check:commit-card-trailers` exit 0. `check:pm-dispatch-gates` exit 0 —
`✓ dispatch-gates self-test: 1905 cases pass` (`the battery took 713.0s
on this box`; run detached as its header prescribes, exit captured to a
file). `check:doc-formula-expressions` (derived only because
`dispatch-gates.mjs` is edited) exit 0 after its prerequisite `turbo run
build --filter=@objectstack/formula --filter=@objectstack/lint` under
the verify lock (`VERDICT command-exit 0`): 22 record-scoped examples
across 456 files clean.

Changed lines: 623 additions + 710 deletions = 1,333 (under the 5,000
human-merge threshold). `check-governed-merges.mjs --branch
claude/issue-19716-skill-md-principle-only` (offline, merge-base): exit
3 GOVERNED — Tier S(席内达档复核落地), `.claude/** ×26`, `scripts/pm/*` not on
the register, size 1,333 ≤ 5,000; the `--pr` reading (head repo) is in
the report.

## Readings on the base that differ from the card

- `references/lanes/director.md` and `scripts/pm/git-history.mjs` carry
no `core-rules` reference on `fae870352` (`git grep -c core-rules` → 0
for both). The 「三章程 touch list」 the card attributes to `git-history.mjs`
is SKILL.md line 89 (the tool is `git-history.mjs touch`); it is
rewritten as listed above.
- The 「本车道」 sentence (base line 266) stays in 域车道 and was left
untouched; PR objectstack-ai#19792 landed on `origin/main` while this branch was in
flight and the merge brought the ruled wording in cleanly (now SKILL.md
line 175). Nothing of another card's is pre-applied here.

## Acceptance notes

- Two gate docblocks — `scripts/pm/check-clause2-carriers.mjs` 3940–3941
and `scripts/pm/check-governed-queue-guard.mjs` 272–273 — attribute 「记录
= 同形评论落 PR 或卡」 to `SKILL.md 〈入队与落地〉`; the phrase is nowhere in
`SKILL.md` on the base (pre-existing staleness). Left untouched:
retargeting the pointer would keep a false claim. 承接者: skills seat (a
one-line docblock correction when either file is next edited).
- `scripts/pm/check-clause2-carriers.mjs` prose that says "SKILL.md's
handover sentence" (its `CLAIM_HANDOVER_SENTENCE_LINES` twin now lives
in `execution-duties.md` 〈认领〉, byte-identical) — file-name-only prose,
no heading pointer; left. 承接者: same.
- `check:doc-formula-expressions` is derived for this change set only
through `dispatch-gates.mjs` being edited; it needs
`@objectstack/formula` and `@objectstack/lint` built first (exit 3 =
prerequisite, not a finding, until built).

## Changeset

`skip-changeset`: every changed path is under `.claude/**` or
`scripts/pm/**`, which are in no package's `files[]` — nothing published
moves.

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

---------

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

Labels

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

Projects

None yet

1 participant