Skip to content

docs(pm,agents): three rules-layer lines catch up with the charter rulings - #18051

Merged
os-zhuang merged 3 commits into
mainfrom
claude/issue-18019-charter-residue-three-lines
Sep 13, 2026
Merged

os-zhuang merged 3 commits into
mainfrom
claude/issue-18019-charter-residue-three-lines

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes #18019

Three governed lines outside the charter rules-layer PR's declared file surface still carried pre-ruling text after 9489e2c0 (rules layer) and c185d087 (references) landed. Each now states the rule that governs it — one commit per file, each quoting its ruling verbatim and untranslated.

file was now budget
.claude/agents/os-dev.md :50 先搜再立 — the filer scans open issues before filing 立卡者不查重, 3–5 keywords on the card, never a board pull 403/403 lines, 115 B (cap 120)
.claude/skills/pm-dispatch/SKILL.md :106 跨车道移交 listed as a fourth release cause three causes, 改路由 qualified 限未派发, 在飞卡跟到 MERGED 812/812 lines, row 235 B
same file :113 跨域 PR 指定车道、改路由, unqualified 定车道与改路由限未派发卡 row 269 B; widest row unchanged at 342 B (L244)
.claude/skills/pm-dispatch/references/lanes/ui.md :25 draft until a human merges, 不留批准 draft until an authorized approval, then the owning seat lands it; the seat never approves 38/38 lines, 118 B (cap 120)

check:pm-skill-ratchet green with every touched ceiling still at headroom 0 and no ceiling moved; SKILL.md frame block L733-754 md5 3327d02c56f8a0eca88569dad2270f32 byte-unchanged.

Executable criterion — measured origin/main 226970b to HEAD

git grep -c per file: 先搜再立 in os-dev.md 1 to 0 · 跨车道移交 in SKILL.md 1 to 0 · 停在 draft 等人合 in lanes/ui.md 1 to 0. Lit controls on neighbouring surviving phrases, same greps, same two refs: noted, not filed 3 to 3 · 离手恒走释放 1 to 1 · 异议评论写明所求 1 to 1 · 未命中的 PR 按同节走合并队列落地 1 to 1.

Gates

node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at HEAD ac73575c derived 18 families; all 18 ran in the foreground with the exit code captured before any pipe, all exit 0; --ran reconciles 18 derived / 18 run / 0 NOT-MEASURED / 0 UNRUN. check:doc-formula-expressions first answered exit 3 PREREQUISITE NOT MET (unbuilt workspace package, nothing measured) and was re-run to exit 0 after turbo run build --filter=@objectstack/formula --filter=@objectstack/lint.

Repo-wide pnpm lint is not owed here and can produce no verdict on this diff: eslint's own config supplies no configuration for .md, and --format json over the three files reports 3 files, 0 errors, 3 warnings, each of them File ignored because no matching configuration was supplied. skip-changeset applies — .claude/ publishes nothing from any released package.

Premise checks

P1, P2 and P3 confirmed on origin/main 226970b. P4 confirmed: all 12 open PRs were fetched into a private ref namespace and diffed against their own merge-base — none touches these files. P5 measured NO: references/core-rules.md mirrors none of the three pre-ruling lines; it already carries the post-ruling statements at :8, :51 and :123, so it is not in this diff.

Acceptance notes

Noted, not filed: os-dev.md :53, :54 and :58 still describe the dedupe channel the filer no longer uses (the search_issues fallback on 403, the control-word rule, 那一次查重). Same residue class as :50, but outside this card's declared one-line surface and the file's 403/403 ceiling. Carrier: the seat that owns the charter-revision family.

维护者速读(草稿)

改了什么 — 三个文件各改一处(SKILL.md 是两行表格行),把还写着旧规矩的句子换成已经落地的规矩:立卡人只在卡面附查重词、不再自己查重;在飞的卡不跨车道移交,改路由只对未派发的卡;受管面 PR 停在 draft 等的是「授权批准」,批准到手后由认领席自己 ready 加入队落地,席位仍然永不批准。

为什么改 — 这三行落在章程改版 PR 的文件面之外,规则合入后它们就和已生效的规则直接打架。留着,下一个读到的人会照旧规矩办事。

风险与代价(含回滚) — 纯指令文本,没有运行时影响。三个文件都卡在行数上限,改动是等行替换,净增 0 行,棘轮一个都没抬。回滚就是 revert 这三个 commit,彼此独立、互不依赖。

席位意见

你要做的 — 受管面,PR 留 draft 等你。请确认第三条的措辞:拿到授权批准后由认领席自己落地,是不是你要的形态?


Generated by Claude Code

…the board

`os-dev.md` rule 3 still told the dev to dedupe before filing — a targeted
REST scan of open issues, with an MCP `search_issues` fallback. The triage
ruling moved dedupe to the triage seat and left the filer with keywords only,
verbatim and untranslated:

  「立卡者不查重,只在卡面附 3–5 个查重词」

The line now states that rule: no dedupe by the filer, 3–5 keywords on the
card, never a board pull. Net 0 lines (403/403), 115 bytes against the
120-byte cap.

Claude-Session: https://claude.ai/code/session_01DAcomhvR9kKizeYgg89Vo8
Co-authored-by: Claude <noreply@anthropic.com>
…card cannot take

Two rows in 〈状态模型〉 lagged bullets that landed with the charter rules
layer. The assignee row listed 跨车道移交 as a fourth release cause, and the
`pm:retriage` row offered 跨域 PR 指定车道 and 改路由 with no pre-dispatch
qualifier — while the landed bullet reads 「`pm:retriage` 改路由只对未派发卡」
and its neighbour holds a claimed card to 认领即跟到 MERGED, the cross-lane
surface reviewed in place (contract-review.md: 借复核不移卡).

The governing ruling, verbatim and untranslated:

  「C. approve 后不管后续改动都由席位落地:」

