Repository navigation
Commit 88920d1
governed: a fork PR is a proposal, never a delivery — the FORK predicate beside PATH and SIZE, plus the charter's external-contributions section (#19526)
Fixes #19470
Clause-②: no
## The ruling this lands
Maintainer, batch #207 item 1, letter 是, verbatim and untranslated:
「fork PR 的处理同意你的意见。」 — recorded by the director on the card (comment
5754996112) as: **a fork PR is a proposal, not a delivery. No agent seat
flips it ready, enqueues it, arms auto-merge on it, or approves it —
ever.** It enters through the card door (card first; the human decides
the problem, not the code; the seat adopts the diff and never lands the
fork PR; a fork's CI is never approved to run by a seat, zero check runs
read NOT MEASURED, never green), with the minimal mechanism: a second
predicate beside the governed one in
`scripts/pm/check-governed-merges.mjs`, one sentence in AGENTS.md, one
short 〈外部贡献〉 section in the charter, the NOT-MEASURED wording. No new
label, no new sweep; the supply-chain clause is deferred until the first
such fork PR appears.
The live specimen, PR #19342 (head `jinyitao123/objectstack`,
`author_association: FIRST_TIME_CONTRIBUTOR`, 0 check runs on `5fa7b6d`,
0 labels), is read here as evidence only. It stays untouched by this PR:
its disposal is the director's and triage's act in the same stroke, and
this branch writes nothing on it.
## What changed (five files, all on the claim's surface)
| file | before → after | net | what |
|:--|:--|--:|:--|
| `scripts/pm/check-governed-merges.mjs` | 6146 → 6226 lines | **+80**
(98 added, 18 replaced; budget ≤ +80, self-tests included) | the FORK
predicate beside PATH and SIZE; the check-run reading; a new self-test
battery beside the SIZE cases |
| `AGENTS.md` | 1109 → 1109 | **0** (ceiling 1109, headroom 0) | one
sentence in Prime Directive #14's first paragraph, paid by one retired
restatement |
| `.claude/skills/pm-dispatch/SKILL.md` | 815 → 816 | **+1** (ceiling
819, headroom 3) | one rule line in 〈入队与落地〉 |
| `.claude/skills/pm-dispatch/references/core-rules.md` | 151 → 151 |
**0** | the landing-rule line rewritten in place to carry the twin |
| `.claude/skills/pm-dispatch/references/external-contributions.md` |
new, 16 lines | +16 (budget ≤ 20; widest line 120 B) | the ruled 〈外部贡献〉
section, four points + the deferred supply-chain line |
### 1. `check-governed-merges.mjs` — the FORK limb
- `forkVerdict(pull)` (pure, exported, beside `sizeVerdict`): reads
`head.repo.full_name` and `base.repo.full_name` off the PR object; NOT
MEASURED when there is no `base.repo` to compare against; a fork when
the head repo differs — **or is `null`** (the fork was deleted): a
deleted fork is still a fork, fail closed.
- `testVerdict(paths, { size, fork, checks })` carries `fork` and
`checks`; `humanMerge` fires on any of the three limbs;
`landsByHumanMerge` reads the fork limb too, so a fork exits on the
EXISTING GOVERNED code (3) through the Tier H terminal — every caller
that already routes 3 to the human hand routes a fork there without
learning a new code. `applyGeneratedExceptions` keeps the fork limb
across a generated-artifact lift (a lift moves a PATH, never the head
repo).
- `renderForkLines(fork, checks)` (pure, exported): the fork sentence
under the verdict — a PROPOSAL, never a delivery, whatever its paths; no
AI seat flips it ready, enqueues it, arms auto-merge on it or approves
it; a seat's review is required INPUT, never the permission; the owning
seat adopts the diff onto an internal branch (`Co-authored-by:` the
contributor) and lands THAT; requests to the contributor go only as
review comments on the fork PR, which is closed with thanks and the
landing link. When the PATH limb is clear the head line reads `paths:
none on the register — the HEAD REPO decides this PR:` (the SIZE limb's
shape); on a governed diff the sentence rides under the tier block, Tier
S included (a fork head on a `.claude/**` path is still adopted, never
landed).
- `--pr N` reads the head's check-run count off `GET
/repos/{slug}/commits/{sha}/check-runs` — one more GET, on the channel
already chosen for the PR read. `total_count: 0` prints `check runs on
head SHA: 0 — NOT MEASURED, never green` in the register the size limb
already uses; a count that did not read prints `NOT READ (reason) — read
as NOT MEASURED, never green`; a non-zero count prints nothing.
- `--test` and `--branch` cannot see a head repo and now say so on
stdout (`head repo: NOT MEASURED — this verdict cannot tell a fork PR
from an internal one; --pr N reads it`), the same discipline as the size
limb: a verdict silent about a leg it did not run reports a clearance it
never measured. The `--branch` / `--test` byte-identity pin still holds
(both print the same line).
- `--json` carries `fork` and `checks` beside `size`.
- Self-test: battery `⭐ the FORK predicate: head repo ≠ base repo is a
proposal, never a delivery` (6 cases, floor 6, roster 30 → 31), placed
between the SIZE battery and the audit-half battery, never at the tail:
fork head on ordinary paths ⇒ H route with the fork sentence and no
`ordinary queue landing applies`; `head.repo === null` ⇒ the same;
same-repo head on ordinary paths ⇒ unchanged (NOT governed, no fork
line); same-repo head on `AGENTS.md` / `.claude/agents/os-dev.md` ⇒ Tier
H / Tier S word for word, while a fork head on the Tier S path still
prints the fork sentence; no PR object ⇒ NOT MEASURED said; zero check
runs ⇒ the NOT-MEASURED line, 43 ⇒ absent, unread ⇒ `NOT READ`; and
`fetchPullFiles` against an injected fetch reads the head repo off its
own GET and the count off the head sha. Header docblock gains a "The
FORK predicate" section; the summary line names the battery.
### 2. `AGENTS.md` — one sentence, net 0
Prime Directive #14, first paragraph, :264 before (102 B):
```text
paragraph names fewer surfaces than the register — or more. When it reds, name the surface here.
```
:264–:266 after (113 B / 116 B / 106 B):
```text
paragraph names fewer surfaces than the register — or more. When it reds, name the surface here. A fork PR
(head repo ≠ base repo) is a proposal, never a delivery, whatever its paths: no AI seat readies, queues, arms
auto-merge on or approves it; the owning seat adopts the diff onto an internal branch and lands that.
```
"whatever its paths" is the sentence's shape saying the fork rule is a
predicate on the PR, not a surface: the paragraph still names exactly
the register's six surfaces and no `**`-shaped span was added, so `pnpm
check:pm-governed-prose` stays green (quoted below).
**Payment (+2 lines bought by one retired restatement, ⛔ not by the
ceiling, not by re-wrap):** the three paragraphs of Prime Directive #14
are greedy-packed already (878 / 1789 / 918 characters joined ⇒ 8 / 16 /
8 lines at the 120-column cap, exactly what they occupy), so re-wrap
buys nothing. Retired: the Skills section's line (pre-edit :800, 117 B,
plus its trailing blank line):
```text
⛔ **Both roots are governed surfaces** — `skills/` is Tier H, `.claude/skills/` Tier S (**Prime Directive #14**).
```
It restated the register and its tiers, whose home is the directive it
pointed at. Surviving home and grep proof on this head: `grep -n
'skills/\*\*' AGENTS.md` → :6 (the CLAUDE-conflict clause), :260 (the
register names `.claude/**` and `skills/**`), :273 (Tier H names
`skills/**`); `grep -n 'Tier S' AGENTS.md` → :258, :276 (Tier S = all of
`.claude/**`), :292; `grep -n 'Both roots' AGENTS.md` → no hits. The
Skills section keeps its two-root catalog lines; only the restatement
left.
### 3. `SKILL.md` — one rule line in 〈入队与落地〉 (+1, 108 B)
Inserted at :643, directly under the section's pointer line (`- 细则见
references/landing-operations.md,落地窗口查阅。`):
```text
- fork PR = 提案,席位永不放行;采纳 diff 内部落地,见 `references/external-contributions.md`。
```
Placement (four axes): **业务需求** — the acts the rule forbids (ready / 入队
/ auto-merge / 批准) are the acts this section governs, so a seat reading
its landing checklist meets the fork rule where it would otherwise act;
**长远合理性** — one rule line, one pointer, the detail in a references file,
the same shape as the section's first line; **防 AI 犯错** — the line sits
before the 条款② gate and the PASS ⇒ ready ⇒ auto-merge line, so the fork
stop is read before the enqueue reflex; **创业阶段不扩散** — ≤ +1, no new
section. ⛔ Not in the 分诊 / 定级 region (:174–:175, :362, :368–:372, :379
are #19494's) and not on PR #19488's lines (the `area:*` rows,
:119–:134, :215, :243, :259, :294, :310, :371–:372, :470–:471). PR
#19513 edits :632–:633 and :647 / :653 of the same section; :643 sits
between its two hunks with unchanged context on both sides, so the later
lander merges `origin/main` once with no conflict expected.
### 4. `references/core-rules.md` — the twin, paid in place (151 → 151)
:122 before (118 B) → after (112 B):
```text
- 验收后取路径面,命中规则层即分叉 ⛔ 不翻正式不入队;Tier S 席内达档复核 PASS 后入队。
- 验收后取路径面:规则层 ⛔ 不翻正式不入队,Tier S 达档 PASS 后入队;fork PR 永不放行。
```
Every landing-rule line in the core subset sat at 111–118 B of the 120 B
cap, so the twin could only ride a compression: 「命中…即分叉」 and 「席内…复核」 are
folded to 「规则层」 and 「达档」 (the SKILL.md lines they summarise are
unchanged), and the freed bytes carry the rule. No clause dropped. PR
#19513 and PR #19488 touch :55–:69, :102, :106–:113 of this file, not
:122.
### 5. `references/external-contributions.md` — new (16 lines, every
line ≤ 120 B)
Frame as its siblings (title, a 「见 SKILL.md 〈…〉」 pointer line, one rule
per line). Bytes per line: 45 · 0 · 117 · 117 · 0 · 113 · 77 · 97 · 92 ·
102 · 110 · 113 · 106 · 113 · 116 · 120. Content, the four points of the
ruling in substance: ① card first — triage's fire scans open PRs whose
head repo ≠ base repo, files a card from the PR body graded like any
card, posts the ONE fixed comment (`this repository works card-first;
filed as #N; this PR stays a draft until the card is graded`), a fork PR
with no card does not exist on the board; ② the human decides the
problem, not the code — a plain reproducible defect routes to a seat,
floor items (security / permission boundary, contract, feature) go to
the decision box as a business question; ③ the seat adopts the diff,
never lands the fork PR — internal branch, cherry-pick or
re-implementation, `Co-authored-by:` the contributor, full gate
derivation + at-tier review + the queue as for internal work, requests
to the contributor only as review comments on the fork PR, closed with
thanks and the landing link when the internal PR lands; ④ a fork's CI is
never approved to run by a seat, zero check runs read NOT MEASURED,
never green; the machine face (`check-governed-merges.mjs --pr N` routes
head repo ≠ base repo through the H(人合) exit, `head.repo` null the
same); and the deferred supply-chain line.
`check-skill-line-ratchet.mjs` does **not** require a CEILINGS row for a
new references file (it enumerates only `references/lanes/` for
uncovered files; the file is simply outside the ratchet's map, as
`references/instrument-discipline.md` on PR #19513 is). No row was
added: the ratchet script is outside this card's file surface. Noted
under Acceptance notes for the seat.
## The two `--pr 19342` readings
Before (origin/main 8fc6a5f), `node scripts/pm/check-governed-merges.mjs
--pr 19342 :: exit 0`:
```text
governed-surface predicate: 0 of 4 path(s) hit the register (6 surfaces, repo-agnostic).
✅ NOT governed — ordinary queue landing applies to a PR with exactly this file list.
Derived from GOVERNED_SURFACES, not recalled. Re-run on the FINAL file list: the register
has grown several times in two days, and a reading taken earlier in the session is recall.
size: 56 changed line(s) (+48 / -8) ≤ 5000 — under the human-merge threshold (generated files included in the count).
```
After (this head 0a50588), `node scripts/pm/check-governed-merges.mjs
--pr 19342 :: exit 3`:
```text
governed-surface predicate: 0 of 4 path(s) hit the register (6 surfaces, repo-agnostic).
paths: none on the register — the HEAD REPO decides this PR:
⛔ FORK PR — head jinyitao123/objectstack ≠ base objectstack-ai/objectstack: a PROPOSAL, never a delivery, whatever its paths.
The Tier H terminal, with its own reason: no AI seat flips it ready, enqueues it, arms auto-merge on it or approves it —
ever; a seat's review is required INPUT, never the permission. The owning seat adopts the diff onto an internal branch
(`Co-authored-by:` the contributor) and lands THAT through the ordinary gates; requests to the contributor go only as
review comments here, and this PR closes with thanks and the landing link.
1 parent ecf56e7 commit 88920d1
5 files changed
Lines changed: 119 additions & 22 deletions
File tree
- .claude/skills/pm-dispatch
- references
- scripts/pm
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
640 | 640 | | |
641 | 641 | | |
642 | 642 | | |
| 643 | + | |
643 | 644 | | |
644 | 645 | | |
645 | 646 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
119 | 119 | | |
120 | 120 | | |
121 | 121 | | |
122 | | - | |
| 122 | + | |
123 | 123 | | |
124 | 124 | | |
125 | 125 | | |
| |||
Lines changed: 16 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
261 | 261 | | |
262 | 262 | | |
263 | 263 | | |
264 | | - | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
265 | 267 | | |
266 | 268 | | |
267 | 269 | | |
| |||
797 | 799 | | |
798 | 800 | | |
799 | 801 | | |
800 | | - | |
801 | | - | |
802 | 802 | | |
803 | 803 | | |
804 | 804 | | |
| |||
0 commit comments