Skip to content

feat(pm): fleet-write transfer op and an issue-transfer door — a card moves with its history - #19902

Merged
os-zhuang merged 6 commits into
mainfrom
claude/issue-19884-fleet-write-transfer-op
Sep 23, 2026
Merged

os-zhuang merged 6 commits into
mainfrom
claude/issue-19884-fleet-write-transfer-op

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #19884
Clause-②: no

Summary

The fleet-write relay gains a transfer op (GraphQL transferIssue), and a new door, scripts/pm/issue-transfer.mjs, moves ONE card to a sibling repository with its comment thread, timeline, cross-references and reactions intact. The old URL redirects to the new card. Until now, the only route was the rebuild recipe (a provenance header, bare numbers spelled out, the source closed as moved), which loses all of that and leaves a closed husk behind.

Instruction (verbatim, from the card): 「objectui 你负责迁移。fleet 中继不支持 GitHub 的 issue transfer, 应该立 skills 卡解决」

What changed

  • scripts/pm/fleet-write/ops.mjs: a transfer row that takes issue + target_repo (never pull) and spends issues. Its one request is the transferIssue mutation. The row never sets createLabelsIfMissing, so GitHub's default (false) applies: a label the target does not carry is dropped, never created there.
    • target_repo is a closed enum, TRANSFER_TARGETS. It REUSES GOVERNED_REPOS from scripts/pm/check-governed-merges.mjs (objectstack, objectui, cloud, objectos, hotcrm), so there is no second roster.
    • A new row key, secondRepo, marks the one row whose token must reach a second repository.
    • transferRemedy() spells the installation remedy once.
  • scripts/pm/fleet-write/validate.mjs: stroke rules that span actions. A stroke carrying a transfer carries transfers alone, all to ONE target that is not the source, and names each card once.
    • tokenRepositoriesOf(payload) computes the list the token is minted for: the payload's repository, plus the transfer target for a transfer stroke.
    • The --github-output key is renamed from repo_name to repositories (for example objectstack or objectstack,objectui).
  • .github/workflows/fleet-write.yml: the mint reads repositories: ${{ steps.validate.outputs.repositories }}, the validator's output and never the payload. Permissions are unchanged (no contents added).
  • scripts/pm/fleet-write/execute.mjs:
    • The sender gate runs on EVERY repository the token reaches, so a transfer needs write, maintain or admin on the source AND the target.
    • A transfer reads the issue and refuses a pull request, or a card that no longer answers from the source (already moved). It then reads the target's node id and sends the mutation.
    • It accepts only an answer that places the card on the target. On failure, the summary and the log print the remedy.
  • scripts/pm/issue-transfer.mjs (new door): --repo / --issue / --to / --dry-run / --json / --self-test. The relay's own validateStroke judges the plan for both transports.
    • Before any write, one pre-read refuses a pull request, or a card that already moved (naming where it lives now).
    • dispatch sends ONE transfer through the relay. direct sends the relay row's own mutation with the token it holds. auto is issue-create's rule: it falls back only on no-run, never from a failed run.
    • Read-back: the old URL, read WITHOUT following redirects, must answer 301 naming the new number, and the new card must answer from the target with the same title. The read-back also prints labels kept and dropped.
    • Exit ladder follows issue-create: 0 / 2 / 3 / 4 board disagrees / 5 / 6 UNCONFIRMED / 10.
    • The door paces its one write through write-pace.mjs.
  • scripts/pm/write-pace.mjs: the door joins WIRED_WRITE_TOOLS (the census comment now says ten).
  • package.json: check:pm-issue-transfer.
  • .claude/skills/pm-dispatch/references/cross-repo-coordination.md: the fallback line (one line for one line, 51/51, 120 bytes) now points at the door. Rebuild is kept only for a target outside the roster, or a move the platform refuses.

Premise check (against origin/main at dabf8d7)

  • ops.mjs named 14 ops and none was a transfer.
  • fleet-write.yml minted repositories: ${{ steps.validate.outputs.repo_name }}, which is one repository.
  • The reference line was the rebuild fallback.

