Skip to content

docs(agents): the contract-review carrier is the seat's to hang; --pair PR-NUMBER is the only spelling - #18250

Merged
os-zhuang merged 4 commits into
mainfrom
claude/issue-18181-carrier-is-the-seats-to-hang
Sep 15, 2026
Merged

os-zhuang merged 4 commits into
mainfrom
claude/issue-18181-carrier-is-the-seats-to-hang

Conversation

@claude

@claude claude Bot commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Fixes #18181

What changed

Three lines across two governed rules-layer files; line counts unchanged (os-dev.md 403/403, SKILL.md 812/812), every edited line at or under the 120-byte cap, no re-wrap anywhere, and the decision-frame block SKILL.md :734–:755 untouched (md5 3327d02c56f8a0eca88569dad2270f32 before and after).

.claude/agents/os-dev.md :287 — the line the card names, re-derived on b3b43b6 (the card body's :288 had drifted; the filer's correction comment 5664488072 gives :287):

-- 认领写 `Clause-②: yes` ⇒ 开 PR 同笔挂 `needs:contract-review`,报告附 `--pair N` 退出码。
+- `needs:contract-review` 归席位,⛔ 不挂不摘不等;报 PR 上有无与 `--pair PR-NUMBER` 退出码作读数。

The dev no longer hangs the carrier. It never hangs, removes or waits for needs:contract-review; it reports whether the label is present on the PR and the --pair PR-NUMBER exit code — as a reading, not as a gate it must clear.

.claude/agents/os-dev.md :301 — the neighbouring skip-changeset label-write rule (mechanism assumption A2, same file surface, same class — an additive label write the container may refuse):

-- 被拒 ⇒ 停下报 `blocked` 点名端点与状态码,⛔ 不换 MCP 写道;写后必做对比式读回。
+- 被拒 ⇒ 报端点与状态码、席位代挂,⛔ 不报 `blocked`、不走 MCP;写后必做对比式读回。

One rule now covers both carriers: a refused additive label write is reported with endpoint and status and the seat hangs the label; the dev never reports blocked for it and never switches to an MCP write.

.claude/skills/pm-dispatch/SKILL.md :646 — the one --pair N token (REGION claim: this line only):

-- `Clause-②: yes` 认领同笔在卡上挂标;PR 开出即读 `check-clause2-carriers --pair N` 为 0 再请审。
+- `Clause-②: yes` 认领同笔卡上挂标;PR 开出读 `check-clause2-carriers --pair PR-NUMBER` 为 0 再请审。

--pair PR-NUMBER is now the only spelling in the three governed files (contract-review.md :42 already spelled it so).

Why

Direction A of the card's grading (comment 5673619664), executed as ruled: the needs:contract-review carrier is the PM seat's to hang — on the card at the claim and on the PR when the PR appears — which is what every seat already does; the dev never blocks on a label write; --pair PR-NUMBER becomes the only spelling. Option B (granting the dev container the label write) is a permissions widening and was ruled not this card.

The two defects the card and its correction measured: a dev that obeyed :287 literally was refused the additive label write by its container's write classifier on two independent objectui runs, while an objectstack dev could write it — per container, unknowable in advance; and --pair N bound N to the nearest referent, the card, while the script takes a PR number and answers a card number with exit 2 (UNJUDGED), which a naive capture reads as a red gate.

