Repository navigation
docs(pm): a suspended seat account is a dead session — takeover, re-rendered records, retriage, durable state - #19795
Conversation
…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>
Contract reviewServed-tier: ① Derived judgmentsRendered in-seat by the director seat (summon #27,
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 ② VerdictReviewed-by: VERDICT: PASS — lands through the queue after ACCEPT on the card; Generated by Claude Code |
…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>
…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>
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.mjsceiling (241 / 469 / 91 — counts unchanged, every line ≤ 120 bytes, no re-wrap). Baseorigin/mainat 2cf9db7.dispatch-runbook.md〈接手中断的 dev(worktree 接手协议)〉- 账号封禁即死会话:接手者重认领,消失的记录(认领/复核/裁决)按正文行在当前 head 重出。- 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`。`- 用户类写绑作者:封号则其评论与卡按作者 404,标签/状态/标题/正文存活。— the director's measurement (104 cards, zerocomment_deletedevents; assignee, PRs and bodies persist) supersedes it; the⇒ 耐久算按令牌类重算…line under it standsseat-post-protocol.md〈活性、写侧刷新与收班〉- 耐久状态只住正文或以 App 写入;正文守中继上限 60,000 字节,超大贴由半状态巡查浮出。- 超大贴由半状态巡查的 report-only 项浮出,⛔ 不等读取失败。(〈贴的四段与派生视图〉) into itNot 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/**) atCONTRACT_REVIEW_TIER: the director seat renders the## Contract reviewrecord 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 onmain; nothing to rebase.Verification
Gates derived by
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commandson the edited tree at d192977; each run alone, exit captured before any pipe (cmd > log 2>&1; ec=$?).node scripts/check-closing-keyword-parity.mjsnode scripts/check-closing-keyword-parity.mjs --self-testnode scripts/check-comment-mask-corpus.mjsnode scripts/pm/check-governed-queue-guard.mjs --self-testnode scripts/pm/check-harness-current.mjs --self-testpnpm --filter @objectstack/lint run check:doc-formula-expressionspnpm check:agent-test-spellingpnpm check:cross-package-test-inputspnpm check:doc-authoringpnpm check:driver-memory-censuspnpm check:gitlink-declaredpnpm check:nul-bytespnpm check:pm-governed-mergespnpm check:pm-half-statespnpm check:pm-skill-id-lintpnpm check:pm-skill-ratchetpnpm check:refd-timer-probepnpm check:required-contextspnpm check:skill-frame-syncpnpm check:watch-hint-literalAlso run on this head, named by the dispatch and outside the derived list:
pnpm check:skill-frame-freshnessexit 0 ·pnpm check:pm-governed-proseexit 0. The doc-formula gate measured only after@objectstack/lintand its closure were built throughscripts/pm/os-verify-lock.sh(VERDICT command-exit 0).--ranreconciliation: ✓ 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