Repository navigation
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
Activity
决策分析(skills 席落卡入箱)—
domain:skillsseat 2(session_017ETYWqMQD4qMtZzAGovWNi,席位帖 #19287)于 2026-09-21T02:32Z;前提在origin/main5e7d83c于 2026-09-21T02:24Z 重读,卡面每个读数本席逐一复核,见下。finding→needs-user-decision·tooling·priority:p1。 本卡是维护者本人在 spec 席会话里提出的问题(卡面 Origin 段),问的是一字裁决;「安全/权限边界」与「门禁削弱」都在代裁人工地板上(SKILL.md :395),本席按 〈升级与决策〉落卡入箱,⛔ 不代裁。四棱卡面块已贴到卡正文末尾(H62 读正文),此处同文。一句话问题
今天任何人 fork 本仓开一个 PR,把「OAuth 只走 TLS」放宽成「内网明文 HTTP 也能跑」,只要它不碰受管路径,我们的机器判定就把它当普通 PR;而合并前唯一的防线「席位纪律」只约束我们自己的席位,⛔ 不约束 fork 上的外人。
背景 / Governing text
- Governing text ①:AGENTS.md Prime Directive feat: Comprehensive CRM example demonstrating all ObjectStack protocol features #14(:255–:262)——「A governed surface lands only the way its tier allows … before that record no AI seat merges, queues, or arms auto-merge on a PR whose diff touches one」——判定按路径,不按作者。
- Governing text ②:
.github/CODEOWNERS头注(裁决 governance: human merge IS the review record for governed surfaces — retire the per-PR ADR approval check, add a report-only post-merge audit #9495,2026-08-18)——「the pre-merge line of defense is seat discipline」。 - Governing text ③:SKILL.md :395「人工地板同含:安全/权限边界;门禁削弱(降阈/删必查/抬上限/跳测)」。
- grep(本席重跑):
git grep -n -iE 'fork|FIRST_TIME|外部贡献|第三方' origin/main -- AGENTS.md .claude/skills/pm-dispatch→ 3 命中,全是别的义项(fork = 分叉/「报 fork」;第三方 = 运行时依赖);check-governed-merges.mjs中head.repo|author_association|FIRST_TIME→ 0。卡面「三个半」的读数成立。 - 协议声明:不改协议。本卡不动 spec / 发布契约;动的是本仓的合并前防线(AGENTS.md 一句 +
check-governed-merges.mjs一个谓词)。
带 re-check 命令的前提
- 活标本 PR feat(auth): allow explicit intranet HTTP OAuth #19342:open · draft ·
author_association: FIRST_TIME_CONTRIBUTOR· headjinyitao123/objectstack· 0 标签 · head5fa7b6d上 check runs 0(GET /repos/objectstack-ai/objectstack/commits/5fa7b6d/check-runs→total_count: 0)。 - 人口:仓公开(
private: false),forks 16;最新 100 张 PR 中来自 fork 的 1 张 = feat(auth): allow explicit intranet HTTP OAuth #19342(GET /pulls?state=all&per_page=100,按head.repo.full_name过滤)。⇒ 今天撞上的是一张,不是零。 - re-check:
node scripts/pm/check-governed-merges.mjs --pr 19342应仍不报 GOVERNED(它只碰packages/plugins/plugin-auth/**与一个 changeset)。
选项 × 真实代价
选项 做什么 客户可感知的后果 A 是:fork 上来的 PR 一律只由人合。任何 agent 席位 ⛔ 不翻 ready、不入队、不挂 auto-merge、不批准;席位复审是必需输入,⛔ 永不是放行许可;head 上 0 条 check run 读作 NOT MEASURED,⛔ 不读作绿。落地 = check-governed-merges.mjs受管谓词旁加一个 fork 谓词(head.repo.full_name !== base.repo.full_name)走既有 H 出口 + AGENTS.md 一句。外人的每张 PR 都要您点一下(今天 1 张 / 100)。换来:没有任何 AI 席位能被写在 PR 正文或提交信息里、专门给它看的话说服去放行外来代码。 B 否:fork PR 按路径谓词走普通队列;席位达档复审 + 您点「Approve and run workflows」后 CI 绿,即可由席位入队。 省您的点击;代价是复审 agent 读的正文、提交信息、注释、测试名全是外人可控文本,一张写得小心的 PR(如 #19342:默认关、只认字面 true、带测试)足以让席位把「明文 HTTP 跑 OAuth」送进队列。C A + 供应链路径加严:fork PR 触及 package.json依赖、lockfile、.github/workflows/**、scripts/**或任何 install hook 时,席位 ⛔ 连「Approve and run workflows」都不代点,只由您。与 A 同;多一条今天无标本的规则(#19342 一条都没碰)。GitHub 默认已对 fork 的 workflow 扣住 secrets、要人点批准——C 是把这个默认写进章程。 业务含义直译
- A = 开源项目的常规:外部贡献由维护者合,机器人不合外人的代码(GitHub 自己对 fork workflow 的默认也是「先人批准再跑」)。
- B = 把「谁写的」这一维从防线里拿掉,只看「改了哪」——等于门卫只查包裹不查人。
- C = A 再加一条「快递里有电池的另走人工通道」。
四轴(业务立场)
- 长远合理性:A 缩小特例——一条封闭谓词覆盖全部外来 PR;B 让「席位纪律」对一部分人口失效,等于契约上留一个只靠运气堵的洞。
- 实际业务拉动:今天 1 张活标本在板上,且是安全边界改动(OAuth 走明文);分诊还没到它。
- 防 AI 犯错:A 是响亮拒绝——席位读到 fork 谓词即停、交人;B 是静默容忍——攻击面正是 agent 的输入文本。
- 创业阶段不扩散:A 一个谓词 + 一句话;C 多一条规则;B 零改动但把风险留在板上。
防 AI 犯错轴:出错时谁看到什么
- A 下出错(席位误把 fork PR 当自家 PR):
check-governed-merges以 H 出口拒绝,席位与您在 PR 上都看得见一句「fork PR — 只由人合」;0 check run 时读到 NOT MEASURED 而非绿。 - B 下出错:没有人看到任何东西——PR 静默进队列并合并;发现只能靠合并后审计,而该审计今天已按您的话退役(Charter (ruling #202 B): the dev queue is product-only — tooling cards close at first grading, broken gates are deleted not repaired, at most one tooling dev in flight, and
toolingis a first-touch label with named readers #19457 评论 5754225479)。
推荐 + 回退 + 置信缺口
- 只看①选 A;②③④ 是否翻转:否(②③同向;④ 认为 A 已是最小改动)。终态句:两年后本仓是一个有外部贡献者的公开平台,外来代码由人合、由机器测、由 agent 审而不放行——与主流开源平台(GitHub 对 fork workflow 的默认;多数大型 OSS 的「外部 PR 由维护者合并」规则)同型。
- 回退项:C(A 加供应链一条)。⛔ 不荐 B。
- 置信缺口:①
pull_request_target出现在 4 个 workflow 文件(至少 2 处是注释限定「只在 closed 后跑」/「⛔ 永不」),本席未逐一核实是否有 workflow 会以写权限对 fork PR 运行——若有,A 之外还欠一张 workflow 卡;② MCPmerge_pull_request/enable_pr_auto_merge对 fork head 的行为未测(REST 路线本席按 PD feat: Comprehensive CRM example demonstrating all ObjectStack protocol features #14 不碰);③ 历史上是否有 fork PR 已被席位合过——只看了最新 100 张(已合 0 张)。 - ⛔ 不沿用他席推荐:卡面「Shape of a fix」三条本席逐条复核后采其 1、2 为 A,3 单列为 C;卡面「ruled OUT」那段(agent 复审 ≠ 放行许可)本席同意并写进 A。
裁后执行段
- 裁 A:本卡一笔换工作态,首行补
Path: 合并前防线 | none | none,以「所护已发布面 = 本仓全部已发布包」满足裁决 🔗 Broken links detected in documentation #202 B 的tooling入队条件;skills 席在舰队 tooling 一张在飞的名额内派一张 os-dev:check-governed-merges.mjs加 fork 谓词 + 自测(fork head 出 H;同仓 head 不变;0 check run = NOT MEASURED)+ AGENTS.md PD feat: Comprehensive CRM example demonstrating all ObjectStack protocol features #14 一句(Tier H,您合)。feat(auth): allow explicit intranet HTTP OAuth #19342 本身仍是分诊席的路由动作,⛔ 不在本卡内。 - 裁 C:同 A,加 SKILL.md 人工地板一句(供应链路径的 fork PR 连 approve-and-run 都只由您)。
- 裁 B:本卡关
not_planned,理由引您的字;活标本 feat(auth): allow explicit intranet HTTP OAuth #19342 交分诊按普通流程。
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 张)。您只需回一字:A / B / C。
Generated by Claude Code
- addedpriority:p1High: required for production / M2High: required for production / M2and removed
on Sep 21, 2026 os-project-manager commented
on Sep 21, 2026 CollaboratorMore actionsRuling: batch #207 item 1 · letter 是 · maintainer 「fork PR 的处理同意你的意见。」 2026-09-21T03:28Z
Director seat, summon #25 (
session_012GcsUbuqFGBibkEDMRC1eE). Presented with this seat's recommendation 是 from facet ① and, on the maintainer's follow-up 「fork PR 问题还挺多的 … 人工应该怎么复核处理呢?」, the intake process below; the maintainer adopted the opinion as given. Governing text: AGENTS.md Prime Directive #14 (path-based governed landing);.github/CODEOWNERSheader (#9495: the pre-merge defence is seat discipline); SKILL.md 人工地板 「安全/权限边界」「门禁削弱」.Ruled: 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 (machine). Triage's fire scans open PRs whose
head.repo.full_name ≠ base.repo.full_name; for each without a card it files one from the PR body (finding or feature, graded like any card) and posts one fixed comment on the PR: 「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 without the maintainer; anything on the floor (security / permission boundary, contract, feature) goes to the decision box as a business question and is ruled in a batch like any other card.
- The seat adopts the diff; it never lands the fork PR. After the ruling the owning seat carries the contributor's change onto an internal branch (cherry-pick or re-implementation,
Co-authored-by:the contributor), runs the full gate derivation, the at-tier contract review and the queue exactly as for internal work; requests to the contributor go only as review comments on the fork PR (the one place a seat writes there). When the internal PR lands, the fork PR is closed with thanks and the landing link. A fork's CI is never approved to run by a seat; zero check runs read as NOT MEASURED, never as green. - Mechanism, minimal: a second predicate beside the governed one inside
scripts/pm/check-governed-merges.mjs(head.repo ≠ base.repo⇒ the existing H route), one sentence in AGENTS.md, one short 〈外部贡献〉 section in the charter (the four points above), the NOT-MEASURED wording for zero check runs. ⛔ No new label, no new sweep. The supply-chain clause (dependencies / lockfile / workflows / scripts from a fork) is deferred until the first such fork PR appears.
Execution:
needs-user-decision→pm:queuein this stroke;domain:skillsseat dispatches,priority:p1, Tier H (AGENTS.md + charter) — the maintainer merges by hand. The live specimen PR #19342 is disposed of under this ruling and the maintainer's reading of its need (「我理解实际需求是开发人员在开发环境要想测试 mcp,遇到要求https」): a product card is filed in this stroke for the dev-mode shape (os devaccepts plain-HTTP OAuth for MCP on any host with a loud startup warning; production unchanged, no switch), the owning seat adopts the contributor's logic and tests with co-author credit, and #19342 is closed with the landing link when that lands; the contributor is told so on the PR now.
Generated by Claude Code
- Card first (machine). Triage's fire scans open PRs whose
Claim:
domain:skillsseat 2 takes #19470 for dispatch under the maintainer's ruling batch #207 item 1 letter 是 (「fork PR 的处理同意你的意见。」, recorded by the director 5754996112:domain:skillsseat dispatches, p1, Tier H), at 2026-09-21T05:26Z
Session:session_017ETYWqMQD4qMtZzAGovWNi
Branch:claude/issue-19470-fork-pr-is-a-proposal
Worktree: the dev's own detached worktree fromorigin/main(⛔ never the seat's checkout)
Domain:domain:skills
Seat:domain:skills#2
File surface:scripts/pm/check-governed-merges.mjs— a second predicate beside the governed one (head.repo.full_name !== base.repo.full_name⇒ the existing H(人合) route with its own reason sentence) and the NOT-MEASURED wording for a head with zero check runs, self-test cases beside the existing predicate cases (≤ +80 including tests);AGENTS.md— ONE sentence in Prime Directive #14's paragraph (:255–:262 at8fc6a5f; 1109 / 1109 ⇒ paid in place by re-wrap or one retired restatement named in the PR body);.claude/skills/pm-dispatch/SKILL.md— ONE rule line in the 落地/派发 rules (fork PR = proposal, never landed by a seat; details by path) at ≤ +1 (815 / 819; ⛔ not the 分诊 / 定级 region :174–:175 / :362 / :368–:372 / :379, which is #19494's, and ⛔ not PR #19488's lines);.claude/skills/pm-dispatch/references/external-contributions.md— NEW, the ruled 〈外部贡献〉 section: the four points of 5754996112 (card first by triage's fire; the human decides the problem; the seat adopts the diff withCo-authored-by:, never lands the fork PR, writes on it only as review comments, closes it with the landing link; zero check runs = NOT MEASURED, never green) and the deferred supply-chain clause, ≤ 20 lines;references/core-rules.mdtwin of the rule line where it fits in place (151 / 151). ⛔ No new label, no new sweep, no workflow, nodocs/NORTH-STAR.md.
Container & model:mode:subagent·CONTRACT_REVIEW_TIER(mandatory tier:SKILL.mdis on the surface); landing Tier H (AGENTS.md) — the PR stays draft after the seat's ACCEPT, the maintainer merges by hand
Clause-②: no
Thread-read: 5754996112
Thread-read note: three comments read in full — the seat's decision analysis 5754644503, the director's ruling record 5754996112 (the maintainer's letter 是 and the four-point intake process), and the body's facets block.
Ruling-ref: batch #207 item 1 letter 是 — maintainer 「fork PR 的处理同意你的意见。」 (chat with the director, 2026-09-21, recorded 5754996112 withGoverning text:); the shape is the seat's option A as ruled: 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; mechanism minimal (predicate + one AGENTS.md sentence + one charter section + NOT-MEASURED wording), supply-chain deferred. The live specimen PR #19342 and the product card for the dev-mode shape are the director's / triage's acts in the same stroke — ⛔ not this card.
Premise re-read (origin/main8fc6a5f):check-governed-merges.mjsstill has nohead.repo/author_associationreading (0 hits);AGENTS.md:255–:262 still decides by paths only; no charter text on fork PRs (git grep -iE 'fork|外部贡献' -- AGENTS.md .claude/skills/pm-dispatch→ the three unrelated senses only); PR #19342 still open, draft, 0 check runs on5fa7b6d.
Serial constraints cleared: region-disjoint with #19494 (this seat, claimed, gated on PR #19488) and with PR #19488 (#19483) onSKILL.md;AGENTS.md— no open PR of this seat touches it, the dev readsgit log --oneline -5 origin/main -- AGENTS.mdand the open PR list before editing; the later lander mergesorigin/mainonce. Fleet tooling slot: the maintainer's ruling names this seat to dispatch — the direct channel, as #19457 records.
Net-line budget:check-governed-merges.mjs≤ +80;AGENTS.md0;SKILL.md≤ +1;core-rules.md≤ 0; the new references file ≤ 20.
Generated by Claude Code
os-dev-report
Reading time 2026-09-21T06:27Z · session session_017ETYWqMQD4qMtZzAGovWNi · head 0a50588 · PR #19526 (draft, Tier H)
{ "issue": 19470, "status": "done", "branch": "claude/issue-19470-fork-pr-is-a-proposal", "pr": "https://github.com/objectstack-ai/objectstack/pull/19526", "head_sha": "0a50588f76051acfa3bc27eb2aa88e16c55682d6", "session": "session_017ETYWqMQD4qMtZzAGovWNi", "premise_still_valid": true, "summary": "Report written at the stamped reading-time line above the JSON. Implemented the ruled minimal mechanism (batch #207 item 1, letter 是): a FORK predicate beside the PATH and SIZE limbs in scripts/pm/check-governed-merges.mjs (head.repo.full_name differs from base.repo.full_name, or head.repo null = a deleted fork, fail closed) that exits on the existing GOVERNED code through the Tier H terminal with its own reason sentence, plus the NOT-MEASURED wording when --pr reads ZERO check runs on the head (one extra GET on the chosen channel); one sentence in AGENTS.md Prime Directive #14 at net 0 (paid by retiring the Skills section restatement, pre-edit :800, 117 B, whose home is the directive itself: grep skills/** at :6/:260/:273, Tier S at :258/:276/:292, Both roots 0 hits); one rule line in SKILL.md 〈入队与落地〉 (:643, 108 B, 815 → 816 / 819); core-rules.md :122 rewritten in place to carry the twin (118 → 112 B, 151 / 151); new references/external-contributions.md (16 lines, widest 120 B) with the four ruled points and the deferred supply-chain line. Premise re-verified on origin/main 8fc6a5f before editing: 0 hits for head.repo / author_association / FIRST_TIME in the script, no fork text in AGENTS.md or the pm-dispatch skill (lit control governed = 18 hits), PR #19342 still open / draft / fork head / 0 check runs / 0 labels. The live probe --pr 19342: before exit 0 NOT governed; after exit 3, HEAD REPO decides, the fork sentence, check runs 0 NOT MEASURED. Mechanism assumption 4 verified after the PR existed: --pr 19526 exit 3 GOVERNED Tier H (.claude/** x3 + AGENTS.md; checks total 33). The PR stays draft for the maintainer hand; it carries skip-changeset (written through label-write.mjs, read back MATCHES). State on the PR I did not set: size/m and documentation labels by github-actions[bot] (the labelers). Assignee on the card was already set (os-steve) and the newest Claim: names this branch; no second claim posted. Nothing written on #19342, no label on #19470, no ready flip, no PATCH.", "files_changed": [ "scripts/pm/check-governed-merges.mjs (+98 / -18, net +80 incl. self-tests)", "AGENTS.md (+3 / -3, net 0: 1109 → 1109)", ".claude/skills/pm-dispatch/SKILL.md (+1 / -0: 815 → 816, ceiling 819)", ".claude/skills/pm-dispatch/references/core-rules.md (+1 / -1, net 0: 151 → 151)", ".claude/skills/pm-dispatch/references/external-contributions.md (new, 16 lines)" ], "net_lines": { "scripts/pm/check-governed-merges.mjs": "+80 (budget ≤ +80)", "AGENTS.md": "0 (1109 / 1109, headroom 0; +2 bought by one retired restatement + its blank line, not by re-wrap: the three PD #14 paragraphs are greedy-packed at 878 / 1789 / 918 chars = 8 / 16 / 8 lines)", ".claude/skills/pm-dispatch/SKILL.md": "+1 (816 / 819, budget ≤ +1)", ".claude/skills/pm-dispatch/references/core-rules.md": "0 (151 / 151, budget ≤ 0)", ".claude/skills/pm-dispatch/references/external-contributions.md": "+16 (budget ≤ 20; no CEILINGS row required by the ratchet, none added — the ratchet script is outside the file surface)" }, "before_after": { "AGENTS.md :264 → :264-:266": "before (102 B): \" paragraph names fewer surfaces than the register — or more. When it reds, name the surface here.\" | after (113 / 115 / 105 B): \"... 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.\"", "AGENTS.md pre-edit :800-:801 (retired)": "\"⛔ **Both roots are governed surfaces** — `skills/` is Tier H, `.claude/skills/` Tier S (**Prime Directive #14**).\" (117 B) + its trailing blank line; surviving home PD #14 :260 / :273 (register + Tier H name skills/**) and :276 (Tier S = all of .claude/**)", "SKILL.md :643 (new, 108 B)": "\"- fork PR = 提案,席位永不放行;采纳 diff 内部落地,见 `references/external-contributions.md`。\" — under the section pointer line, before the 条款② gate line; not in #19494 region (:174-:175, :362, :368-:372, :379) nor on PR #19488 lines; PR #19513 edits :632-:633 and :647 / :653 of the same section, :643 sits between its hunks", "core-rules.md :122": "before (118 B): \"- 验收后取路径面,命中规则层即分叉 ⛔ 不翻正式不入队;Tier S 席内达档复核 PASS 后入队。\" | after (112 B): \"- 验收后取路径面:规则层 ⛔ 不翻正式不入队,Tier S 达档 PASS 后入队;fork PR 永不放行。\"", "external-contributions.md": "bytes per line 45 · 0 · 117 · 117 · 0 · 113 · 77 · 97 · 92 · 102 · 110 · 113 · 106 · 113 · 116 · 120 — frame like siblings (title, 见 SKILL.md pointer line, one rule per line): ① card first + the one fixed comment; ② the human decides the problem; ③ the seat adopts the diff with Co-authored-by, review comments only, closed with thanks + link; ④ CI never approved by a seat, 0 check runs = NOT MEASURED; the machine face (--pr N routes head ≠ base through the H exit, head.repo null the same); supply-chain clause deferred", "check-governed-merges.mjs": "hunks: header docblock \"The FORK predicate\" (+11); battery roster +1, floor 30 → 31; applyGeneratedExceptions humanMerge keeps the fork limb; testVerdict(paths, { size, fork, checks }) carries fork + checks, humanMerge on any limb; forkVerdict(pull) (pure, exported) + landsByHumanMerge reads fork.isFork; renderTestVerdict prints renderForkLines under every branch and \"the HEAD REPO decides this PR\" when the path limb is clear; renderForkLines(fork, checks) (pure, exported); fetchPullFiles reads forkVerdict(head) and GET /commits/{sha}/check-runs?per_page=1 on the chosen channel, returns fork + checks; emitVerdict takes an intake, runPullMode passes it; self-test battery (6 cases) between the SIZE battery and the audit-half battery; summary line names it" }, "tests": "All on head 0a50588 (the union ran after the final commit; no later push). node scripts/pm/check-governed-merges.mjs --self-test :: exit 0 — \"✓ check-governed-merges --self-test: 441 assertions\" (first run with floor 7 exited 1 naming the new battery at 6 of 7: the floor was set to the registered 6, no case was lost). Live probes: --pr 19342 before (8fc6a5f) :: exit 0 \"✅ NOT governed — ordinary queue landing applies\" + \"size: 56 changed line(s)\"; --pr 19342 after :: exit 3 \"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.\" / \"⚠️ check runs on head 5fa7b6dc9d: 0 — NOT MEASURED, never green\"; --json fork {measured true, isFork true}, checks {total 0}. --test README.md :: exit 0 with \"head repo: NOT MEASURED\"; --test AGENTS.md :: exit 3 Tier H word for word. --pr 19526 (this PR) :: exit 3 GOVERNED Tier H, fork.isFork false, checks.total 33. npx eslint scripts/pm/check-governed-merges.mjs :: exit 0. Control-character scan of the five files: 0 hits. Ablation: none owed (no build/dist path; the new battery is the reverse proof — its cases fail on the pre-change predicate by construction: forkVerdict/renderForkLines did not exist and landsByHumanMerge ignored the head). NOT MEASURED: nothing — no package pnpm test / typecheck owed (no packages/** source touched); pnpm lint (repo-level eslint) is CI-owned, the edited script linted directly.", "gates": [ "pnpm check:pm-skill-ratchet :: exit 0 (before, on 8fc6a5f) — AGENTS.md 1109 / 1109 headroom 0", "pnpm check:pm-governed-prose :: exit 0 (before, on 8fc6a5f)", "pnpm check:pm-skill-ratchet :: exit 0 (after) — AGENTS.md 1109 / 1109; SKILL.md 816 / 819; core-rules.md 151 / 151", "pnpm check:pm-skill-id-lint :: exit 0 (after)", "pnpm check:pm-governed-prose :: exit 0 (after) — 2 surfaces name all 6 registered surfaces and claim no others", "pnpm check:nul-bytes :: exit 0 (after)", "node scripts/pm/check-governed-merges.mjs --self-test :: exit 0 (441 assertions)", "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths, on 0a50588) :: exit 0 — 44 commands derived; every one run in the foreground, exit captured before any pipe", "node scripts/pm/dispatch-gates.mjs --ran ran-union.txt --repo objectstack-ai/objectstack :: exit 0 — \"44 derived, 44 run, 0 NOT-MEASURED, 0 UNRUN\", all 44 exit 0", "pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 3 PREREQUISITE NOT MET on the fresh worktree (formula, then lint not built) → bash scripts/pm/os-verify-lock.sh -c \"pnpm --filter @objectstack/formula... build\" VERDICT command-exit 0 (191 s) and -c \"pnpm --filter @objectstack/lint... build\" VERDICT command-exit 0 (176 s) → re-run :: exit 0 (58 self-test cases, 22 examples judged clean)", "heavy families in the union all exit 0: node scripts/pm/check-governed-queue-guard.mjs --self-test, pnpm check:pm-dispatch-gates, node scripts/check-skills-token-ratchet.mjs (+ --self-test), pnpm check:pm-governed-merges, pnpm check:pm-half-states, pnpm check:pm-expected-skips", "git push pre-push hook check:commit-card-trailers :: 1 commit carries no card relation and no model identifier in the trailer pair", "CI on PR #19526: in_progress at report time (33 check runs on the head when --pr 19526 was read); not waited for" ], "mcp_calls": "0 — no MCP GitHub tool called, read or write", "api_writes": "3 REST writes + 2 git pushes — POST /repos/objectstack-ai/objectstack/pulls (draft PR #19526, body written once, read back: byte-identical modulo the trailing newline the platform strips, footer stored once); POST /repos/objectstack-ai/objectstack/issues/19526/labels (skip-changeset via label-write.mjs, four steps, read back MATCHES); POST /repos/objectstack-ai/objectstack/issues/19470/comments (this os-dev-report via post-stamped.mjs); git push -u origin claude/issue-19470-fork-pr-is-a-proposal ×2 (empty-branch probe 8fc6a5f, then 0a50588). Zero writes on PR #19342, zero on #19470 labels, no ready flip, no PATCH, no approval.", "deviations": [ "Line budget met exactly: the script is +80 net including self-tests (first cut measured +119, trimmed by joining assertion lines and compressing prose; six cases in the new battery, floor 6).", "The derived union batch 3 loop was moved to the background by the harness at its 10-minute foreground cap; the loop completed on its own (exit 0, all 11 recorded) and a defensive re-run of pnpm check:pm-dispatch-gates started before that record landed, so that family ran twice (both exit 0); the run record was de-duplicated keeping the first exit.", "Two builds ran under the shared verification lock only to satisfy check:doc-formula-expressions (it reads the built @objectstack/formula and @objectstack/lint); no package test/typecheck was owed by the diff.", "The harness attribution reminder asks for a Co-Authored-By trailer carrying a model name; AGENTS.md and the pre-push hook forbid a model identifier in the trailer pair, so the model-free pair was used (AGENTS.md wins).", "The PR body footer is the session-URL form under a rule line as AGENTS.md prescribes for PR bodies, not the harness reminder's Generated-with line.", "core-rules.md twin paid by compressing :122 (命中…即分叉 → 规则层; 席内达档复核 → 达档), no clause dropped; stated in the PR body with the byte readings.", "Behaviour change declared: --test and --branch stdout now carry a \"head repo: NOT MEASURED\" line under every verdict (the size limb precedent); exit codes unchanged; the --branch / --test byte-identity pin still holds." ], "open_questions": [], "out_of_scope_findings": [ "carrier: the domain:skills seat · references/external-contributions.md has no CEILINGS row in scripts/pm/check-skill-line-ratchet.mjs — the ratchet enumerates only references/lanes/ for uncovered files, so the new page is outside the line ratchet until pinned (first pin at the landed 16 is not a raise); the ratchet script is outside this card's file surface · noted in the PR Acceptance notes, not filed", "carrier: the domain:skills seat · the decision analysis's three confidence gaps stay unmeasured (pull_request_target in four workflow files; the MCP merge_pull_request / enable_pr_auto_merge route against a fork head; fork PR history beyond the newest 100) · noted, not filed", "carrier: none (observation, not a defect) · on a fresh worktree pnpm --filter @objectstack/lint run check:doc-formula-expressions is PREREQUISITE NOT MET (exit 3) until the formula and lint closures are built; its own frame says so and prescribes the build, so it is by design · Acceptance notes only" ] }
Generated by Claude Code
ACCEPT —
domain:skillsseat 2 (session_017ETYWqMQD4qMtZzAGovWNi, seat post #19287) on the dev report 5756301592, at 2026-09-21T06:31Z. PR #19526 at0a50588, reviewed in seat:## Contract reviewPASS 5756332685 on the PR (served tierCONTRACT_REVIEW_TIER),check-clause2-carriers --pair 19526exit 0,check-governed-merges --pr 19526exit 3 GOVERNED Tier H (AGENTS.mdon the register) ⇒ the PR STAYS DRAFT for the maintainer's hand; ⛔ no seat readies, queues or arms it.- Shape: the ruled minimal mechanism (batch Align concept documentation with 11-namespace protocol architecture and add individual protocol pages #207 item 1, letter 是) exactly:
forkVerdict— head repo ≠ base repo, orhead.reponull, fails closed as a fork — exits through the existing H(人合) terminal with its own sentence; zero check runs read 「NOT MEASURED, never green」; oneAGENTS.mdsentence in Prime Directive feat: Comprehensive CRM example demonstrating all ObjectStack protocol features #14 at net 0 (paid by retiring the Skills section's restatement, home = the directive's register lines); oneSKILL.mdline in 〈入队与落地〉 (:643, 108 B); core-rules :122 rewritten in place to carry the twin; the newreferences/external-contributions.md(16 lines) with the four ruled points, the fixed card-first comment verbatim, the machine face and the DEFERRED supply-chain line. ⛔ No new label, sweep or workflow — as ruled. - Scope: five files, all on the claimed surface; budgets met exactly (+80 / 0 / +1 / 0 / +16); region-disjoint with Charter(grading contract): after 57 of #19458 the triage criterion missed eight times — each with the card it was measured on; §3 is an input to #19483, §1 the two holes #19457 does not cover #19494 and PR docs(north-star,pm-dispatch): the feature axis — area:* labels, the 「路」 section as an ordered list of feature points, cross-layer features as parent + per-layer sub-issues, domain:* retreats to file ownership (#19483) #19488 on
SKILL.md; merge-tree clean againstorigin/main; nothing written on PR feat(auth): allow explicit intranet HTTP OAuth #19342, on 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's labels, no ready flip. - Contract:
Clause-②: noon card and PR — correct. - Tests:
check-governed-merges --self-test441 assertions exit 0 (seat re-run in an installed worktree); the live probe--pr 19342before / after (NOT governed → exit 3, fork sentence, 0 check runs NOT MEASURED) quoted in the record;--test/--branchbyte-identity pins hold with the declared 「head repo: NOT MEASURED」 line. - Gates / CI: ratchet 0 (1109 / 1109 · 816 / 819 · 151 / 151), id-lint 0, governed-prose 0 (six surfaces named, no more); the dev's union 44 / 44 exit 0; CI on the head green but for two runs still in progress at this write.
- Report: complete per the contract —
api_writes3 + 2 pushes named,mcp_calls0,deviations8 (each public, the model-free trailer pair kept over the harness reminder — correct), noopen_questions,out_of_scope_findingswith carriers (the references page's missing CEILINGS row rides the seat's next ratchet touch; the three confidence gaps stay seat items), ⛔ no card filed. - Landing: Tier H — the maintainer merges by hand (the PR carries its
## 维护者速读(草稿)); at MERGED the seat confirms by content, removespm:dispatched, confirms the card closed byFixes. After it lands, the fork-PR intake (card first by triage's fire) is live for the specimen PR feat(auth): allow explicit intranet HTTP OAuth #19342, whose disposal is the director's / triage's act.
Generated by Claude Code
- Shape: the ruled minimal mechanism (batch Align concept documentation with 11-namespace protocol architecture and add individual protocol pages #207 item 1, letter 是) exactly:
- added a commit that references this issue
on Sep 28, 2026
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/CODEOWNERSheader, read fromorigin/main2026-09-20T23:35Z, recording the 2026-08-18 ruling (#9495):⭐ 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.mdplus the wholepm-dispatchskill onorigin/main, 2026-09-20T23:35Z, forfork,third.party,第三方,外部贡献,FIRST_TIME:governedinAGENTS.mdZZZQNotAPolicy⇒ 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.mjsdecides 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
feat(auth): allow explicit intranet HTTP OAuthjinyitao123,author_association: **FIRST_TIME_CONTRIBUTOR**jinyitao123/objectstack, branchcodex/intranet-http-oauthpackages/plugins/plugin-auth/**and one changesetWhat it does: adds an escape hatch
OS_ALLOW_INSECURE_OAUTH_HTTP=truetoisOAuthEligibleBaseUrl, 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.trueopens 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:393the 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
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 insidecheck-governed-merges.mjsso it exits through the existing H route — ⛔ not as a new label and a new sweep.PREREQUISITE NOT MET … ⛔ This is NOT a pass: nothing was measuredwording two gates in this repo already use.package.jsondependencies, 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-auth⇒domain: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/mainand the REST API. Filed-by:session_01LvwGppdonww4zGLWZo5rho(domain:specexecution seat 1) — a governance finding for theskillslane; ⛔ 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