Mechanism readings

  • A1 — :287 is the line on b3b43b6 (108 B; the card's :288 is the skills/** line). Replaced as above. Under the byte cap the Clause-②: yes trigger did not fit, so the rule is now unconditional: on every PR the dev hangs nothing, removes nothing, waits for nothing, and reports carrier presence plus the --pair PR-NUMBER exit code. That superset is deliberate — --pair also carries the widening-tell row, which only fires on a Clause-②: no claim, so a reading on every PR is worth more than one on yes claims alone.
  • A2 — measured on the text: :301's 被拒 ⇒ 停下报 blocked is the same container-refusal class one line over, and nothing about skip-changeset requires the dev to block on it: CI's changeset gate reads the label, the dev's report says whether it landed, and the seat can hang it on that report. So one rule covers both carriers and :301 changed as above; :299 (the label is your step, hang it as soon as the PR opens) and :300 (additive endpoint) stay — the dev still attempts the write. The live half of the measurement is this PR's own skip-changeset write; its outcome (landed, or the endpoint and status of the refusal) is in the os-dev-report comment on the card, not here, because the PR body is written before the label write and is not re-sent.
  • A3git grep -n -- '--pair N\b' -- '.claude/**' 'scripts/pm/**': before 6 hits (os-dev.md :287, SKILL.md :646, and 4 in scripts/pm/**); after 4 hits, all outside the claimed surface: check-clause2-carriers.mjs :24 and :490 (docblocks), :4947 (a self-test title), and check-widening-tells.mjs :492 (docblock). In the three governed files the count is 0. Lit control --pair PR-NUMBER: 1 before (contract-review.md :42), 3 after (:42, os-dev.md :287, SKILL.md :646). The script docblocks bind N to a PR number in their own text (:11 spells --pair 13910 # ONE PR), so the ambiguity is not live there; noted below, not edited.
  • A4 — measured from the sibling checkout on this container: cd /home/user/objectui && PM_SWEEP_REPO=objectstack-ai/objectui node ../objectstack/scripts/pm/check-clause2-carriers.mjs --pair 9552 exits 0 with the provenance line every row below is read from objectstack-ai/objectui (source: PM_SWEEP_REPO) and the verdict PR #9552 / card #6910 — the clause-② declaration is readable in the fixed spelling and both carriers agree (token present, 3 reads served; the proxy re-exec happened by itself). A control on objectui#9495 (no longer open) exits 2 — UNJUDGED, with the board still retargeted. So the invocation works from a sibling checkout. The byte cap leaves no room on :287 for the invocation (the line stands at 119 B), so the line carries only the flag spelling; the script header (:20–:24) already spells the sibling invocation and every run prints the board it read, which is the remedy text the ruling asks to keep in the script.

Gates

Derived with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths; change set from the merge base): 18 families on a2cbbef0, re-derived on the merged head 0c934967 (merge base 68fea8bc) — identical list. All 18 run in the foreground on 0c934967, exit codes captured by redirect before any pipe, all 0:

check-closing-keyword-parity (+ --self-test) · check-comment-mask-corpus · check-governed-queue-guard --self-test · check:doc-formula-expressions (lint pkg) · check:agent-model-declared · check:agent-test-spelling · check:commit-card-trailers · check:doc-authoring · check:driver-memory-census · check:nul-bytes · check:pm-governed-merges · check:pm-governed-prose · check:pm-skill-id-lint · check:pm-skill-ratchet · check:refd-timer-probe · check:skill-frame-sync · check:watch-hint-literal

Reconciliation on 0c934967: ✓ dispatch-gates --ran: 18 derived famil(ies) accounted for — 18 run, 0 NOT-MEASURED (a DERIVED zero — all 18 recorded an exit code and none of them is 3).

Verdict lines: ✓ check-skill-line-ratchet: .claude/agents/os-dev.md is 403 lines (ceiling 403; headroom 0) · ✓ check-skill-line-ratchet: .claude/skills/pm-dispatch/SKILL.md is 812 lines (ceiling 812; headroom 0) · ✓ check-agent-model-declared: 1 agent definition(s) under .claude/agents/ all declare a model · ✓ check-skill-id-lint: 27 file(s) clean · ✓ check-skill-frame-sync: the one declared copy of the decision frame is internally coherent.

check:doc-formula-expressions first answered exit 3 (PREREQUISITE NOT MET — @objectstack/formula and @objectstack/lint unbuilt in a fresh worktree); prerequisite built under the verify lock (turbo run build --filter=@objectstack/formula --filter=@objectstack/lint, VERDICT command-exit 0), rebuilt again after the merge, then the gate exits 0. The exit 3 is not a measurement and is not counted as one.

Not run locally, by design: the 11 wide-population families and the whole-repo pnpm lint sweep belong to CI. Control-byte self-scan on both edited files: no hits.

Line budget

file before after ceiling edited lines (bytes)
.claude/agents/os-dev.md 403 403 403 :287 108→119 · :301 110→114
.claude/skills/pm-dispatch/SKILL.md 812 812 812 :646 116→118

Paid by density in place; no line merged or split.

Deviations, declared

  1. SKILL.md :646: the token swap alone (NPR-NUMBER, +8 B) puts the line at 124 B, over the 120-byte cap. Two characters were dropped on the same line (, ), content unchanged, landing at 118 B. Still inside the one-line REGION claim.
  2. os-dev.md :287: the Clause-②: yes trigger is gone for the byte cap; the rule is unconditional (see A1). If the seat wants the trigger back, the price is the 作读数 tail or the 不等 limb.
  3. os-dev.md :287 does not spell the objectui invocation (A4); the line names the flag, the script header names the sibling form.
  4. os-dev.md :301 edited under A2 (inside the file surface and the card's class). objectui's copy and the dispatch-order template are untouched.
  5. skip-changeset: nothing published moves — .claude/** is on the fast path (no package files[] ships it).
  6. origin/main merged at 68fea8bc (one spec commit, nothing on this surface) before opening; pm-dispatch: reconcile the quota rows with the identity-bound rate limit; merged-write rule; account-suspension recovery readings #18242 remains open on SKILL.md :172–:185 — disjoint from :646.

Acceptance notes

  • To file (class b, for the seat — dedupe words: needs:contract-review, PR 创建者, 同笔挂, SKILL.md, direction A): SKILL.md :645 still reads 「needs:contract-review(恒英文)由 PR 创建者随可复审契约增量同笔挂:draft PR,或先到的报告。」 For a dev-created PR the creator is the dev, which direction A says never hangs it. Outside this card's REGION claim (:646 only), so not edited here; it sits on the SKILL.md chain the seat owns.
  • noted, not filed: 4 --pair N spellings remain in scripts/pm/** docblocks and one self-test title (listed under A3); each is next to text that binds N to a PR number. 承接者:无.
  • noted, not filed: os-dev.md :300's 「可达性按会话探,先探后用」 is redundant with the new :301 — the write attempt is the probe. Left as is; deleting it frees bytes, not a line. 承接者:无.
  • Observed on this PR after opening: the labels present are reported in the os-dev-report comment with a read-back; under one shared identity the author field says nothing about who hung what.

维护者速读(草稿)

改了什么:改了两个受管规则文件里的三行,行数不变。开发 agent 定义(os-dev.md)一行:needs:contract-review 这个标签归席位挂,开发 agent 不挂、不摘、不等,只在报告里写 PR 上有没有它、以及 --pair PR-NUMBER 的退出码,作读数。另一行:加法写标签被容器拒绝时,报端点与状态码、由席位代挂,不再报 blocked、不走 MCP。PM 技能(SKILL.md)一个 token:--pair N 改成 --pair PR-NUMBER

为什么改:两次独立实测里,开发 agent 的容器拒绝写标签,而规则要求它开 PR 同笔挂——照做就失败、如实报告又像抗命。分诊裁定 A 向:载体由席位挂(认领时挂卡、PR 出现时挂 PR),开发 agent 只报所见。同一行的 N 让人绑到卡号,脚本要的是 PR 号,给卡号返回 exit 2(未判),会被当红灯读;PR-NUMBER 是唯一拼写。

风险与代价(含回滚):行数与上限不变,每行 ≤120 字节,决策框架块未动。风险是开发 agent 不再自己挂载体,若席位漏挂,条款② PR 的载体会缺失——但席位侧已经是现行做法,且开发 agent 报告的 --pair 读数会暴露缺失。回滚 = revert 本 PR,无其他副作用。

席位意见:

你要做的:审阅上面三行措辞(尤其 :287 去掉了 Clause-②: yes 触发词、改为对每个 PR 都成立);同意即给授权批准,由席位落地;不同意请指出要保留的措辞。


Generated by Claude Code

…air PR-NUMBER` is the only spelling

os-dev.md: the dev never hangs, removes or waits for `needs:contract-review`;
it reports the carrier state seen on the PR and the `--pair PR-NUMBER` exit
code as a reading. A refused additive label write is reported with endpoint
and status for the seat to hang — never `blocked`, never MCP.
SKILL.md: the one `--pair N` token becomes `--pair PR-NUMBER`.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HZfg2AwVX191qCizp88gQr
…e PR creator

SKILL.md :645 now says the seat hangs `needs:contract-review` in the same
stroke: on the PR as soon as it exists, on the card first when the report
arrives before the PR — the order contract-review.md already states.
Equal-line, paid by density.

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

claude Bot commented Sep 15, 2026

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: d181b4f0c65d10f8bbf5d34d49bb50bd3e6d95a6

Reviewing seat: domain:skills, in-seat review at the contract-review tier (get_session at 2026-09-15T02:54Z: configured = session = last served, the constant's tier). Card #18181, claim 5674057831, reports 5674343438 (round 0) and 5674431308 (patch round 1 after REWORK 5674367642); Clause-②: no on the claim and in the PR body.

① Derived judgments

  • Accept set / public surface: none moves — .claude/agents/os-dev.md (+2/−2) and .claude/skills/pm-dispatch/SKILL.md (+2/−2) against merge-base 75237a9a; nothing published; skip-changeset on the PR (read back with size/xs, documentation).
  • The four lines, read on the head by the seat in a detached worktree at d181b4f0: os-dev.md :287 (119 B) 「needs:contract-review 归席位,⛔ 不挂不摘不等;报 PR 上有无与 --pair PR-NUMBER 退出码作读数。」; os-dev.md :301 (114 B) 「被拒 ⇒ 报端点与状态码、席位代挂,⛔ 不报 blocked、不走 MCP;写后必做对比式读回。」; SKILL.md :645 (101 B) 「needs:contract-review(恒英文)由席位同笔挂:PR 一现即挂 PR;报告先到则先挂卡。」; SKILL.md :646 (118 B) with --pair PR-NUMBER as the only spelling. Direction A holds in every carrier — the seat hangs (:645), the dev never hangs, removes or waits (:287), a refused additive label write is reported for the seat to hang, never blocked, never MCP (:301) — and contract-review.md :15–:16 keeps the ACCEPT-time PR-side catch-up. No rule is homeless.
  • Ratchets on the head, seat-run: 403/403 and 812/812 ✓, check-skill-id-lint 27 files clean ✓, check-skill-frame-sync ✓, frame block :734–:755 md5 3327d02c56f8a0eca88569dad2270f32 unchanged ✓; every edited line ≤ 119 B by the seat's own awk length.
  • A3: --pair N is gone from the three governed files (contract-review.md :42, os-dev.md :287, SKILL.md :646 all spell PR-NUMBER); the four remaining hits sit in scripts/pm/** docblocks beside text binding N to a PR number — outside the surface, recorded. A4: the sibling invocation PM_SWEEP_REPO=objectstack-ai/objectui node ../objectstack/scripts/pm/check-clause2-carriers.mjs --pair PR-NUMBER measured working from an objectui checkout (exit 0 on a live pair, exit 2 on a closed one); the byte cap keeps it off :287 and the script header names it. A2 measured: the additive label write was NOT refused on this container (200, read back), so the refusal is per container as the card said; :301 covers the refusing containers.
  • Gates 18 / 18 / 0 / 0 on the head per the patch report with exits captured by redirect (doc-formula-expressions after its prerequisite build under the verify lock); --pair 18250 exit 0 re-read by the seat on the head.

② Semver level

Not applicable — nothing published moves; skip-changeset measured (.claude/** is on the fast path).

③ Boundary flags

open_questions: none. Round-0 deviations (6) accepted on the REWORK; patch-round deviations (3) accepted: the widened region (the seat's own act), the catch-up clause kept in the reference, the worktree recreated from the pushed branch with no work redone. The REWORK's one item is discharged (the (b) finding on :645 is closed by this head). Out-of-scope notes — four --pair N docblock spellings in scripts/pm/**; os-dev.md :300 redundant with :301 — are acceptance notes, no card. Governed (both files = rules layer) ⇒ four-piece: needs-user-decision on the PR, 速读终稿 below, reviews requested; ⛔ not flipped ready by the seat before an authorized approval. Serial: PR #18242 (:172–:185) is disjoint; the second to land merges origin/main first.

Implemented-by: claude/issue-18181-carrier-is-the-seats-to-hang
Reviewed-by: session_01HZfg2AwVX191qCizp88gQr

VERDICT: PASS


Generated by Claude Code

@claude

claude Bot commented Sep 15, 2026

Copy link
Copy Markdown
Contributor Author

维护者速读(终稿)

改了什么:两份席位规则文件各改两行,等行数、每行 ≤ 120 字节。① os-dev.md :287:needs:contract-review 这枚复核标签归席位——dev 不挂、不摘、不等,只在报告里写 PR 上有没有它,并附 --pair PR-NUMBER 的读数;:301:dev 的加法标签写被容器拒绝时,报端点与状态码由席位代挂,不再报 blocked、不走 MCP。② SKILL.md :645:标签由席位同笔挂(PR 一出现就挂 PR;报告先到就先挂卡);:646:--pair N 改成 --pair PR-NUMBER,三份治理文件里只剩这一种拼写。

为什么改:原文要求 dev「开 PR 同笔挂标签」,而 objectui 的两名 dev 实测被容器的写分类器拒绝——照做的 dev 失败、报告失败的 dev 看起来像抗命;同一行还把 --pair N 的 N 绑到卡号,脚本要的却是 PR 号(传卡号退 2 = 未测,天真读法当红)。席位本来就在替 dev 挂这枚标签,现在把文本改成事实。补丁轮顺手把 SKILL.md :645「由 PR 创建者挂」改掉——否则规则层落地后会自相矛盾。

风险与代价(含回滚):纯文本、不碰发布包、不改 CI;行数不变。回滚 = revert 一个 PR。放宽容器写权限(方案 B)是权限面的事,不在本 PR,由你另定。一处已知残留:scripts/pm/** 的 4 处 docblock 仍写 --pair N,旁边都有 N 绑 PR 号的说明,不动。

席位意见:建议批准。本席在 head 上亲核:四行内容与字节(119/114/101/118)、棘轮 403/403 与 812/812、frame 块 md5 不变、id-lint 与 frame-sync 绿、--pair 0;objectui 侧调用形态 dev 实测可用。CI 在新 head 上重跑中(旧 head 的 type-check 红是并发取消伪红),绿后才由本席按裁决 C 落地;与 PR #18242 同文件不同区域,后落地者先合 main。

你要做的(一个动作):approve 本 PR。approve 后本席翻 ready + 入队。


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review September 15, 2026 06:07
@os-zhuang
os-zhuang added this pull request to the merge queue Sep 15, 2026
@claude

claude Bot commented Sep 15, 2026

Copy link
Copy Markdown
Contributor Author

Ruling-C landing provenance — skills seat, session session_01HZfg2AwVX191qCizp88gQr, 2026-09-15T06:14Z. Authorized approval: os-zhuang's APPROVED reviews 5206130889 (2026-09-15T06:07Z) and 5206135851 (2026-09-15T06:07Z) on head d181b4f0c65d10f8bbf5d34d49bb50bd3e6d95a6, the same head the review of record 5674459504 (## Contract review PASS) names and the ACCEPT 5674459894 on #18181 cites; not dismissed. The approver also flipped the PR ready (2026-09-15T06:07Z) and enqueued it (added_to_merge_queue 2026-09-15T06:07Z, actor os-zhuang; queue ref gh-readonly-queue/main/pr-18250-* present on origin at 2026-09-15T06:13Z), so the seat's remaining acts under ruling C are the readings and the label: check-clause2-carriers --pair 18250 exit 0 re-read at 2026-09-15T06:13Z with the C6-RECORD note naming that record; on that head Lint & Repo Gates and TypeScript Type Check both success and every other check is success or skipped (31 runs: 18 success, 13 skipped); mergeable_state clean, no main commit touching os-dev.md or SKILL.md since the merge-base, so no branch update was made (the queue builds the merge, behind PRs #18242 and #18244); needs-user-decision cleared through label-write.mjs and read back. Serial note: PR #18242 (:172–:185) and PR #18256 (:306–:415) share SKILL.md in disjoint regions and are in the same queue; PR #18273 (#18264) holds os-dev.md :319–:398, disjoint from this PR's :287 / :301, and merges origin/main at its own landing. MERGED is confirmed later by two readings (the queue ref gone + git log origin/main carrying (#18250) with one parent), then #18181's residue is stripped.


Generated by Claude Code

Merged via the queue into main with commit 41ff021 Sep 15, 2026
35 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-18181-carrier-is-the-seats-to-hang branch September 15, 2026 06:32
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 17, 2026
…quotes the governing line (objectstack-ai#18256)

Fixes objectstack-ai#17497

One rule line in 〈分诊座位职责〉 of `.claude/skills/pm-dispatch/SKILL.md`: a
triage grading whose direction lands inside an adjudicated area (an ADR,
a ruled card, a not-re-adjudicable rule) pastes the governing line — the
`Prior rulings read:` line that `check-prior-rulings.mjs` prints — or
names no target. Triage keeps writing directions (「写方向」 is charter
text); the line binds the direction's evidence, never removes the
direction.

Governed surface (`.claude/**`, rules layer): draft, no reviewer
requested, no ready flip — the skills seat hangs the four-piece and the
maintainer lands it. REGION claim :306–:415 only; PR objectstack-ai#18242 holds
:172–:185 and PR objectstack-ai#18250 holds :645–:646 on the same file and neither
range is touched here (the seat merges `origin/main` at landing).

## The added line (SKILL.md :339 on the head, 120 bytes)

```text
- 方向落在 ADR/已裁卡/不可重裁规则:贴 `check-prior-rulings.mjs` 的 `Prior rulings read:` 行,或不点。
```

Vocabulary is the corpus's own: 「已裁卡」 (SKILL.md 〈家族派发〉), 「不可重裁」
(〈派发词三分区〉), 「落点」 = the named fix target (〈分诊座位职责〉's `pm:queue`
definition). The tool is cited by file name and the paste line by its
literal prefix — no issue or PR number (`check:pm-skill-id-lint` green).

**Placement (P2, chosen by reading the section's order):** directly
under the line that mandates the direction — 「裁定写方向、给六态之一」 (:338 on the
head, :339 on `origin/main`). That is where 「写方向」 actually lives on this
tree: `references/lanes/triage.md` is 7 lines and its :7 is the
half-state rule, so the grading's citation of 「写方向」 at `triage.md :7` is
stale as a location while its substance holds. The English audit comment
line (`Triage: lands in …; rationale: …`, :364 on the head) was the
alternative; it names the artifact the direction is written into, but
the obligation reads best beside the mandate it qualifies, and 「方向」 is
anchored by the preceding line there.

**P1 confirmed by a live run** (`node scripts/pm/check-prior-rulings.mjs
--card 17497`, exit 0) — the tool printed exactly the shape the line
cites:

```text
Prior rulings read: triage,gradings,named,target,governing,itself,rejects,grading,quotes,line,adjudication,routes → 61 hits; ADR-0129 D4, ADR-0067 D3, ADR-0076 D11, ADR-0006 D2, ADR-0020 D2, ADR-0021 D1
```

`formatPasteLine` (`check-prior-rulings.mjs`) prints `Prior rulings
read: TERMS → N hits; ADR-xxxx Dn|none` and the whole-line form `Prior
rulings read: unresolved` when the search did not run;
`check-half-states.mjs` H62 reads the same key (`PRIOR_RULINGS_KEY`),
and `references/decision-analysis.md` already binds that line for
decision cards.

## Payment (P3, measured) — one adjacent pair inside the region
rewritten tighter

Before (`origin/main` :311 + :312, 89 B + 50 B):

```text
- fire 开局只按名加载互斥检查所需工具,`ToolSearch` 用 `select:` 形式。
- 判定本轮有活之后才加载其余工具。
```

After (head :311, 116 B):

```text
- fire 开局只按名加载互斥检查所需工具,`ToolSearch` 用 `select:`;判定本轮有活才加载其余。
```

Nothing operative is lost: every instruction token survives — `fire 开局`,
`只按名加载`, `互斥检查所需工具`, `ToolSearch`, `select:`, `判定本轮有活`, the ordering
`才加载其余` — and the three dropped words are filler (「形式」 names the code
span it follows; 「之后才」 → 「才」 keeps the only-after ordering; 「其余工具」 →
「其余」 in a line whose subject is tool loading). The PM's measured pairs
(:312+:314, :363+:364, :397+:398) all rewrite past 120 B; this is the
pair that fits. Not a re-wrap (no line-joining bought the budget), no
rule deleted, ceiling untouched.

## Reverse verification (on head `c96b507d`)

- `git diff origin/main -- .claude/skills/pm-dispatch/SKILL.md`: two
hunks, `@@ -308,8 +308,7 @@` (the fold) and `@@ -337,6 +336,7 @@` (the
insert) — both inside :306–:415; no other line moves.
- `wc -l` = 812 (ceiling 812, headroom 0 — the ratchet's own line:
`.claude/skills/pm-dispatch/SKILL.md is 812 lines (ceiling 812; headroom
0)`).
- `sed -n '734,755p' | md5sum` = `3327d02c56f8a0eca88569dad2270f32`
(frame block byte-identical).
- an `awk` length scan for lines longer than 120 bytes over :306–:415
prints nothing; touched lines measure 116 B (fold) and 120 B (new).
- Positive anchors: `grep -c 'Prior rulings read:'` = 1; the removed
:312 text = 0 hits; the fold text = 1 hit.

## Gates (derived, run in the foreground, exits captured before any
pipe, reconciled with `--ran`)

`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` (no paths; change set derived off merge base
`cb648cb1`) → 16 commands; `--ran` verdict: `16 derived famil(ies)
accounted for — 16 run, 0 NOT-MEASURED (a DERIVED zero — all 16 recorded
an exit code and none of them is 3)`, exit 0.

| command | exit |
|---|---|
| `node scripts/check-closing-keyword-parity.mjs` | 0 |
| `node scripts/check-closing-keyword-parity.mjs --self-test` | 0 |
| `node scripts/check-comment-mask-corpus.mjs` | 0 |
| `node scripts/pm/check-governed-queue-guard.mjs --self-test` | 0 |
| `pnpm --filter @objectstack/lint run check:doc-formula-expressions` |
first run exit 3 `PREREQUISITE NOT MET` (unbuilt `@objectstack/formula`
/ `@objectstack/lint`) — built both under `os-verify-lock.sh` (`VERDICT
command-exit 0`), re-ran: 0 |
| `pnpm check:agent-test-spelling` | 0 |
| `pnpm check:doc-authoring` | 0 |
| `pnpm check:driver-memory-census` | 0 |
| `pnpm check:nul-bytes` | 0 |
| `pnpm check:pm-governed-merges` | 0 |
| `pnpm check:pm-governed-prose` | 0 (`2 instruction surface(s) name all
5 registered governed surfaces`) |
| `pnpm check:pm-skill-id-lint` | 0 (`27 file(s) clean`) |
| `pnpm check:pm-skill-ratchet` | 0 (812/812) |
| `pnpm check:refd-timer-probe` | 0 |
| `pnpm check:skill-frame-sync` | 0 (`the one declared copy of the
decision frame is internally coherent`) |
| `pnpm check:watch-hint-literal` | 0 |

Extra, outside the roster: `node scripts/check-skill-frame-freshness.mjs
--self-test` exit 0 and `--no-fetch` exit 0 (`the decision frame in this
tree is current with refs/remotes/origin/main`). The roster did not
derive `check:pm-dispatch-gates`, so no detached run was owed. Full
repo-level scans (`pnpm lint`) belong to CI.

`skip-changeset`: `.claude/**` is published by no package's `files[]`
(fast track) — nothing released moves.

## Acceptance notes

- noted, not filed: the grading's location citation for 「写方向」
(`lanes/triage.md :7`) is stale — that file's :7 is the half-state rule
and the charter text lives at SKILL.md :339 on `origin/main`; the
ruling's substance is unaffected. 承接者: the skills seat, at landing of
this PR.
- noted, not filed: the dispatch names a gate
`check:pm-skill-frame-sync`; the script's real name is
`check:skill-frame-sync` (`scripts/check-skill-frame-sync.mjs`), and it
ran green. 承接者: the dispatching PM seat, via this report.
- noted, not filed: the ratchet header's reading 「of the file's 601
adjacent bullet pairs ZERO merge under the 120-byte cap」 was a pure-join
measurement on an earlier tree; on this tree one pair folds at 116 B as
a tighter rewrite. Not a contradiction of that reading and not a defect.
承接者:无.

## 维护者速读(草稿)

**改了什么**:分诊座位职责里加一行:分诊的方向若落在 ADR、已裁卡或不可重裁规则内,须贴
`check-prior-rulings.mjs` 印出的 `Prior rulings read:` 行,否则不点落点。为了不突破 812
行上限,把开局工具加载的两行合成一行(116 字节),每条指令原样保留。

**为什么改**:两次分诊给出的落点恰是治理文本已经拒绝的拼写或形状(一次是字段 schema
拒收的扁平拼写,一次是标记为不可重裁的通则),都在执行席的围栏才被拦下,其中一次派发令已写出。分诊的推理没错,错在落点没有引治理行,所以看不见。决策卡那边已有同一行的约束,这次把它也绑到分诊评级上。

**风险与代价(含回滚)**:一行规则、一次同义压缩,不动上限、不删规则、不改工具。风险是新行 120 字节写得紧,读者需要认得
`check-prior-rulings.mjs` 这个工具(它已在仓内、有自检)。回滚 = revert 本 PR 一次提交。

**席位意见**:(留空,席位定稿成评论)

**你要做的**:确认这条规则的措辞与落位(放在「裁定写方向」那一行之下);同意即批准本 PR,由席位落地。

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

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 17, 2026
…bjectstack-ai#18273)

Fixes objectstack-ai#18264

The `os-dev-report` contract in `.claude/agents/os-dev.md` (:319 the
comment; :360–:377 the JSON shape) gains ONE field, `session`, so every
dev report carries in its TEXT the `session_` id that the same file's
:51 makes the attribution — 「归属 = 文本里的 session ID,非 `user.login`」 — and
that the re-keyed H64 in `scripts/pm/check-half-states.mjs` reads. One
governed file, two hunks, both inside the claimed REGION :319–:398 (PR
objectstack-ai#18250's :287 / :301 are untouched). `check-half-states.mjs` is not in
the diff: P2 below is the measurement that says it does not need to be.

Landing: `.claude/**` is the rules layer of the governed surface — this
PR stays a DRAFT; the seat hangs the four-piece after the report; an
authorized approval lands it. `skip-changeset`: `.claude/**` publishes
nothing (os-dev.md :297, the fast lane).

## What changed — 403/403, every edited line at or under 120 bytes

Line numbers are on this branch at `f60aba4b`; B = bytes of the line as
stored (`LC_ALL=C awk '{print length($0)}'`). The file spells the
placeholder inside angle brackets, as the template always has; it is
written here as ID.

**Added, :366 (117 B)** — the fifth identity field, right after `pr`:

"session": "session_ID — this run's harness-stamped id (transcript
Claude-Session: line; subagent = parent's)",

**Paid, :379 + :380 → :380 (106 B + 66 B → 119 B)** — a tighter rewrite
of the adjacent note pair under the JSON block:

before - `premise_still_valid: false` = 你的核验证伪了 issue 的前提(规则 6):证据写进
`summary`。
    before  - 那时 `pr` 为 null 或只圈存活的部分,PM 重新分诊。
after - `premise_still_valid: false` = 核验证伪了前提(规则 6):证据进 `summary`,`pr`
null 或只圈存活部分。

Why nothing operative was lost: the words that left are 你的 / issue 的 / 写
/ 那时 / 为 / 的 and the clause 「PM 重新分诊」 — the rule-6 cross-reference, the
evidence-in-`summary` instruction and the `pr`-null-or-surviving-part
rule all survive; 「PM 重新分诊」 is the PM's act, not a dev instruction, and
SKILL.md 〈报告契约〉 already states it (「当再分诊输入复核」).

`wc -l` = 403 before and after. `pnpm check:pm-skill-ratchet` on the
head: `✓ check-skill-line-ratchet: .claude/agents/os-dev.md is 403 lines
(ceiling 403; headroom 0).`

### The payment deviates from the suggested route, and says so

The ruling allowed 「a semantic no-op fold or a tighter rewrite of
adjacent lines」. The fold was measured — `open_questions` :372–:374
collapses to one 111 B line, JSON-identical — and NOT taken: os-dev.md
:292–:293 (「付行数棘轮的唯一合法货币是删内容:⛔ 不拿
re-wrap(折行合并)当筹行,新增以删减付账」「分界只问折行有没有为新增内容买行」) and the ratchet ledger
(「re-wrap funding is refused per the 2026-08-17 rule」) refuse a fold
that buys a line for new content, and the file's own conflict clause
makes the file win over the dispatch prompt. The tighter rewrite is the
route's second option and the file's currency at once.

Adjacent-pair measurement of the region, the way the SKILL.md precedent
did it: no two adjacent prose lines concatenate under 120 B — :319+:320
= 223, :320+:321 = 200, :321+:322 = 207, :322+:323 = 202, :323+:324 =
210, :324+:325 = 178, :378+:379 = 198, :379+:380 = 172, :380+:381 = 160,
:381+:382 = 203, :386+:387 = 231, :387+:388 = 225, :388+:389 = 183,
:389+:390 = 171, :390+:391 = 210, :391+:392 = 221, :392+:393 = 195
(pre-edit numbering). So every 2→1 merge deletes words, and :379+:380 is
the one pair whose deletable words carry no dev rule. Candidate prose
lines for a SECOND added line (the no-MCP route and the subagent case,
in Chinese) measured 134–141 B in every spelling tried, over the cap —
which is why the whole instruction lives in the one JSON line.

## P1 — how a dev actually obtains its id (measured in this run,
`mode:subagent`)

| route | reading |
|---|---|
| the harness attribution stamp in the transcript (the `Claude-Session:`
line) | `session_01HZfg2AwVX191qCizp88gQr` |
| `get_session` with no argument (the Claude Code Remote MCP — not a
GitHub tool) | `session_01HZfg2AwVX191qCizp88gQr`, title `PM dispatch
skills` |
| env `CLAUDE_CODE_REMOTE_SESSION_ID` | `cse_01HZfg2AwVX191qCizp88gQr` —
the same 24-character tail under a `cse_` prefix |
| env `CLAUDE_CODE_SESSION_ID` | `d13ed524-f222-55f1-9c04-ff395efe6ed7`
— a UUID, not a `session_01` form; H64 cannot read it |
| env `CLAUDE_CODE_CHILD_SESSION` | `1` |

**P1 is half-falsified.** A subagent has no `session_01…` id of its own:
every route the harness offers stamps the PARENT session's id — the same
id it already puts on the dev's commit trailer and PR-body footer
(os-dev.md :286 / :398). So in `mode:subagent` the only value that can
be filled honestly equals the dispatching PM's id by VALUE; what the
ruling's ⛔ protects — provenance: read your own stamp, never copy the
prompt — still holds, and is what the line says (「this run's
harness-stamped id」). The line is followable with no MCP (the
transcript's `Claude-Session:` line, the source the footer already draws
on) and names the subagent case so the field is fillable in both modes;
`get_session` is the MCP-only second route and stays out of the rule
line (byte budget; not universal). In `mode:cloud` the same routes
return the dev session's own id. This PR's own report carries
`"session": "session_01HZfg2AwVX191qCizp88gQr"` — the first live
instance of the contract, and the first instance of the subagent case.

Premise correction, small: the ruling's 「the way the file already
prescribes for the harness-stamped `model` field」 — os-dev.md has no
such field; that discipline lives in `references/contract-review.md` :57
and `references/platform-readings.md` :369 (`get_session` →
`last_served_model`). The analog inside os-dev.md is the PR-body
session-URL footer, which is what the new line is anchored to.

## P2 — the id in a JSON value clears H64; the reader is untouched

Replayed through the UNTOUCHED module at `c053f748` (`git diff e592b4b
HEAD -- scripts/pm/check-half-states.mjs` is empty): an `os-dev-report`
comment, fenced JSON, only the platform's bare footer, `created_at`
after the pin.

| fixture | `seatSignature` | `seatSessionIdPresent` | `h64SpeaksAbout`
| `h64UnattributedSeatContent` |
|---|---|---|---|---|
| WITH `"session": "session_01HZfg2AwVX191qCizp88gQr"` | `report` |
`true` | `false` | `null` |
| WITHOUT the field (the control) | `report` | `false` | `true` |
「comment `1` on this card carries an `os-dev-report` marker — a seat/dev
artefact — and NO session id appears anywhere in its text…」 |

H52's `devReportOpenQuestions` reads
`{"marked":true,"parsed":true,"questions":[]}` for BOTH bodies: the
reader parses the object and reads `open_questions` alone, so an extra
key is neither seen nor judged. `seatSessionIdPresent` tests the WHOLE
body with `SEAT_SESSION_ID`, so a JSON value carrying the id attributes
the report with no reader change — `check-half-states.mjs` stays out of
the surface, as the ruling's measure-first clause asked.

## P3 — one JSON, two copies

:319 is unchanged: the comment is 「同一段 JSON」 as the terminal message, so
the field reaches both copies through the one payload. No second
mechanism.

## Gates — all on head `f60aba4b`

Roster derived with `node scripts/pm/dispatch-gates.mjs --commands
--repo objectstack-ai/objectstack`, NO paths (stderr: change set 1 path
vs merge base `c053f748b`; committed 1, working tree 0, untracked 0): 17
commands. Every one ran in the foreground with the exit captured by
redirect before any pipe and recorded as `command :: exit N`:

- 16 exit 0 on the first pass; `pnpm --filter @objectstack/lint run
check:doc-formula-expressions` exit 3 (PREREQUISITE NOT MET —
`@objectstack/lint` not built), re-run exit 0 after `pnpm exec turbo run
build --filter=@objectstack/formula --filter=@objectstack/lint
--concurrency=2` under `scripts/pm/os-verify-lock.sh` (`VERDICT
command-exit 0`). No exit-3 reading is reported here as a measurement.
- `--ran` verdict: `✓ dispatch-gates --ran: 17 derived famil(ies)
accounted for — 17 run, 0 NOT-MEASURED (a DERIVED zero — all 17 recorded
an exit code and none of them is 3).`
- Named by the route and not in the roster: `pnpm
check:pm-governed-prose` exit 0 (`2 instruction surface(s) name all 5
registered governed surfaces … and claim no others`).
`check:pm-dispatch-gates` was not derived for this change set, so it was
not run.
- Verdict lines: the ratchet line above; `check:pm-skill-id-lint`: `27
file(s) clean (pattern /#[0-9]{3,}/g)`; `check:agent-model-declared`:
`os-dev.md → opus`; `check:nul-bytes` exit 0; the pre-push hook's
`check:commit-card-trailers`: `1 commit message(s) on this push carry no
card relation and no model identifier in the trailer pair`.

**Reverse verification of the ratchet (one-off, not landed):** with the
fix committed, a 404th line was appended under a `trap … EXIT INT TERM`
with an absolute repo root — marker count 0 → 1, mutated blob
`6f16705b…` against HEAD blob `7a914c94…` — and the gate went RED (`✗ …
os-dev.md is 404 lines; the ratchet ceiling is 403`, exit 1); `git
checkout HEAD -- .claude/agents/os-dev.md` restored it: marker count 0,
`git diff HEAD` empty, `git status --porcelain` empty, restored blob
equal to HEAD's.

## Acceptance notes

- noted, not filed: in `mode:subagent` the report's `session` equals the
claim's id by value, so H64 clears but a reader cannot tell a subagent's
report from its seat's own post by id alone; a per-subagent
`session_01…` id would be a harness feature, not a contract line. 承接者:无.
- noted, not filed: os-dev.md :318 (outside this region) restates :17
and is the natural payment for the next line this section needs. 承接者:the
next PR claiming :316–:318.
- noted, not filed: the ruling's `model`-field premise (above) — a
wording slip in the dispatch prompt; no rule is affected.

## 维护者速读(草稿)

**改了什么**:开发 agent 的终报契约(`.claude/agents/os-dev.md` 的 JSON 模板)新增一个字段
`session`,要求每份报告的正文自带写它的会话 id;同一节的两行说明合并为一行付账,文件仍是 403 行,改动只落在
:319–:398。

**为什么改**:同一文件 :51 规定「归属 = 文本里的 session ID」,而报告模板里没有任何一行能承载这个
id,平台附加的页脚也不带它;刚落地的 H64 巡查按此把每一份 dev 报告都记成「无归属」。补上字段后报告自证归属,巡查行自然消失(P2
实测)。

**风险与代价(含回滚)**:零运行时影响,不发布任何包;唯一代价是每份报告多一行。子代理模式下该 id 与派发席位相同(P1
实测),这是事实而非漏洞。回滚 = revert 这一个 commit。

**席位意见**:(留空)

**你要做的**:审阅并批准这张受管面 PR;落地由席位执行。

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

Co-authored-by: claude[bot] <claude[bot]@users.noreply.github.com>
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

2 participants