— ownership follows the claim through to landing, so a lane handover is not
one of the ways an in-flight card leaves its seat's hands. The assignee row
now carries three causes with 改路由 qualified 限未派发, and the retriage row
says 定车道与改路由限未派发卡.

Net 0 lines (812/812), widest table row unchanged at 342 B (L244), the two
edited rows 235 B and 269 B, 〈决策分析四轴〉 frame block L733-754 md5
3327d02c56f8a0eca88569dad2270f32 byte-unchanged.

Claude-Session: https://claude.ai/code/session_01DAcomhvR9kKizeYgg89Vo8
Co-authored-by: Claude <noreply@anthropic.com>
… at a human merge

Line 25 read a governed-surface hit as unconditional draft-until-a-human-
merges, with 不留批准 next to it. The ruling that landed with the charter
rules layer says otherwise, verbatim and untranslated:

  「C. approve 后不管后续改动都由席位落地:」

Once an authorized APPROVED review exists on a governed PR, the owning seat
lands it; the draft hold applies only until then. The ban that survives is
the one on the seat approving — never on the seat landing what an authorized
account already approved.

Net 0 lines (38/38); the line is 118 bytes against the 120-byte cap and is
still the file's widest.

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

claude Bot commented Sep 13, 2026

Copy link
Copy Markdown
Contributor
  • Served-tier: 1198/1198 claude-fable-5-1 — harness model stamp counted over this seat's own transcript (non-sidechain assistant messages a model served; <synthetic> harness notices excluded) at 2026-09-13T15:54Z; get_session external_metadata.last_served_model read claude-fable-5-1 at 2026-09-13T15:54Z.

Contract review

Head: ac73575c (PR #18051, card #18019) — read at 2026-09-13T15:55Z by the skills seat at the contract-review tier. GOVERNED rules layer, measured: .claude/agents/os-dev.md :50, .claude/skills/pm-dispatch/SKILL.md :106 + :113, .claude/skills/pm-dispatch/references/lanes/ui.md :25 (4 insertions, 4 deletions, three commits each quoting its ruling) ⇒ four-piece: this record + 速读终稿 below, ACCEPT on #18019, needs-user-decision on this PR, reviews requested from os-zhuang and hotlong; ⛔ draft until an authorized approval exists, then ruling C landing by this seat. Execution ran at opus under the tiering ruling's own exit (one-line-class mechanical governed edits), this review at claude-fable-5-1.

① derived judgments — each line now states the rule that already governs it, nothing new is ruled:

  1. os-dev.md :50 「立卡者不查重,只在卡面附 3–5 个查重词;查重归分诊席,⛔ 不扫 open issues、不拉板。」 — ruling ③ point 3 verbatim in substance (5652544529); 115 B; the surrounding :53/:54/:58 still describe the old channel (dev's note, same class, outside this surface — carried by this seat).
  2. SKILL.md :106 — the assignee row drops 跨车道移交 (an in-flight card follows through: 〈多仓协调〉 :227 认领即跟到 MERGED, contract-review.md 借复核不移卡) and qualifies 改路由 with 限未派发. The dev's open question is answered here: option A. 改路由(限未派发) is the narrowest reading that keeps the row true — the only window it can fire in is a self-claimed, not-yet-dispatched card, which is exactly the window the landed bullet 「pm:retriage 改路由只对未派发卡」 leaves open; B deletes a legitimate act, C leaves the row readable as a licence to re-route an in-flight card, the defect this card was filed about. 235 B.
  3. SKILL.md :113 — the pm:retriage row gains 「定车道与改路由限未派发卡」, agreeing with :106 and the bullet; 269 B. The widest row stays L244 at the 342 B pin.
  4. lanes/ui.md :25 「命中即停 draft;⛔ 未获授权批准不 ready 不入队不自合、永不批准,获批后认领席落地。」 — ruling C (5652306063) in one line; 118 B. ⛔ never approve is kept.
  5. Budgets: every touched file at its ceiling before and after — os-dev.md 403/403, SKILL.md 812/812 (widest row 342/342), lanes/ui.md 38/38 — equal-line replacements, no payment owed; frame block :733–:754 md5 3327d02c56f8a0eca88569dad2270f32 byte-unchanged (seat-measured on the head export). core-rules.md mirrors none of the three lines and already carries the post-ruling statements (:8, :51, :123) — the 〈优先级〉 same-PR rule is not engaged.
  6. Deviations, declared, accepted: the 速读 in the five-section shape os-dev.md fixes (a superset of the dispatch's four); the labels POST retried with Content-Type after a 415; MCP was used for the PR creation (so the PR is authored by os-project-manager, the linked user — a channel fact this seat measured only after the dispatch; the record and the card stay App-owned).

Seat measurements on the head tree (git archive ac73575c): check-skill-line-ratchet per-file rows all ✓ at headroom 0 (the run's one ✗ is my export lacking CLAUDE.md, not the PR); frame md5 equal. Executable criterion re-read on the diff: 先搜再立 1→0, 跨车道移交 1→0, 停在 draft 等人合 1→0, with the dev's four lit controls unchanged. Checks on ac73575c at 2026-09-13T15:54Z: 29 runs, 0 red, 2 running (Lint & Repo Gates, Type Check · consumer gates) — the landing check ③ reads them green before ruling C fires.

② semver: .claude/** publishes nothing; skip-changeset is right.

③ boundary flags: open_questions — one, answered above (A). Two 「noted, not filed」 items stand: os-dev.md :53/:54/:58 (this seat's family, next density pass) and lanes/ui.md :24 → objectui AGENTS.md §9 (the objectui card in this seat's filing batch).

Implemented-by: claude/issue-18019-charter-residue-three-lines
Reviewed-by: session_01DAcomhvR9kKizeYgg89Vo8

Verdict: PASS — three residue lines now say what PR #18018 and PR #18038 already rule, net zero everywhere; awaiting an authorized approval.

维护者速读(终稿)

改了什么:三个文件各改一行(SKILL.md 是两行表格行),把还写着旧规矩的句子换成已经生效的规矩:立卡人只在卡面附 3–5 个查重词、不再自己扫板查重;在飞的卡不跨车道移交,改路由只对未派发的卡;受管面 PR 停 draft 等的是「授权批准」,批准到手后由认领席自己 ready 加入队落地,席位永不批准。
为什么改:这三行落在章程改版两张 PR 的文件面之外,规则合入后它们和已生效的规则直接打架。
风险与代价(含回滚):纯指令文本,无运行时影响;三个文件都在行数上限,等行替换,净增 0 行,棘轮一个没动;回滚 = revert 三个 commit,彼此独立。
席位意见:通过。dev 问的一个字眼(assignee 行保不保留「改路由(限未派发)」)我按 A 定了:保留并限定未派发,是让这一行仍为真的最窄写法。
你要做的:一次批准(os-zhuang 或 hotlong)。批准后我按裁决 C 落地,不再找你。
一句问:同意「改路由(限未派发)」留在 assignee 行吗(是/否)?


Generated by Claude Code

@claude
claude Bot requested review from hotlong and os-zhuang September 13, 2026 15:55
os-project-manager pushed a commit that referenced this pull request Sep 13, 2026
…ontrols

Offline rows only: #18045, objectui#9404, PR #18051 and comment 5652138683
fire; comment 5654046782 (the same claim shape authored `claude[bot]`) is the
clean control, and every fire has a control differing in exactly one feature.

Two of the filing card's five signature forms were measured against the
specimens it named and would not reach them: the os-tesla claim carries no
`Session:` line, and PR #18051's only session token sits in its attribution
footer on the last line. Both widenings are pinned as measurements.

Claude-Session: https://claude.ai/code/session_01DAcomhvR9kKizeYgg89Vo8
Co-authored-by: Claude <noreply@anthropic.com>
@os-zhuang
os-zhuang marked this pull request as ready for review September 13, 2026 22:40
@os-zhuang
os-zhuang enabled auto-merge September 13, 2026 22:40
@os-zhuang
os-zhuang added this pull request to the merge queue Sep 13, 2026
@claude

claude Bot commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Ruling C landing step — skills seat (session session_01DAcomhvR9kKizeYgg89Vo8), 2026-09-13T22:41Z. Authorized approval read: os-zhuang APPROVED on head ac73575c (review 5192546519, submitted 2026-09-13T22:39Z; not dismissed), who also flipped the PR ready and armed auto-merge. Seat readings at 2026-09-13T22:41Z: ① the contract review of record 5654347747 names this head ac73575c (unchanged since); ② check-clause2-carriers --pair 18051 exit 0; ③ 35 checks on ac73575c, 0 red, 0 running; ④ needs-user-decision cleared in this pass and read back. The seat re-reads and re-arms only if the queue ejects it. Landing record follows on #18019 once origin/main carries (#18051); PR #18072 (locks 1–4) still awaits its own approval and merges origin/main after this lands (shared SKILL.md / os-dev.md on different lines).


Generated by Claude Code

akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 17, 2026
… a user account (objectstack-ai#18073)

Fixes objectstack-ai#18069

Lock 5 of the mechanism-layer plan. Maintainer, skills seat chat
2026-09-13T16:14Z, verbatim: 「机制层的五道锁 现在就派发处理」.

One REPORT-ONLY patrol row in `scripts/pm/check-half-states.mjs` (the
whole file surface). **H64** fires on an artefact that carries a
seat/dev signature while GitHub records its author as a USER account
rather than the App. A suspended user account hides everything it
authored, so the artefact's text and its account field disagree about
who wrote it and the record is held by an account the protocol does not
control. ⛔ No new listing, ⛔ no label, ⛔ no write path, ⛔ no roster of
seat accounts — the artefact is recognised structurally (H44's refusal,
taken for H44's reason) and the author is read only as the defect.

## Two of the card's five signature forms were measured and widened

| form | the card | measured | now |
|---|---|---|---|
| claim | `Claim:` block **with a `Session:` line** | comment 5652138683
— the card's own os-tesla fixture — carries no `Session:` line in its 35
| `CLAIM_COMMENT_MARKER` alone, the marker H2/H33/H34/H37 already share
|
| session id | bare id **in the first three lines** | PR objectstack-ai#18051's only
session token is line 45 of 45, in the attribution footer; the head
window fires on 0 of 9 open PRs, the footer form on 4 | head window
stays narrow, the `claude.ai/code/session_…` footer is a SIXTH form; ⛔
the platform's own bare footer is not a signature |

## Two mechanisms the live measurement forced

- **A pin (H55's shape).** 870 of 1075 signed texts on this board are
user-authored. `created_at` before `USER_AUTHORED_WRITE_SINCE`
(2026-09-13) is a CENSUS count and files no row — 816 of them, reaching
back to 2026-08-05 — because "re-post it through the proxy" is not a
remedy anyone performs 816 times.
- **A 10-row cap, newest first.** `renderMarkdown` sorts by card number
ASCENDING and the body trim eats the tail, so for this family the newest
write — the one the lock exists to surface within the hour — is the
first row removed. Changing that sort belongs to every family, so the
family bounds itself instead; the clause prints the full judged count on
every run.

`user.type` is the test, `user.login` is printed and never tested: the
login form is a one-name roster wearing an equality sign and judges
`github-actions[bot]` (2 open cards, 1 open PR here) as a user account.

## Verification

- `--self-test` 3915 → 4042 cases, green. Ablation, both legs restored
byte-identical to the HEAD blob: removing the author test reddens 4
cases, removing the footer form reddens 11.
- Live read-only run on today's objectstack board: 2073 texts read, 1075
signed, 870 user-authored, **54 judged, 10 filed, 816 census, 0
declined**. Rows name PR objectstack-ai#18051 and 9 newer artefacts; nothing authored
`claude[bot]`.
- All four specimens fire against their LIVE payloads — objectstack-ai#18045
(`session`), PR objectstack-ai#18051 (`footer`), objectui#9404 (`filer`), comment
5652138683 (`claim`); comment 5654046782, the same claim shape authored
`claude[bot]`, is silent. objectstack-ai#18045 is inside the judged 54 and outside the
rendered 10: the cap, working.
- Every family from `dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` run in the foreground at this head, exit
codes captured before any pipe. Full list and readings in the
`os-dev-report` on objectstack-ai#18069.

## Acceptance notes

- Declared residuals, both stated in the row's banner and in the summary
clause: a PULL-REQUEST comment thread is outside the corpus (H44's
declared residual, restated), and an EDIT re-writes a body through the
same channel without moving `created_at`.
- Noted, not filed: the self-test's row-wrapper discipline covers the
three-valued PREDICATES but nothing states it for three-valued HELPERS;
this row's own ablation aborted 4042 cases at a bare
`seatSignature(...).kind` before the wrappers went in. Carrier: the next
row that exports a nullable helper.

`skip-changeset`: `scripts/pm/**` ships in no package's `files[]`.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 17, 2026
… writes, REST-only dev writes with `api_writes`, `batch` default 2, user-account roles (objectstack-ai#18072)

Fixes objectstack-ai#18068

Dev session `session_01DAcomhvR9kKizeYgg89Vo8` on branch
`claude/issue-18068-write-identity-locks` (worktree
`objectstack-issue-18068`), off `origin/main` 6d64785, merged e248c4d
before opening (no incoming commit touched these files). One commit per
lock; each quotes its ruling.

## Rulings (verbatim, untranslated)
- Maintainer, skills seat chat, 2026-09-13T16:14Z: 「机制层的五道锁 现在就派发处理」 —
the whole card.
- Maintainer to the services seat, 2026-09-13: 「当前任务处理完,后续并发降到2」 — lock
3.
- Triage ruling ③: 「立卡者不查重,只在卡面附 3–5 个查重词」 — why os-dev.md :51–:58 were
stale (lock 2's payment).
- Standing exception, pm-dispatch SKILL.md: 「唯一例外:`platform-readings.md`
增量抬上限到落地行数,免决策卡,记 `ruledRaises`
引常设裁决。条件:席位验收评论逐条核实、去重计数(候选/落地/已有/拒收)、一事一行、不计重排」 — the +2 on
platform-readings.

## What landed
1. **Lock 1** `.claude/settings.json`: `permissions.deny` with the 14
content-writing `mcp__github__*` tools the card lists; `allow` and hooks
untouched; `JSON.parse` passes. Docs reading
(code.claude.com/docs/en/permissions and /settings): rules evaluate
deny, then ask, then allow; a deny at any scope beats an allow at any
scope; `mcp__server__tool` is the per-tool spelling; the shared
`.claude/settings.json` is read in cloud sessions and deny needs no
workspace trust — lock 1 is ENFORCED, and a denied tool is removed from
the roster. Recorded as two 文档载明未实测 lines in
`references/platform-readings.md` (references tier, declared): 451 →
453, THIRTEENTH `ruledRaises` record; candidates 2 / landed 2 / already
present 0 / refused 0, one matter per line.
2. **Lock 2** `.claude/agents/os-dev.md` 403/403: :51–:58 replaced by
the REST-proxy write rule (`curl` + environment `GITHUB_TOKEN`, authored
`claude[bot]`), the four-write budget, ⛔ no MCP GitHub write tool and no
board enumeration, payload-or-single-card reads, findings reported for
the seat to file, zero writes outside the budget (no PR-body `PATCH`),
the rest-channel pointer (kept), and `api_writes` + `mcp_calls` in the
report; the report template gains `"api_writes"`. In place, net 0: the
control-word rule now sits under rule 6; resource rule 6 routes late
results to the report; the label-write fallback no longer prescribes an
MCP `issue_write`; the `out_of_scope_findings` example no longer shows a
dev-filed card number.
3. **Lock 3** SKILL.md :60 「默认 `3`」 → 「默认 `2`」; ceiling `5` unchanged;
core-rules :11 states no default, so nothing mirrored.
4. **Lock 4** SKILL.md 〈全体座位的不变量〉 +2 lines (account roles; REST-proxy
content and approver never seats), paid in the section (state and resume
lines merged; the four Chinese channels named inline, dropping two
parentheticals restated in 复核 and 升级与决策); the 〈认领〉 shared-identity line
now reads 「身份只认正文 session ID,⛔ 不认作者字段」. core-rules 〈全体座位的不变量〉 one mirror
line, paid by folding the three language lines into two. 812/812 (widest
row 342 B), 151/151, frame :733–:754 md5
`3327d02c56f8a0eca88569dad2270f32` unchanged.

## Executable criterion, BASE 6d64785 → HEAD 7e1aeca
`grep -c mcp__github__issue_write .claude/settings.json` 0 → 1 (inside
`deny`); SKILL.md 「默认 `2`」 0 → 1 and 「默认 `3`」 1 → 0; os-dev.md
`api_writes` 0 → 2 (rule + template), `search_issues` 1 → 0; SKILL.md
「批准账号」 / 「永不跑席位」 0 → 1, core-rules 「批准账号」 0 → 1. Lit controls unchanged:
「每个方案必须沿四条固定评估轴分析」 1 → 1, 「一座位一车道双射」 1 → 1 in both files, os-dev.md
`mcp_calls` 2 → 2.

## Gates
`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` at 88e0610: 42 families, every one exit 0
in the foreground with the code captured before any pipe; `--ran`: 42
derived, 42 run, 0 NOT-MEASURED, 0 UNRUN.
`check:doc-formula-expressions` first answered exit 3 (PREREQUISITE NOT
MET, lint/formula unbuilt) — built under the verify lock (187 s held)
and re-run: exit 0. Rule ⑤ for the ratchet-script edit: its
`--self-test` (inside `check:pm-skill-ratchet`),
`check:ratchet-remedy-authority`,
`scripts/check-published-list-mirrors.mjs` and
`scripts/check-skills-token-ratchet.mjs` all exit 0. Re-run at 7e1aeca
after the merge: `pm-skill-ratchet`, `pm-skill-id-lint`,
`skill-frame-sync`, `pm-governed-prose`, `nul-bytes`,
`agent-model-declared` exit 0. Every added prose line ≤ 120 B (the one
longer added line is inside the report's JSON fence, structurally
exempt); SKILL.md over-120 baseline 23 → 23; control-character scan
empty. `skip-changeset`: nothing published moves (`.claude/**`,
`scripts/pm/**` only).

## Deviations, declared
- Lock 4 is two SKILL.md lines, not one: the card's four clauses do not
fit one 120-byte line; both are paid inside the section.
- Lock 2 edits four lines outside :51–:58 (rule 6 premise line, resource
rule 6, the label fallback, the report template), each net 0, each of
which would otherwise contradict the budget.
- The card's budget has no PR-body `PATCH`; the rule says so explicitly
and routes late gate results to the report comment. Allowing a body
refresh would be one clause on that line, the seat's call.
- PR objectstack-ai#18051 (open draft) edits os-dev.md :50, adjacent to this diff's
:51 — whichever lands second takes a one-hunk merge; its SKILL.md
:106/:113 lines do not overlap.

## Acceptance notes
- noted, not filed (承接者: the skills seat reviewing this PR):
`mcp__github__update_pull_request_branch` writes merge commits, and
`request_copilot_review` writes a review request, yet neither is on the
card's list, so both stay allowed.
- noted, not filed (承接者: the skills seat): SKILL.md :778 still reads
「只列三类立卡与 noted, not filed」; under lock 2 the dev lists findings to file
and the seat files them.

## 维护者速读(草稿)
**改了什么**:四道机制锁。① 仓库的 Claude 设置里禁掉所有"以用户账号写内容"的 MCP GitHub 工具(建 issue、开
PR、评论、审查、推文件、合并等 14 个),状态类与只读工具照旧;② 开发 agent 对 GitHub 的写只走 REST 代理(署名
`claude[bot]`),预算固定四笔,报告新增 `api_writes` 供席位核对;③ 并发默认 3 → 2,天花板 5 不变;④
写明用户账号只做三件事(assignee、授权批准、维护者亲手),批准账号永不跑席位,内容身份只认正文里的 session ID。
**为什么改**:今天的封号事故证明,用 MCP 工具写的内容署在关联用户名下,用户一被停,内容整批消失;走 REST 代理的内容署在 App
名下不受影响。并发只是放大器,身份才是被封的对象。
**风险与代价(含回滚)**:deny 名单在会话启动时读入,已开的会话不受影响;席位仍可用 `update_pull_request`
等状态工具翻 ready、挂 auto-merge。回滚 = revert 本 PR 的任一 commit(每锁一个
commit,互不依赖)。platform-readings 上限 +2 走常设例外,不另开决策卡。
**席位意见**:(留空,由席位定稿)
**你要做的**:一个动作 —— 在本 PR 上给出授权批准;受管面,席位按裁决 C 落地。

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 17, 2026
…he pre-ruling landing (objectstack-ai#18148)

Fixes objectstack-ai#18083

`scripts/pm/check-governed-queue-guard.mjs` decides on an authorized
APPROVED review and has
since the 2026-09-04 predicate landed. What had not moved was the text a
seat is told to
consult: the docblock called a draft awaiting the maintainer's own merge
the regime's terminal
state, and the printed remedy's preferred option told a seat to take the
PR out of the queue
"and leave the merge to the maintainer". Since the charter chain landed
(PR objectstack-ai#18018 `9489e2c0`,
PR objectstack-ai#18038 `c185d087`, PR objectstack-ai#18051 `137eb00e`) the rule is ruling C — issue
objectstack-ai#17971, maintainer
2026-09-13, verbatim and untranslated:

> C. approve 后不管后续改动都由席位落地:

So the narration under-permitted: it told a seat not to do the thing the
rule now says it
should do once an authorized approval is on record.

## What moved

Wording only. ⛔ No decision branch changed — `entrySatisfied`, the
approval reduction, the
tier split and every exit code are untouched. Anchored by content, not
by line number.

1. **Docblock, the "why the PR leg must not redden" paragraph.** The
healthy resting state is
now a draft *waiting for an authorized approval*, in the landed rule's
own words from
`.claude/skills/pm-dispatch/references/landing-operations.md`: 「四件套留
draft 等人批,⛔ 不翻正式不入队;获授权批准后认领席落地。」
2. **Docblock, a new paragraph for what happens after the approval** —
ruling C, quoted
verbatim, plus `SKILL.md`'s operational half: 「席位落地 =
过落地前检、清标、ready、auto-merge,踢出/变基同法。」
The seat's ready → enqueue is now stated as the correct next act there
rather than a violation.
3. **Docblock, the "this guard cannot stop a direct merge" paragraph.**
The direct merge is now
the OTHER landing rather than the only one — `references/core-rules.md`:
「受管面由维护者人合或授权批准后席位落地」.
   The objectstack-ai#11387 measurement it cites is kept verbatim as history.
4. **The `pull_request` leg's EARLY WARNING rendering.** The ⛔ list
(flip ready / enqueue / arm
auto-merge) is now explicitly conditional — "while no authorized
APPROVED review is on
record", with AGENTS.md Prime Directive objectstack-ai#14's own "lift only for that
approval" — and a new
   ✅ line says what a seat DOES do after the approval.
5. **The `merge_group` refusal remedy.** Steps 1 → 2 are now an order
rather than a menu:
out of the queue first, then the authorized approval, and the CLAIMING
SEAT lands it from
there. The unapproved direct merge (人工直合) is kept as the landing that PR
still has.
6. **Two internal comments** that restated the same pre-ruling shape:
the tier-default
asymmetry cost (was "costs one hand merge") and the exit-code precedence
note.

The references tier is untouched and still says the in-seat review of
record lands it: remedy
option 3 renders byte-for-byte as before, and both of its pins (offered
on a references-only
refusal, withheld on a rules-layer one) still pass.

## Acceptance readings

Taken on `a2e4cd7d9`, in the worktree, against merge base `a90a9f267`.

**The two pre-ruling phrases, in the narration and the printed text: N →
0.**

| reading | before | after |
|---|---|---|
| `git grep -c -E 'leave the merge to the maintainer\|human merge IS the
review record'` | 2 | 2 |
| … of those, hits in narration or printed remedy | 2 | **0** |
| … of those, hits inside the self-test pin that FORBIDS them | 0 | 2 |

Both remaining hits are the new negative assertion itself — a comment
and the regex literal in
`⛔
a-refusal-never-tells-a-seat-to-leave-the-merge-to-the-maintainer-nor-calls-that-merge-the-record`.
A pin has to name what it forbids; neither is text the guard ever
prints.

**The landed phrase: 0 → 5** (`git grep -c 'CLAIMING SEAT
lands\|claiming seat lands'`).

**Every hit of the pre-ruling wording enumerated** (`git grep -n -E
'hand merge|human merge|leave the merge|人工合'`),
6 before → 4 after, and each remaining one accounted for:

- `:44` 「人工合并即人工审核」 (docblock) — **moved**.
- `:52` "the human merge IS the review record" (docblock) — **moved**.
- `:536` "costs one hand merge" (tier-default comment) — **moved**, now
"costs one authorized approval".
- `:1171` 「人工合并即人工审核」 (EARLY WARNING rendering) — **moved**.
- `:1242` "leave the merge to the maintainer. A human merge" (remedy) —
**moved**.
- `:2010` (was `:1983`) the `objectstack-ai#9319` replay fixture's name, quoting PR
objectstack-ai#9238's own body: "a .claude/skills PR whose own body said 'awaiting a
human merge'" — **stays**. It is a historical measurement naming what
that PR said in 2026; rewriting it would falsify the fixture.
- `:2429`, `:2436`, `:2468` — **new**, the three negative pins (two
English phrases, one 人工合并即人工审核).

**Self-test:** `node scripts/pm/check-governed-queue-guard.mjs
--self-test` :: exit 0,
**233 cases before → 238 after** (5 added, none removed, no battery
floor lowered).

**Diff surface:** `git diff --stat a90a9f2` names exactly one file,
`scripts/pm/check-governed-queue-guard.mjs`, 104 insertions / 32
deletions. `origin/main`
advanced under this worktree during the run (`a90a9f267` → `739ab526d`,
a shared-ref hazard
AGENTS.md names), so the anchor above is the merge base, not the moving
ref.

**Governed?** No — `node scripts/pm/check-governed-merges.mjs --test
scripts/pm/check-governed-queue-guard.mjs`
:: exit 0, "NOT governed — ordinary queue landing applies". This PR is
opened as a draft and the
seat lands it; nothing here flips ready or arms anything on its own.

## Reverse verification — the new pins can actually fail

One-shot, from the committed state, with a `trap … EXIT INT TERM`
restoring absolute paths.
Predicted direction: **turns red**.

- HEAD blob `f7938efe94a20f34a3c1e6f07e2aaca393f16a47`.
- Mutation: the remedy's step-2 line rewritten back to the pre-ruling
wording. On-disk
observation, anchored on both texts: landed anchor 1 → **0**, injected
stale phrase 0 → **1**;
blob `87b912dbc8acaf5af5d02fb86cc06eb2bcffd986` ≠ HEAD blob, so it
reached disk.
- Mutated leg: `--self-test` :: **exit 1**, `✗ 2 of 238 case(s) failed`
— exactly

`a-refusal-orders-the-remedy-DRAFT-then-the-authorized-APPROVAL-then-the-CLAIMING-SEAT-lands-it`
and `⛔
a-refusal-never-tells-a-seat-to-leave-the-merge-to-the-maintainer-nor-calls-that-merge-the-record`.
- Restore leg: `git checkout HEAD -- FILE` → blob back to `f7938efe…`
(byte-identical),
`git diff HEAD` empty, `git status --porcelain` empty, anchor restored
1, injected 0.
- Restored leg: `--self-test` :: exit 0, 238 cases pass.

## Gates

Derived with `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack`
(no paths, three-dot against merge base `a90a9f267`) — **33 commands,
all run in the
foreground, every exit code captured before any pipe, all `exit 0`.**
Reconciled:

```
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran RAN_FILE
Run reconciliation — 33 derived, 33 run, 0 NOT-MEASURED, 0 UNRUN.
✓ 33 derived famil(ies) accounted for — a DERIVED zero — all 33 recorded an exit code and none of them is 3.
```

Including `node scripts/pm/check-governed-queue-guard.mjs --self-test`
:: exit 0 and
`pnpm check:nul-bytes` :: exit 0. `pnpm check:pm-governed-merges` ::
exit 0 was run too, though
the derivation does **not** place it for this path — it is a
`--self-test`-only checker-health
family here, so its green grades that checker's fixtures, not this diff.

Control characters: `grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]'` over
the changed file — no hits.

No changeset: `scripts/pm/**` ships in no package's `files[]`, so this
publishes nothing — the
`skip-changeset` label carries it.

## Acceptance notes

Two observations outside this card's one-file surface, noted and not
filed (this seat's write
budget for the round is the branch push, this PR, the label and one
report comment):

- `scripts/pm/check-governed-merges.mjs` :140 and
`scripts/check-required-contexts.mjs` :288
both still say "a human merge IS the review record" for a governed PR.
Neither is wrong —
it is one of the two terminals the charter names 「终局两条:人工直合即审核记录;授权批准 ⇒
席位落地。」 —
but neither mentions the second one. That is an omission rather than a
contradiction, so it is
not the class this card is. Issue objectstack-ai#18083 fences the first file off from
this PR by name.
- `.claude/hooks/guard-governed-enqueue.sh` :548 was checked and is
already ruling-C shaped
(its steps read draft → wait for the approval → "Then enqueue"), so it
needed nothing.

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

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

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

Fixes objectstack-ai#18237

H64 keyed on the author account. The rule it was built on had already
been superseded: write identity follows the CHANNEL, the REST actor is
`claude[bot]` **or** a user per session, and `user.login` records the
token rather than the seat — attribution is the `session_` id in the
text (`.claude/skills/pm-dispatch/references/rest-channel.md` :54,
`.claude/skills/pm-dispatch/SKILL.md` :98). So the row fired on
compliant REST writes from user-token sessions, told their authors the
write had gone through the MCP GitHub tool, and prescribed a re-post
through the REST proxy that lands under the same login and fires again.

This re-keys the row onto the attribution the landed rule actually
requires. One file, `scripts/pm/check-half-states.mjs` (non-governed PM
tooling; `check-governed-merges.mjs --test` on this file list reads `0
of 1 path(s) hit the register`).

## The measurement first — the discriminator the card doubted does not
exist

The PM's mechanism assumption P1 was that
`performed_via_github_app.slug` tells the MCP tool from the REST proxy.
Measured with GET-only reads on live rows, 2026-09-15:

| row | `user.login` | `user.type` | `performed_via_github_app` |
|---|---|---|---|
| comment 5673548571 on objectstack-ai#7623 — REST-proxy write, App token |
`claude[bot]` | `Bot` | `{ id: 1236702, slug: 'claude', name: 'Claude'
}` |
| comment 5673265919 on objectstack-ai#17076 — REST-proxy write, user token |
`os-warren` | `User` | `{ id: 1236702, slug: 'claude', name: 'Claude' }`
|
| comment 5673413139 on objectstack-ai#18237 — REST-proxy write, user token |
`os-warren` | `User` | `{ id: 1236702, slug: 'claude', name: 'Claude' }`
|

And the four specimens H64 was built from, all read the same slug:
objectstack-ai#18045 (`os-project-manager` / `User`), objectui#9404
(`os-project-manager` / `User`), PR objectstack-ai#18051 (`os-project-manager` /
`User`), comment 5652138683 on objectui#9370 (`os-tesla` / `User`); with
the clean control comment 5654046782 (`claude[bot]` / `Bot`).

**P1 is falsified.** The field names the App whose credential signed the
write, never the tool. The first two rows above are both REST-proxy
writes differing only in the token class the session was handed, and
they are byte-identical in this field. So no channel is inferred
anywhere in this family any more, and `artefactChannel`'s docblock now
says what it does read: an App credential versus none. Following P1's
own instruction for this outcome, the MCP half of the seat direction
(candidate (i)) collapses — it has no readable population — and
candidate (ii) is the whole firing row.

## What fires now

A seat/dev-signed artefact — a claim, a report, a contract review, a
filing header — carrying **no `session_` id anywhere in its text**. The
text asserts a seat or a dev wrote it; nothing says which session, and
the author field records only the token. Remedy: the owner gives the
artefact its id (an edit in place is enough). The account is explicitly
stated not to be the repair.

A structural consequence worth naming: two of the six signature forms
**are** session ids (the bare id in the head window, and the attribution
footer's `claude.ai/code/session_` URL), so a text matching either
carries its attribution by construction and can never fire. The firing
population is the other four forms with no id anywhere. The id test
reads the whole body while the signature's head window stays narrow —
pinned as a case.

## The exposure is not relaxed — it moved to where it costs nothing

The addendum stands: a suspended user account 404s everything it
authored, measured on this board. Nothing here relaxes it. It is now
reported as an **informational count plus a login roster** inside the
H64 summary clause, on every run, with no remedy named and no row filed.

That is P5's first option, chosen for the reason P5 gives: no act
available to a user-token session moves its content to the App, because
the token class is handed to a session at start rather than chosen at
write time. A row naming no remedy re-files every sweep at full weight
against an anchor that trimmed 423 of 441 rows. The clause states the
same fact for free, unconditionally, and is strictly **more** visible
than the old rows were — those competed for the cap and were trimmed; a
clause is not.

## Board measurement, before and after (live sweep, GET only)

| reading | before (`ecf91cb4`) | after (`c509364b`) |
|---|---|---|
| texts read / signed | 1697 / 873 | 1703 / 875 |
| finding population | 671 user-authored | 116 naming no session id |
| judged (on/after the pin) | 70 | 26 |
| rows filed (cap 10) | 10 | 10 |
| census behind the pin | 601, back to 2026-08-05T19:32:57Z | 90, back
to 2026-08-08T13:49:21Z |
| informational exposure | not reported | 671 texts, 17 logins, 3 PAT, 3
unread channel |

The two sweeps ran ~40 min apart, so the corpus moved slightly on its
own (1697 to 1703 texts).

**Reverse verification on the live board.** Every row the card named as
a false positive is gone: comments 5673315903 (objectstack-ai#17396), 5673859871
(objectstack-ai#17502), 5673413139 (objectstack-ai#18237), 5670954004 (objectstack-ai#13801) and 5671601791
(objectstack-ai#16695) each appear in 0 rows after, having appeared in 1 before.
objectstack-ai#18237's own two rows are gone.

**And the retired test was blind in the other direction too.** Of the 10
rows filed now, 7 are authored `claude[bot]` — for example comment
5674562475 on objectstack-ai#16166 and comment 5674534458 on objectstack-ai#18174, both
`os-dev-report` payloads whose only footer is the platform's bare one,
and card objectstack-ai#18167's `Filed by the ... seat` body. Every one of those is
exactly as unattributable as the ones the old row shouted about, and the
old row could not see any of them. Spot-checked by GET: none carries a
session id anywhere.

## Fixtures — one kept its role, three were re-classified

Each with its reason, as P4 asks:

| fixture | before | after | why |
|---|---|---|---|
| comment 5652138683 (objectui#9370, `os-tesla`) | fires | **fires** |
names its seat by ACCOUNT and carries no session id in 35 lines —
verified against the live comment. It is now the row's live positive
rather than one specimen of four. |
| objectstack-ai#18045 (`os-project-manager`) | fires | clean | its bare session id is
on line 1: attributed. Its lit control is its own silence — strike the
id and it leaves the population entirely, because `Filed and claimed by`
is not the filing header, so the id was its whole signature. Pinned. |
| objectui#9404 (`os-project-manager`) | fires | clean | same id, beside
a filing header that survives id removal — so **this** is the specimen
carrying the lit control (`BODY9404_NOID` fires). |
| PR objectstack-ai#18051 (`os-project-manager`) | fires | clean | its only id is in
the footer on line 45 of 45, and the id test reads the whole body. The
`/pulls` fire control is a filer-signed body with only the platform bare
footer. |
| comment 5654046782 (`claude[bot]`) | clean | clean | now clean for a
different reason: it carries a `Session:` line. The byte-identical
comment under a USER login is also clean, which is the inversion of the
old case. |

Two new fixtures were added, `restBot64` and `restUser64`, carrying the
2026-09-15 fields above, so the indistinguishability is pinned offline
and the channel inference cannot be re-derived from the reader's name.

## Ablation (one-off proof, not a landed test)

Predicted direction: removing the session-id gate from `h64SpeaksAbout`
should turn the attributed-side silences RED, and should not abort the
suite.

- HEAD blob `ac93c610dc39c93444e72605ab45fafb827c6b32`; mutated blob
`35505df98ecef337f80d1ebccc514257a9ff4897` (distinct, so the mutation
reached disk).
- On-disk proof before reading any result: the anchor line count went 1
to 0 and the injected marker count went 0 to 1.
- Ablated run: `8 of 4113 case(s) failed`, exit 1 — and the 8 are
exactly the attributed-side silences (objectstack-ai#18045, objectui#9404, PR objectstack-ai#18051,
the cleared claim, comment 5654046782 under both logins, and the two
structural cases). No abort.
- Restored with `git checkout HEAD --`, under a `trap ... EXIT INT TERM`
with an absolute repo root: `git status --porcelain` empty, `git diff
HEAD` empty, restored blob `ac93c610dc39c93444e72605ab45fafb827c6b32`
equal to HEAD's, injected marker count 0.

## Deviations from the suggested route, stated rather than buried

1. **"The `claude[bot]` fixtures stay clean" does not survive the
re-keying, by design.** A `claude[bot]`-authored claim with no session
id now fires, and a case pins it. The account was the retired test;
keeping the App side categorically clean would keep half the retired
premise alive, and the live board shows 7 of 10 rows are that shape.
2. **`USER_AUTHORED_WRITE_SINCE` was renamed
`UNATTRIBUTED_WRITE_SINCE`** and `h64UserAuthoredSeatContent` became
`h64UnattributedSeatContent`. Both names encoded the superseded premise,
and both are file-local (grepped: no reference anywhere else in either
repo tree). The pin's instant is unchanged.
3. **The summary window anchor changed** from `User-authored seat
content (H64): ` to `Unattributed seat content (H64): `; the window key
`h64SeatSigned` is unchanged, and `summaryClause`'s uniqueness cases
still pass.
4. `artefactChannel` was kept rather than deleted — with corrected
semantics, and used by the exposure clause's PAT/unread split. Deleting
it would have removed the only place the measurement is enforceable.

## Self-test and gates

- `pnpm check:pm-half-states`: **4075 cases before, 4113 after**, all
passing. (The 4075 baseline was read by running the `ecf91cb4` copy of
the file; 7 of its cases fail when it is run outside the tree because
they read sibling scripts by path, which is a harness artefact, not a
baseline failure — in the tree it is green.)
- Gate roster derived with `node scripts/pm/dispatch-gates.mjs
--commands --repo objectstack-ai/objectstack`, no paths (change set: 1
path, three-dot vs merge base `ecf91cb44`): 41 commands. 40 ran in the
foreground with exits captured by redirect — **all exit 0**. The first
pass of 14 read exit 3 `PREREQUISITE NOT MET` in a fresh worktree with
no `node_modules`; after `pnpm install` all 14 re-ran green, and no
exit-3 reading is reported here as a measurement.
- `pnpm check:pm-dispatch-gates` was run detached per its own header (it
exceeds the foreground cap), and waited on rather than polled.
- `skip-changeset`: `scripts/pm/**` publishes nothing — no package's
`files[]` ships it.

## Acceptance notes

Out-of-scope observations, noted and not filed:

- The four-form firing population makes every `os-dev-report` comment a
candidate, because the report JSON carries `branch` but no session id
and the platform appends only a bare footer. That is the row working as
the landed rule specifies, not a defect — but if the maintainer would
rather the report template carry the id, that is a change to
`.claude/agents/os-dev.md`, a different (governed-adjacent) surface and
a different card. Successor: the next `domain:skills` card touching the
dev report template. Noted, not filed.
- `H64_ROW_CAP` still binds at 10 with 26 judged findings, so 16
clearable rows are unfiled per sweep. The cap is correct as designed
(the trim eats the tail) and the clause states the full count.
Successor: whoever next revisits `renderMarkdown`'s ordering, which is
the real constraint. Noted, not filed.

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

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

3 participants