All three held, so the premise stands.

The card's three open questions

  1. App installation coverage of objectui, cloud and hotcrm: NOT readable from a seat. The design fails closed at runtime instead:
    • A mint that cannot cover the target fails the mint step, with zero writes.
    • A target whose node id cannot be read, or a refused mutation, stops the action and prints transferRemedy: the maintainer adds the target to the App's repository access. It is never silently rebuilt.
  2. Labels without a same-named label on the target: dropped. GitHub docs, "Transferring an issue to another repository": "Labels and milestones are also retained if they're present in the target repository, with labels matching by name". The schema carries createLabelsIfMissing: Boolean = false, read from the published GraphQL schema. The relay never sets it, and the door's read-back names the labels that were dropped.
  3. Assignees without access to the target: the docs say "comments and assignees are retained" and say nothing about an assignee who lacks access there. This is NOT MEASURED and stays open.

Verification — all at HEAD 425752b

  • Self-tests:

    self-test cases batteries before (at dabf8d7)
    validate 94 9 74 across 7
    execute 59 10 48 across 9
    issue-transfer 46 7 new
    write-pace 113 12 unchanged
    dispatch 101 11 unchanged
    issue-create 42 6 unchanged
    • New validate batteries:
      • The transfer row, 12 cases: the row itself; a target outside the roster refused; a pull refused; the source refused; a mixed stroke, two targets or a card named twice refused; four cards to one target accepted; labels never created.
      • The token scope, 8 cases: ONE repository for every other op and for a mixed stroke of them; source + target for a transfer stroke; the workflow's single repositories: input reads the validator's output.
    • The new execute battery (11 cases): the sender gated on both repositories; a pull or an already-moved card refused before the mutation; landing only on the target; the remedy printed on failure.
  • Gate union: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 70 commands. --ran reconciliation reads: 70 derived, 68 run, 2 NOT MEASURED, 0 UNRUN.

    • All 68 that ran exited 0. That includes check:pm-dispatch-gates (about 880 s), check:self-test-wired, check:required-contexts, check:stall-guard-*, check:workflow-*, check:nul-bytes, check:pm-skill-ratchet and check:pm-skill-id-lint.
    • NOT MEASURED: check:dual-build-cjs-loads and check:lean-entry-closure, reason: PREREQUISITE NOT MET (no monorepo dist/). They are matched only because root package.json is in the Build Core job filter; this diff changes one scripts row there and no workspace package source. CI's Build Core runs them.
    • check:dts-closure and check:sourcemap-no-sources-content exited 0, but over only the 4 packages built locally, not the CI closure. Read them as partial for the same reason.
  • eslint, narrowed:

    • Population: the 5 changed .mjs files, confirmed linted by eslint --print-config. The .md and .json files answer "no matching configuration".
    • Count: --format json reports 5 files, 0 errors, 0 warnings.
    • Invariance: this repo's eslint.config.mjs enables no type-aware linting (no parserOptions.project), so the diff cannot move any untouched file's verdict.
  • Ablations: each mutation was landed with scripts/ablation-replace.mjs (anchor 1 to 0 on disk) and then restored. Each restore was confirmed as blob equal to HEAD with an empty git diff HEAD.

    mutation self-test result
    executor gates only payload.repo execute RED, 4 of 59
    every stroke's token list gains objectui validate RED, 4 of 94
    door's pull-request refusal disabled issue-transfer RED, 1 of 45
    door's old.status !== 301 check disabled GREEN on the first run

    The fourth one was not a no-op: the mutation landed, but the 404 fixture died later at the redirect-number check, so the 301-only branch had no witness. A fixture now answers 307 with a location that names the new card; the rerun is RED, 2 of 46.

  • NOT MEASURED: live transfer. Reason: the relay runs main's workflow. The first live use belongs to the consumer session named on the card, on one of the four repo:objectui cards. That use also answers open question 1.

