Skip to content

A fork PR is inside no line of defence: #9495 makes the pre-merge defence SEAT DISCIPLINE, which does not bind an outside contributor, and the governed predicate is path-only — measured live on #19342 #19470

Description

@os-litant

Path: 合并前防线 — 所护已发布面 = 本仓全部已发布包(fork PR 只能作为提案进入,裁决批 #207 项 1「是」) | none | none
Class (b) — a declared defence that does not hold for a population it is relied on to cover, with the declaring text quoted and a live specimen on the board.

Dedupe words: fork PR, FIRST_TIME_CONTRIBUTOR, seat discipline, check runs zero, #9495.

维护者速读

这个仓是公开的,已经有 16 个 fork,任何人都能往回开 PR —— 这是 GitHub 的设计,不需要我们授权。问题是:我们所有的合并前防线都假设「动手的是我们自己的席位」。外部贡献者不读 AGENTS.md,不受红线约束。而受管面判定只看改了哪些文件,不看谁改的

现在板上就有一个:#19342,一个首次外部贡献者在 fork 上放宽 OAuth 的 TLS 要求(让明文 HTTP 也能跑 OAuth),一条 CI 都没跑过,没有任何标签,而且它改的包不在受管面上 —— 按今天的机器判定,它可以走普通队列。

要裁的一句话:fork PR 是否一律不得由任何 agent 席位放行(翻 ready / 入队 / 挂 auto-merge / 批准),只由人合? 是 / 否。

The declared defence, quoted

.github/CODEOWNERS header, read from origin/main 2026-09-20T23:35Z, recording the 2026-08-18 ruling (#9495):

a human merge IS the review record for a governed surface, the pre-merge line of defense is seat discipline (no AI seat flips ready / enqueues / arms auto-merge on a governed PR — AGENTS.md Prime Directive #14), and detection moved to the report-only post-merge audit

⭐ That sentence is load-bearing, and it is true only of PRs written under this repository's own discipline. An outside contributor on a fork is not a seat. They do not read AGENTS.md, are not bound by any 红线, and owe no claim, no clause-② declaration and no measurement record.

The gap has three independent halves

① No rule exists. Searched AGENTS.md plus the whole pm-dispatch skill on origin/main, 2026-09-20T23:35Z, for fork, third.party, 第三方, 外部贡献, FIRST_TIME:

probe hits what they were
the five terms above 4 all different senses — 「新增运行时第三方依赖」, 「报 fork」(a divergence), 「Third-party exceptions」(an env-var allowlist)
governed in AGENTS.md 20 lit control — the corpus is being read
ZZZQNotAPolicy 0 dark control

⇒ the zero is a real absence, not a missed search. Nothing anywhere says what to do with a PR from outside.

② The governed predicate is path-only. check-governed-merges.mjs decides by which paths a diff touches. It has no notion of who wrote it or where the head lives. A fork PR that touches no registered path is, to that instrument, an ordinary PR.

③ A fork PR's CI does not run by default, and a PR that never ran looks exactly like a PR that passed — both render as "no red". GitHub withholds secrets from fork workflows until a maintainer clicks Approve and run workflows.

The live specimen — PR #19342, read 2026-09-21T01:26Z

field reading
title feat(auth): allow explicit intranet HTTP OAuth
author jinyitao123, author_association: **FIRST_TIME_CONTRIBUTOR**
head fork jinyitao123/objectstack, branch codex/intranet-http-oauth
state open, draft, untouched since 2026-09-20T12:28Z
size 4 files, +48 −8
check runs on its head 0 — lit control: PR #19373's head on the same instrument reads 43
labels none — triage has not touched it
governed predicate does not fire: it touches only packages/plugins/plugin-auth/** and one changeset

What it does: adds an escape hatch OS_ALLOW_INSECURE_OAUTH_HTTP=true to isOAuthEligibleBaseUrl, letting non-loopback plain HTTP carry MCP OAuth — i.e. OAuth exchanges and bearer usage over cleartext. The docblock it replaces cited OAuth 2.1 §1.5: TLS required, loopback exempt, fail-closed.

⚠️ Stated fairly: the diff is careful. Default off; only the exact string true opens it (TRUE, 1, yes, " true " all refused, with tests); non-http/https still refused; the changeset is honest about what it does; the mount-time warning is updated to match. This card is not a claim that the change is malicious or wrong — it is a claim that nothing in our machinery would have stopped it, or anything else, from reaching the queue.

Per SKILL.md:393 the change is on the 人工地板 twice over: 「安全/权限边界」 and 「门禁削弱」. That floor binds our seats. It binds no one on a fork.

Shape of a fix — suggestions, ⛔ not a ruling

  1. A fork predicate beside the governed one. head.repo.full_name !== base.repo.full_name ⇒ no agent seat flips ready, enqueues, arms auto-merge or approves. Same red line as H(人合), different reason: governed is 「the content needs a human」, fork is 「the author is outside the discipline」. Best placed as a second predicate inside check-governed-merges.mjs so it exits through the existing H route — ⛔ not as a new label and a new sweep.
  2. Zero check runs reads as NOT MEASURED, never as green. Same shape as the PREREQUISITE NOT MET … ⛔ This is NOT a pass: nothing was measured wording two gates in this repo already use.
  3. Supply-chain paths get their own bar. A fork PR touching package.json dependencies, a lockfile, .github/workflows/**, scripts/** or any install hook can execute code in CI. feat(auth): allow explicit intranet HTTP OAuth #19342 touches none of these, but the rule should exist before one does.

⭐ And one thing that should be ruled OUT, not in

The obvious reflex is 「let an agent review it first」. An at-tier contract review of a fork PR is worth doing — an outside author will not write this repository's measurement prose, so the diff's real intent needs deriving. But an agent verdict must never be the thing that opens the gate.

A review agent reads the PR body, the commit messages, the code comments and the test names. On a fork PR all of those are attacker-controlled text. Someone wanting to get through can write instructions aimed at the reviewing agent in any of them. That is categorically different from reviewing our own dev's output, which at least came from inside the same discipline.

agent review = required input, ⛔ never the permission. The permission stays with a human. Recorded here so a later round does not "improve" the rule by letting a PASS substitute for the merge.

Scope note

⛔ This card does not dispose of #19342 itself. That PR lands in packages/plugins/plugin-authdomain:services, it carries no labels, and routing a bare card is the triage seat's act, ⛔ not an execution seat's. It is named here as the specimen and as a live item triage has not yet reached.

Origin: raised by the maintainer in this seat's session on 2026-09-20 after reading #19342; every reading above taken first-hand from origin/main and the REST API. Filed-by: session_01LvwGppdonww4zGLWZo5rho (domain:spec execution seat 1) — a governance finding for the skills lane; ⛔ not graded, prioritised or dispatched by this seat, and ⛔ no rule text was edited.

os-decision-facets
① 项目长远合理性:A 以一条封闭谓词(head 仓 ≠ base 仓)把「席位纪律不约束外人」这个洞收进既有的 H(人合)出口,不增标签、不增巡检;B 让合并前防线对 fork 人口失效。
② 实际业务拉动:今天板上 1 张活标本 #19342(首次外部贡献者、OAuth 放宽到明文 HTTP、head 上 0 条 check run、0 标签),分诊未触;仓公开、forks 16。
③ 防 AI 犯错:A 是响亮拒绝(谓词命中即交人;0 check run 读作 NOT MEASURED,⛔ 不读作绿);B 让 agent 读到的 PR 正文、提交信息、注释、测试名全部成为攻击面,出错静默。
④ 创业阶段不扩散:A = 一个谓词 + 一句章程;C 再加供应链一句;⛔ 不荐新标签、新巡检、新 workflow。
Prior rulings read: fork,外部贡献,first_time_contributor,outside contributor → 1 hits; ADR-0087 D1(义项不同:「freeze-contract fork」是发布分支,不是 fork PR);thread: none
推荐 A(回退 C;⛔ 不荐 B)。自检:只看①选 A;②③④ 是否翻转:否。
置信缺口:pull_request_target 在 4 个 workflow 文件出现、未逐一核实是否有 workflow 以写权限对 fork PR 运行;MCP 合并路线对 fork head 未测;fork PR 历史只看了最新 100 张(已合 0 张)。


Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions