Repository navigation
Commit cb8756b
feat(fleet-write): pr_create carries labels; os-dev declares skip-changeset in the create's stroke (#24175)
Fixes #24157
Clause-②: no
## What this PR does
Two parts, one PR, the card's route 1 + 3 (fold `skip-changeset` into
the `pr_create` stroke; rewrite the 「席位代做」 line). Option 4 (relaxing
`Check Changeset`) is a gate change and is not here.
### 1. The relay op — `pr_create` carries `labels`
- `scripts/pm/fleet-write/ops.mjs`: `pr_create` gains an optional
`labels` list. It becomes a later request of the SAME action, `POST
/repos/REPO/issues/NUMBER/labels` with `fromAnswer: 'number'` and
`labelled: [...a.labels]` — the exact shape the `assignees` leg has
(`assigned`). The row still spends `pull-requests` with `alsoSpends:
['issues']`, so the token minted for the stroke already covers the
labels POST; `PERMISSIONS` is unchanged and the workflow's mint step
needed no edit.
- `scripts/pm/fleet-write/validate.mjs`: the typed check is the one
`labels_add` already uses — `FIELDS.labels` (array of 1..20 strings,
each ≤ 50 bytes, no duplicates) is applied to any op whose key list
admits `labels`, so adding `labels` to the row's `optional` is the whole
validator change; the self-test gains the `pr_create` labels cases
beside the assignees ones, the minimal-shape ledger names `labels`, and
the 「exactly ONE descriptor reads an earlier answer」 pin becomes
「exactly TWO — assignees then labels」, with a new pin that every such
leg is an additive POST to `/assignees` or `/labels`, never the
whole-set replace.
- `scripts/pm/fleet-write/execute.mjs` — **the one place that had to
learn the leg** (the dispatch order assumed none): ① `requestLanded`
judges a `labelled` descriptor's 2xx against the names sent — the labels
endpoint answers the pull's whole label set, so a name it lacks is a
FAILED action naming it, never a quiet partial; more than sent (the Auto
Label workflow's `documentation` / `size/*`) is a landing. ② `resultOf`
prints the labels as the platform lists them on that row. ③ `fromAnswer`
legs are now filled from the action's FIRST answer (the create's), not
the PREVIOUS request's: with both legs present the previous answer of
the labels leg would have been the assignee POST's issue object, which
happens to carry a number but is not the create. The self-test pins the
label leg landing on the number the create answered while the assignee
answer carries a different one.
- `scripts/pm/fleet-write/dispatch.mjs` and
`.github/workflows/fleet-write.yml` need nothing: dispatch's self-test
is green unchanged (222 cases), and the workflow only runs `validate.mjs
--from-env --github-output` then `execute.mjs`.
### 2. The dev contract — `.claude/agents/os-dev.md`, 404 → 404 lines
(ceiling 404, headroom 0)
Four lines rewritten in place, no line added or removed. Each line's old
text and where every dropped clause survives:
| line | before | after | dropped clause → survival |
|---|---|---|---|
| 61 | 写预算四笔:`git push`、`pr_create`(draft,含
assignee)、`label-write`(仅标签)、`os-dev-report` 评论 | 写预算三笔:`git
push`、`pr_create`(draft,含 assignee 与 labels)、`os-dev-report` 评论 |
`label-write`(仅标签) as a budgeted stroke → it is no longer a default
stroke; the late-label case is named on line 303 |
| 303 | objectstack:标签是真实机制,打标签是你的默认步骤,PR 一开出就打;派发词可收窄或禁写 |
objectstack:`skip-changeset` 以 `pr_create` 的 `labels` 同笔申报;后发现的标签才走
`label-write.mjs` | 「打标签是你的默认步骤,PR 一开出就打」→ strengthened: declared in the
create's stroke, not after; 「标签是真实机制」→ the premise lines 306–307 rest on
(a gate reads the label); 「派发词可收窄或禁写」→ line 304 verbatim: 范围 = 派发词点名的标签
+ …;禁写或交集为空 ⇒ 零写并报告 |
| 305 | 写恒经 `scripts/pm/label-write.mjs`:取现集、加法 POST、回读比 union、缺者重挂一次并报告
| 该腿的回读是中继回报的行:答案须列出每个送出的标签,缺者该笔 FAILED、不发后续动作 | the four steps are what
`label-write.mjs` itself performs and prints on every invocation (its
header: 「the four-step label write, as ONE spelling … Every invocation
performs all four … re-added ONCE and REPORTED」), so the contract line
was a mirror of the tool header, which AGENTS.md makes the authority;
「写恒经 scripts/pm/」→ line 55 (GitHub 写一律经 `scripts/pm/` 写工具) and line 303
names the tool for the late-label case |
| 309 | 分类器拒外部写 ⇒ 停手,`deviations` 记命令与拒因,席位代做;⛔ 不换路重发(curl/MCP/手工) |
分类器拒外部写 ⇒ 停手,`deviations` 记命令与拒因,席位只转维护者定夺;⛔ 不换路重发 | 「席位代做」→ replaced by
design (the card's finding: the seat's harness forbids substituting for
a refused action); 「(curl/MCP/手工)」→ line 308 「不走 MCP」, AGENTS.md 「⛔
Never a bare `curl` / `fetch` / `gh api -X POST…` write」, and 「不换路重发」
covers any manual route |
Lines 61 / 303 / 305 / 309 measure 104 / 118 / 119 / 117 bytes against
the 120-byte line budget. No re-wrap, no merge, no ceiling raise.
## Mechanism assumptions, measured
- **(a) confirmed** at the base `bd4276a91` (origin/main had advanced
from the order's `697301491` when the worktree was cut): `pr_create` had
`optional: ['body', 'assignees']` only; the filer's 「the relay's
`pr_create` op takes `labels`」 was false, so option 1 needed the op leg.
- **(b) stands, and nothing here can prove more:** the classifier
refusal was measured once (PR #24153 refused, PR #24139 allowed,
identical `label-write.mjs` command). This PR cannot show the classifier
will accept a `pr_create` carrying `labels`; the measured fact is that
the `pr_create` stroke has not been refused since the assignee precedent
#22467. This PR's own `skip-changeset` had to go the OLD way
(`label-write.mjs`) because a PR cannot use the leg it introduces.
- **(c) confirmed:** `pr-automation.yml` writes `size/*` and the path
labels by additive POST (the `Auto Label` and `Check PR Size` jobs), so
the two never overwrite each other. `Check Changeset` reads the label
live (a re-read plus a settling read, because the frozen `opened`
payload cannot see a label applied after creation; its own comment
measures label latency at 10–45 s after creation). The leg lands inside
the relay's own stroke, seconds after the create, so it falls inside
that settling window — and the `labeled` event re-triggers the job in
any case.
- **(d) run** — see Verification. **(e) read** the current lines (the
card's quotation of line 61 and the labels block matched the tree at the
base).
- **Falsified:** 「read `execute.mjs` … to confirm nothing else must
learn the new leg」 — it did: without `labelled` the executor would have
passed a 200 whose answer lacks the label (ablation B, below, shows
exactly that quiet partial), and the fill source had to move from the
previous answer to the create's (ablation A).
## Verification (final commit `f9e63d6c3`)
Relay self-tests (rule ⑤ — the edited scripts' own suites; no `*.test.*`
file names them):
- `node scripts/pm/fleet-write/validate.mjs --self-test` → exit 0, 158
cases / 11 batteries (was 152).
- `node scripts/pm/fleet-write/execute.mjs --self-test` → exit 0, 128
cases / 16 batteries (was 120; the assignees-leg battery is re-declared
in `SELF_TEST_BATTERIES` as the assignees-and-labels battery, pin 11 →
19).
- `node scripts/pm/fleet-write/dispatch.mjs --self-test` → exit 0, 222
cases / 20 batteries (unchanged).
- `ops.mjs` has no `--self-test` of its own (its pins live in the
validate and execute suites).
Ablations — each via `node scripts/ablation-replace.mjs` (anchor hit ×1,
blob changed, command run, restore proven by blob == HEAD blob and `git
diff HEAD` empty; no `dist/` is involved, these are plain node scripts):
- **A** `execute.mjs`: `if (opener === null) opener = r;` → `opener =
r;` (back to previous-answer fill). Expected red; observed 2 of 128 red
— 「at the number the CREATE answered, not the assignee answer's」 and the
row-text case. Blob `f246498…` → `af59de9…` → restored `f246498…`.
- **B** `execute.mjs`: `const missing = req.labelled.filter(` → `const
missing = [].filter(`. Expected red; observed 1 of 128 red — 「a 200
whose answer lacks a name sent is a FAILED action … never a quiet
partial」 (the run passed with exit 0 and 3 writes under the mutation:
the quiet partial). Blob `f246498…` → `69e1572…` → restored `f246498…`.
- **C** `ops.mjs`: `optional: ['body', 'assignees', 'labels']` →
`['body', 'assignees']`. Expected red; observed 6 of 158 validator cases
red (the labels acceptance cases, the minimal-shape ledger, the
two-descriptor pin). Blob `6ad2cb6…` → `83deec1…` → restored `6ad2cb6…`.
Gates — derived at `f9e63d6c3` with `node scripts/pm/dispatch-gates.mjs
--commands --repo objectstack-ai/objectstack` (no paths; 48 families,
the path-named derivation at the base plus `check:pm-write-pace`, which
names `execute.mjs`). Every command run in the foreground with its exit
code captured before any pipe: all 48 exit 0. One of them, `pnpm
--filter @objectstack/lint run check:doc-formula-expressions`, first
answered exit 3 (PREREQUISITE NOT MET: no `@objectstack/formula` /
`@objectstack/lint` build in the fresh worktree — a non-measurement, not
counted); after `pnpm exec turbo run build --filter=@objectstack/formula
--filter=@objectstack/lint` behind `scripts/pm/os-verify-lock.sh`
(VERDICT command-exit 0) it re-ran to exit 0. Reconciled with `--ran`:
`node scripts/pm/dispatch-gates.mjs --ran ran.list --repo
objectstack-ai/objectstack` → exit 0, 「48 derived, 48 run, 0
NOT-MEASURED, 0 UNRUN」, every family carrying a recorded exit code. The
five families the order named (`check:pm-skill-ratchet`,
`check:pm-skill-id-lint`, `check:pm-governed-prose`,
`check:agent-model-declared`, `check:pm-settings-deny-roster`) are each
exit 0. Repo-wide scans (`pnpm lint`, `test:repo`) are CI's; the PR's
check-runs are the authority for every family CI runs.
## Acceptance notes
- `.claude/skills/pm-dispatch/references/review-checklist.md` line 22
still tells the seat 「dev 挂 `skip-changeset` 遭分类器拒 ⇒ 席位自核
tests/docs-only 即自挂」 — the seat-side twin of the line this PR rewrites,
outside this card's file surface. Reported in the dev report for the
seat; not changed here.
- `dispatch.mjs`'s seat-side read-back (`assigneesReading`) has no
labels twin; the relay row (`execute.mjs`) is the read-back line 305 now
names. Observation only, nothing filed.
- Governed surface, Tier S (`.claude/**`): this PR stays draft and
awaits a `## Contract review` record; `scripts/pm/**` and `.claude/**`
publish nothing → `skip-changeset`, applied the old way (reported in the
dev report with endpoint and status if refused).
## 维护者速读(草稿)
**改了什么**:中继的 `pr_create` 操作新增可选 `labels`,开 PR 的同一笔里顺手贴标签(和已有的 assignee
腿同一形状);执行器只认「答案里列出了每个送出的标签」才算落地,缺一个就报 FAILED 停下。`os-dev.md`
四行原地改写:写预算四笔变三笔;`skip-changeset` 在开 PR 那一笔申报,事后才发现的标签才用
`label-write.mjs`;分类器拒写时席位只转维护者定夺、不代做。行数 404 → 404,未抬上限。
**为什么改**:同一条 `label-write` 命令,一次放行一次被拒(PR #24139 / PR
#24153),被拒后原文让「席位代做」,而席位的 harness
把替子代理补做被拒动作视为洗权限,于是每次被拒都变成一次向您提问。把标签并入从未被拒的 `pr_create` 一笔,是 #22467 处理
assignee 时走过的同一条路。
**风险与代价(含回滚)**:不能证明分类器一定放行带 `labels` 的 `pr_create`,只能说该笔自 #22467
以来未被拒过。本 PR 自身的 `skip-changeset` 仍按旧路贴(新腿要合并后才可用)。回滚 = revert 本 PR 一个
commit;中继无状态,不影响已开 PR。
**席位意见**:(留空,席位定稿)
**你要做的**:本 PR 触及 `.claude/**`(Tier S),由席位做达档复核后入队落地,无需您点击;如希望顺手也改
`review-checklist.md` 第 22 行的席位侧措辞,请一句话说明,席位另立卡。
---
_Generated by [Claude
Code](https://claude.ai/code/session_012PxNHU3dfov2hbpg9ycZ8q)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent f1c0b57 commit cb8756b
5 files changed
Lines changed: 130 additions & 74 deletions
File tree
- .claude
- agents
- skills/pm-dispatch/references
- scripts/pm/fleet-write
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
58 | 58 | | |
59 | 59 | | |
60 | 60 | | |
61 | | - | |
| 61 | + | |
62 | 62 | | |
63 | 63 | | |
64 | 64 | | |
| |||
300 | 300 | | |
301 | 301 | | |
302 | 302 | | |
303 | | - | |
| 303 | + | |
304 | 304 | | |
305 | | - | |
| 305 | + | |
306 | 306 | | |
307 | 307 | | |
308 | 308 | | |
309 | | - | |
| 309 | + | |
310 | 310 | | |
311 | 311 | | |
312 | 312 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
22 | | - | |
23 | | - | |
| 22 | + | |
| 23 | + | |
24 | 24 | | |
25 | 25 | | |
26 | 26 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
95 | 95 | | |
96 | 96 | | |
97 | 97 | | |
98 | | - | |
| 98 | + | |
99 | 99 | | |
100 | | - | |
| 100 | + | |
101 | 101 | | |
102 | | - | |
103 | | - | |
104 | | - | |
105 | | - | |
106 | | - | |
107 | | - | |
108 | | - | |
109 | | - | |
110 | | - | |
111 | | - | |
112 | | - | |
113 | | - | |
114 | | - | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
115 | 122 | | |
116 | 123 | | |
117 | 124 | | |
| |||
246 | 253 | | |
247 | 254 | | |
248 | 255 | | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
249 | 264 | | |
250 | 265 | | |
251 | 266 | | |
| |||
257 | 272 | | |
258 | 273 | | |
259 | 274 | | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
260 | 280 | | |
261 | | - | |
262 | | - | |
263 | | - | |
264 | | - | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
265 | 285 | | |
266 | 286 | | |
267 | 287 | | |
| |||
350 | 370 | | |
351 | 371 | | |
352 | 372 | | |
353 | | - | |
| 373 | + | |
| 374 | + | |
354 | 375 | | |
355 | 376 | | |
356 | 377 | | |
| |||
555 | 576 | | |
556 | 577 | | |
557 | 578 | | |
558 | | - | |
| 579 | + | |
559 | 580 | | |
560 | | - | |
561 | | - | |
| 581 | + | |
| 582 | + | |
562 | 583 | | |
563 | 584 | | |
564 | 585 | | |
| |||
581 | 602 | | |
582 | 603 | | |
583 | 604 | | |
584 | | - | |
| 605 | + | |
585 | 606 | | |
586 | 607 | | |
587 | 608 | | |
| |||
624 | 645 | | |
625 | 646 | | |
626 | 647 | | |
627 | | - | |
| 648 | + | |
628 | 649 | | |
629 | 650 | | |
630 | 651 | | |
| |||
890 | 911 | | |
891 | 912 | | |
892 | 913 | | |
893 | | - | |
| 914 | + | |
894 | 915 | | |
895 | 916 | | |
896 | 917 | | |
| |||
916 | 937 | | |
917 | 938 | | |
918 | 939 | | |
| 940 | + | |
| 941 | + | |
| 942 | + | |
| 943 | + | |
| 944 | + | |
| 945 | + | |
| 946 | + | |
| 947 | + | |
| 948 | + | |
| 949 | + | |
| 950 | + | |
| 951 | + | |
| 952 | + | |
| 953 | + | |
| 954 | + | |
| 955 | + | |
| 956 | + | |
| 957 | + | |
| 958 | + | |
919 | 959 | | |
920 | 960 | | |
921 | 961 | | |
| |||
0 commit comments