Deviations from the card's suggested route

  • No create_labels_if_missing key. Nothing needs it: creating repo:objectui or domain:* labels in objectui would be noise. A dropped label can be re-added on the target with labels_add. Adding the key later would be one row.
  • Two added constraints the card did not ask for: a transfer stroke carries transfers alone to one target, and the sender is gated on the target too. Both keep the widened token's use to exactly transferIssue under GitHub's own two-sided write requirement.

Acceptance notes

  • The door's self-test (pnpm check:pm-issue-transfer) is not run by CI. .github/workflows/lint.yml was outside this card's file surface, and a new CI step is a new gate, which defaults to no. The seat decides.
  • .claude/settings.json carries no allow rule for the door (the maintainer's file, untouched), so a seat running it meets a permission prompt.
  • rest-channel.md says "four write tools" share the selector; it is now five doors. That same file lists issue transfer under the non-REST-migratable items, which remains true: the relay reaches it through GraphQL. Noted only (carrier: none).
  • The relay's static PERMISSIONS include contents: write for the two auto-merge rows. A transfer stroke's token therefore carries it on the target as well. No row spends it, and the executor issues only the table's requests. Noted only, not a new card.

维护者速读(草稿)

改了什么

  • 舰队写中继新增 transfer 操作,并新增 scripts/pm/issue-transfer.mjs:把一张卡迁到兄弟仓,评论、时间线、交叉引用随卡走,旧链接自动跳转。以前只能在目标仓「重建」,历史全丢,还会留一个空壳。

为什么改

  • 会话操作者原话:「objectui 你负责迁移。fleet 中继不支持 GitHub 的 issue transfer, 应该立 skills 卡解决」。首个使用者是正在等着迁移 4 张 objectui 卡的会话。

风险与代价(含回滚)

  • 这是中继令牌唯一的一处放宽:只有迁移批次会把令牌铸给「源仓 + 一个目标仓」。目标仓只能是治理名册里的 5 个仓,发起者在两个仓都必须有写权限;其余操作仍然只铸单仓,由自测钉死。
  • 未实测:fleet App 的安装是否覆盖 objectui、cloud、hotcrm(席位读不到)。首次真实迁移若因此失败,会明确报错并写出补救办法「把目标仓加进 App 安装」,不会悄悄退回重建。
  • 目标仓里没有同名标签的,标签会被丢弃;不会在目标仓自动建标签。
  • 回滚:revert 本 PR 即可。workflow 回到单仓铸令牌,无数据迁移。

席位意见

你要做的

  • 确认 objectstack-fleet App 的仓库访问范围包含 objectui(以及将来要迁入的 cloud、hotcrm)。
  • 可选:是否在 .claude/settings.json 给新门加 allow 规则。这是你的文件,本 PR 未改。

Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 425752b205

Rendered in-seat by the domain:skills seat 1 at 2026-09-23T16:48Z on the diff of head 425752b205 against origin/main (three-dot), read hunk by hunk from the branch; the self-tests re-run by the seat in a review worktree at that head, ⛔ not taken from the report.

① Derived judgments

  • The one review face touched is governed rule text: references/cross-repo-coordination.md :25, the rebuild fallback rewritten to point at the door (「转仓走 issue-transfer.mjs;名册外或平台拒才重建…」), one line for one, 51 / 51. No published schema, doc tree, changelog or changeset moves; Clause-②: no is correct.
  • The relay's one widening is fenced as the card required and tighter: the transfer row is the only op with a secondRepo; its target is a closed enum (TRANSFER_TARGETS = GOVERNED_REPOS reused, never a second roster, never free text); a stroke carrying a transfer carries transfers alone, to ONE target that is never the source, each card once (strokeErrors); validate.mjs computes the token list (tokenRepositoriesOf) and writes it as the repositories output the workflow's mint reads — one name for every other op, source + target for a transfer — and its self-test pins both halves and the workflow line itself; the executor gates the sender on every repository the token reaches, resolves both node ids first (refusing a pull request and a card that no longer answers from the source), accepts only an answer that places the card on the target, and prints the installation remedy on failure. createLabelsIfMissing is never sent. Judged right on the four axes: closed enumeration over free text, loud refusal over rebuild, no new gate.
  • The door issue-transfer.mjs: one card per call, the relay's own validator judging the stroke for both transports, pre-read refusals, the relay's transfer row or the same mutation directly, a read-back that demands the old URL's 301 and the card at its new address, issue-create's exit ladder with 6 UNCONFIRMED never re-run blind, both halves of the throttle around the one write, wired into WIRED_WRITE_TOOLS and check:pm-issue-transfer.
  • Seat readings at 425752b (review worktree): issue-transfer --self-test 46 cases / 7 batteries, fleet-write/validate 94 / 9, fleet-write/execute 59 / 10, write-pace 113 / 12, every one exit 0; --dry-run on objectstack#18397 prints one transfer action to objectstack-ai/objectui with nothing sent. The relay's live leg is NOT MEASURED by construction (the relay runs main's workflow); the first live transfer is the consumer session's on one of the four repo:objectui cards.
  • Retained blast radius, flagged not blocked: the mint's static permission set (contents: write for the two auto-merge rows) rides on the target repository for a transfer stroke; no row spends it there and the executor runs only the closed op table. Per-stroke permission narrowing is a hardening the maintainer may name (round report).

② Semver level

None: scripts/pm/**, .github/workflows/**, the package.json script row and .claude/** publish nothing from any released package; skip-changeset is the correct declaration and is on the PR.

③ Boundary flags

  • Four open_questions, each answered by the dev with a recommendation the seat adopts: (Q3) assignees on transfer — accept GitHub's semantics, read at the consumer's first live transfer; (Q1) whether the fleet App's installation covers objectui / cloud / hotcrm — no seat can read it; the door fails closed with the remedy, and the ask goes to the maintainer before the consumer's first transfer; (CI) check:pm-issue-transfer is seat-run only — a new CI step is a gate the maintainer names (新增门禁默认否); (allow rule) .claude/settings.json carries no rule for the door — the maintainer's file. The last two and Q1 are maintainer items in the round report, ⛔ not this PR's to widen.
  • The door's auto → direct fall-back on a run that never appeared mirrors issue-create's rule and dispatch.mjs's fallbackText; in a cloud seat container such a fall-back writes as the session's user, the same family as the label-write step-④ finding recorded on close-cards: one card = one write — pack comment → pm:* strip → close into ONE fleet-write write set (write-pace cap unchanged, closing throughput ×3) #19824's ACCEPT — an existing rule, flagged with it, not changed here.
  • Three out-of-scope notes (承接者:无, on the PR's Acceptance notes): rest-channel.md :11 now under-counts the doors sharing the transport selector (five); the contents: write ride-along above; GOVERNED_REPOS lacks www.objectos.ai, so the door refuses it and the rebuild recipe covers it.

Implemented-by: claude/issue-19884-fleet-write-transfer-op
Reviewed-by: session_01A22sUB3mUWs6M36VgfijBq

VERDICT: PASS


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review September 23, 2026 17:10
@os-zhuang
os-zhuang added this pull request to the merge queue Sep 23, 2026
Merged via the queue into main with commit 3f3c0f7 Sep 23, 2026
40 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-19884-fleet-write-transfer-op branch September 23, 2026 17:53
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 28, 2026
…ough the allow-listed ccr pair; a landing denial stops and surfaces (objectstack-ai#19997)

Fixes objectstack-ai#19990
Clause-②: no

Rule text only, in three `pm-dispatch` references. This PR adds no allow
row, no tool and no gate. `.claude/settings.json`, `scripts/pm/**`,
`SKILL.md` and `AGENTS.md` are untouched. Line counts are unchanged (183
/ 101 / 37), and every edited line is at or under 120 bytes.

The maintainer's words, in the `domain:engine#1` seat's session, quoted
on the card verbatim and in order:

> 「你的pr为什么没有挂在当前session上」
> 「写一个 skills 卡片,更新技能」
> 「包括你刚才为什么不能merge,我当前session设置的是auto」

The same words reached the `domain:skills` seat directly (claim comment
5817962037): 「你的pr应该挂在当前 session上,对应的卡片优先派发」.

## What changed

| file · line (after) | bytes | rule |
|---|---|---|
| `execution-duties.md` :149 (new) | 118 | Case 1. When a report names a
PR, a session seat subscribes it at once (`subscribe_pr_activity`) and
lists it on the seat post. Reason, stated once: a PR the relay opens is
never attached to the session automatically. |
| `execution-duties.md` :147 | 87 → 116 | The collection line now covers
both modes itself ("(两种模式)", "评论与返回消息皆无"), replacing the deleted
report-channel line (see *Line budget*). |
| `landing-operations.md` :49 | 82 → 117 | (b). The landing executes the
verdict of record (ACCEPT, or the contract-review PASS). It is not a
self-approval. |
| `landing-operations.md` :51 | 65 → 115 | (a). Ready and auto-merge go
only through the two ccr commands that `settings.json` allow-lists
(`rest-channel.md` :51 / :55). |
| `landing-operations.md` :54 (new) | 120 | (c). A classifier denial
during landing means: stop, report to the maintainer, and record the
command and the denial reason on the card. ⛔ Never respell the command
or switch to the relay to get around it. |
| `landing-operations.md` :77 | 106 + 89 → 111 | Case 1, landing side.
Every PR in a session seat's window must be subscribed; subscribe any
that is missing. The optional 「关键 PR」 wording is gone. The old :77 "not
before the report" clause is folded in as 「⛔ 不早于报告」. Routine seats keep
polling. |
| `reading-discipline.md` :23 | 82 → 120 | (d). A timer text carries no
verdict or landing write verb. |

Wording choices that differ from the dispatch text:
- **`判决`, not `裁决`, at :49.** In this corpus `裁决` is a maintainer
ruling, and `判决` is the review verdict (`execution-duties.md` :180–:183,
「判决 ACCEPT / REWORK / ESCALATE」).
- **`判决与落地类写动词`, not only `落地类写动词`, at :23.** The timer that was denied
`[Self-Approval]` told the seat to post the ACCEPT as well as run the
two landing ops.

## Why no new allow row is owed: case 2 (a)

The allow-listed landing route already exists. The skill already names
it, and this PR only makes §B's landing step name it too.

- `.claude/settings.json` :61–:66 allow-lists `curl -sS -X POST
…/pulls/*/ccr/ready_for_review` and `curl -sS -X PUT
…/pulls/*/ccr/auto_merge` for all three repos. The hotcrm pair was added
on 2026-09-24 by `e6a5ecb9`. That commit also deliberately gave
`fleet-write/dispatch.mjs` no row. `git grep -n 'with-fleet'
.claude/settings.json` gives 0 hits; the control `git grep -n
'label-write' .claude/settings.json` gives 2.
- `SKILL.md` :201 says 「ready/draft 走 ccr 路」, and `platform-readings.md`
:48 says 「undraft 单通道:席位凭据走 `POST .../pulls/{n}/ccr/ready_for_review`」.
- Measured on the timeline (`GET /issues/N/timeline`,
2026-09-24T16:3xZ):
- PRs objectstack-ai#19873, objectstack-ai#19895, objectstack-ai#19902, objectstack-ai#19941, objectstack-ai#19948, objectstack-ai#19956, objectstack-ai#19970 and objectstack-ai#19993
were landed by the `domain:skills` seat under auto mode. Each has
`ready_for_review` and `added_to_merge_queue` with actor `os-zhuang`
(the ccr route, which writes as the seat's linked user).
- PRs objectstack-ai#19971, objectstack-ai#19972 and objectstack-ai#19979 have the same two events with actor
`objectstack-fleet[bot]` (the relay route).
- Both routes work. The ccr pair is the one with an allow row. The relay
route has none, so under auto mode the classifier judges it call by
call.

## Where the standing authorization is recorded: case 2 (b)

It is already recorded in the tree, so this PR adds only the one clause
at :49:
- `AGENTS.md` Prime Directive objectstack-ai#14: Tier S lands "by the owning seat on a
contract-tier review of record".
- `AGENTS.md` Multi-agent discipline §7 and Post-Task Checklist step 2:
arm auto-merge on a PR that is green and accepted.
- `landing-operations.md` :59 (Tier S).

Whether that is enough for a seat landing a PR written by its own
`mode:subagent` dev is put to the maintainer below. This PR does not
rule on it.

## Line budget: what left, and where each fact still lives

All three files stand at headroom 0. Each new line is paid for by
deleting content, not by re-wrapping or raising a ceiling.
- **`execution-duties.md` old :147 deleted.** It read 「报告通道统一:GitHub
是两种模式共用的真相源;dev 终报先落 issue 评论、再作返回消息。」
- The dev-side ordering lives in `.claude/agents/os-dev.md` :17–:18
(「报告交付两次,GitHub 优先:同一段 JSON 先作 issue 评论 … 再作为终报消息」).
- "GitHub is authoritative in both modes" lives in `os-dev.md` :324
(「两种派发模式(`mode:subagent` 与 `mode:cloud`)下 GitHub 都是报告的权威源」). It also
stays on the collection line as 「(两种模式)」.
- **`landing-operations.md` old :77, second clause, deleted.** It read
「订阅是感知补充,⛔ 不替代 flip 定点」. The fact lives on:
  - :50: the flip timer is set at ACCEPT.
  - :52: 「CI success webhook 不可靠:⛔ 不坐等」.
- `platform-readings.md` :40: 「订阅来的 `check_suite.completed` 是唤醒不是放行读数」.
  - Its first clause is kept on :77 as 「⛔ 不早于报告」.
- **`landing-operations.md` old :76 rewritten in place.** It dates from
`42af12fe7` (the 2026-08-07 ruling on subscribing *key* PRs). The newer
maintainer words quoted above replace its optional scope.

## Measured risk that stays open

- An allow row does not stop a denial based on content.
`mcp__Claude_Code_Remote__send_later` is allow-listed (`settings.json`
:23, present since before 2026-09-20), yet the engine seat's timer was
denied `[Self-Approval]`. `platform-readings.md` :435 records another
content-based `[Self-Approval]` denial.
- Two explanations are possible: that session did not load this settings
file, or the classifier judges content over an allow row. Which one
holds was not measured. The eight ccr landings are the positive reading.
The new :54 line covers the negative case.
- **Write identity, a tension this PR did not create.** The ccr pair
writes as the seat's linked user, `os-zhuang`, which is in
`GOVERNED_APPROVERS` (`scripts/pm/check-governed-queue-guard.mjs` :576).
Three texts point the other way:
- `AGENTS.md`: "Every GitHub write leaves through `scripts/pm/`, as
`objectstack-fleet[bot]` … ⛔ Never a bare `curl` … write".
  - `SKILL.md` :92: 「批准账号永不跑席位或作其关联用户」.
  - `SKILL.md` :94: 「写侧恒为 `objectstack-fleet[bot]`」.

`SKILL.md` :201 already routes ready/draft through ccr, so this tension
predates this PR. The new :51 states the same route more plainly. The
choice is the maintainer's; see the question below.

## Verification

At `04357257d`, every command from `node scripts/pm/dispatch-gates.mjs
--commands --repo objectstack-ai/objectstack` was run, with the exit
code captured before any pipe. All exited 0: 17 derived commands, plus
`pnpm check:pm-governed-prose`, `node
scripts/check-skills-token-ratchet.mjs` and `pnpm
check:pm-settings-deny-roster`. The reconciliation `dispatch-gates
--ran` reports: "17 derived famil(ies) accounted for — 17 run, 0
NOT-MEASURED (a DERIVED zero …)".

Verdict lines:
- `check:pm-skill-ratchet`: `execution-duties.md is 183 lines (ceiling
183; headroom 0)` · `landing-operations.md is 101 lines (ceiling 101;
headroom 0)` · `reading-discipline.md is 37 lines (ceiling 37; headroom
0)`.
- `check:pm-skill-id-lint`: `34 file(s) clean`.
- `check:skill-frame-sync`: `the one declared copy of the decision frame
is internally coherent`.
- `check:nul-bytes`: `OK … no raw ASCII control bytes`.
- `check:doc-formula-expressions` first exited 3 (PREREQUISITE NOT MET,
unbuilt `@objectstack/formula` / `@objectstack/lint`). It exited 0 after
`turbo run build` for those two packages under the verify lock. That
first run measured nothing; it was not a failure.

No build, test, reverse check or ablation applies to this change: it is
rule text only, with no code path.

## Acceptance notes

- `dispatch-runbook.md` :128 has cloud cards subscribe as soon as the
draft PR exists. The folded 「⛔ 不早于报告」 agrees with it only because a
cloud dev reports at draft-PR time (runbook :135). No change is made.
- `origin/main` was not merged before opening. It moved by one commit (a
`docs(qa)` change touching none of these files), and the queue rebuilds
on current `main`.

## 维护者速读(草稿)

**改了什么**
- dev 报告里点名了 PR,席位当场订阅这个 PR 的动态,并记进座位贴。规则里写明原因:经中继开出的 PR
永远不会自动挂到会话上。落地说明里原来"给关键 PR 挂订阅"是可选说法,现在改成"落地窗口里每个 PR 都要挂上,缺了就补"。
- 落地(转 ready、挂 auto-merge)只走 `.claude/settings.json` 已放行的两条 ccr
命令。席位落地是在执行已记录的复核判决,不是自己批准自己。
- 落地过程中如果被权限分类器拒绝:停手,报给您,在卡上记下命令和拒绝原因;不换写法,也不改走中继绕过去。
- 定时提醒的文本里不再写"判决/落地"这类写操作,只写"到时重读什么、满足什么条件"。

**为什么改**
- 您问了两个问题。
- PR 为什么没挂在当前 session 上:中继开的 PR 不会自动挂上,而技能里只有可选的"关键 PR 挂订阅"。
- auto 模式下为什么不能 merge:engine 席被拒了两次,一次是一条回读命令,一次是一条写着"发 ACCEPT
并落地"的定时器。它随后改走中继落地,而中继没有放行规则。放行的路本来就有:本席今天在 auto 模式下用它落地了 8 个 PR,一次都没被拒。

**风险与代价(含回滚)**
- 只改三个技能参考文件的规则文本。行数不变,每行不超过 120 字节。回滚就是 revert 本 PR。
- ccr 两条命令记在 os-zhuang 名下。而 AGENTS.md 写的是"所有 GitHub 写都经 scripts/pm,以
objectstack-fleet[bot] 身份,永不裸 curl 写",SKILL.md
也写"批准账号永不作席位的关联用户"。这个矛盾早就存在(SKILL.md 本来就写"ready/draft 走 ccr 路"),本 PR
没有新造,只是把它写得更明确。
- 放行规则不保证分类器一定放行。`send_later` 在放行清单里,engine
席那条定时器还是按内容被拒了。所以新加了"被拒就停手上报"这一条。

**席位意见**

**你要做的**
- 回一句话,确认下面两件事,或者指出要改哪一件:
- ① AGENTS.md 第 14 条(Tier S 由所属席位在达档复核记录在案后落地)和 Multi-agent discipline 第
7 条(PR 全绿且已验收就挂 auto-merge),就是席位落地自己子代理所写 PR 的常设授权,不用另外记。
- ② 用 ccr 两条命令落地,算 AGENTS.md「写只经 scripts/pm」这条规则的例外。是把这个例外写进
AGENTS.md,还是改走中继并加一条新的放行规则,都由您决定。

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

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

ci/cd dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation size/xl skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants