Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ AGENTS.md 的「只跑受影响的包」指的是**用上面的路径过滤缩
队列会把 PR **在当前 `main` 上重建**后再落地,重建不绿就把它踢出队列,而不是把红的落到共享 `main` 上。所以旧版那条「绝不 `gh pr merge --auto`」的前提已经反转:它防的正是队列现在替你防住的事,而在强制队列的仓库里 **enable auto-merge 就是入队的标准手段**,也是本仓实际走得通的唯一通路(仓内佐证:`.github/workflows/dependabot-auto-merge.yml` 对 Dependabot PR 用的就是 `gh pr merge --auto --squash`)。注意 path-filter 跳过的检查(显示 `skipping`)不是失败,配合 `mergeStateStatus: CLEAN` 即算全绿。
- **auto-merge 会在「合并冲突」和「draft」窗口里被静默丢弃 —— 事后必须复查并重挂。** 已两次踩实(先例 PR #3458):PR 一旦变成 conflicting、或被(重新)标记为 draft,已挂上的 auto-merge 就没了,**且不会有任何通知**。解完冲突或 `gh pr ready` 之后若不重新挂一次,PR 会一直停在那里 —— 看着"全绿待合",实际谁也没在等它。收工前复查一次:`gh pr view <n> --json isDraft,mergeStateStatus,autoMergeRequest`,`autoMergeRequest` 为 `null` 就是掉了,重挂。
- **不必为了合并去 rebase 其他在途分支** —— 队列自己会在当前 `main` 上重建,旧版「串行合并、合下一个前先 rebase 在途分支」那套编排已是历史。**但队列只拦得住文本冲突和 CI 看得见的破坏**:两个各自全绿的 PR 仍可能**语义冲突**(改了同一约定的两端;一边删掉了另一边刚开始用的导出)。所以动**共享面**(barrel/注册表/公共类型/跨包约定)时,合并前扫一眼在途 PR(`gh pr list`),有交叠就在 PR 正文里写清交叠点与取并集的办法(先例:PR #3458 对 #3456 同文件交叠的说明)。
- ruleset 的**具体配置**(谁可绕过、required checks 清单)本文不写 —— 从仓内读不到,别照抄任何推断。上面几条写的都是实测到的可观测行为。
- ruleset 的**具体配置**(谁可绕过、required checks 清单)本文不写,两半的理由不同(objectui#9520 实测):**谁可绕过**从仓内读不到(ruleset 端点对席位 token 只答「我能不能绕过」,不给名单;这一半没有仪器复核),别照抄任何推断;**required checks 清单**读得到,要看就跑 `node scripts/check-required-check-set.mjs`(`.github/workflows/required-check-set-patrol.yml` 定时跑它;`pnpm check:required-check-set` 只是它的离线 self-test,不读线上),⛔ 别把它的答案抄进本文。上面几条写的都是实测到的可观测行为。

### ⚠️ Actions workflow 注册表:`list_workflows` 回答不了「本仓到底跑不跑 X」

Expand Down Expand Up @@ -547,7 +547,7 @@ sha pin **退休**之后这条**更重、不是更轻**(维护者 2026-09-04 裁
- **CI 全绿、已 review 都不构成例外。** 这类文件是后续每一次 dispatch 读的操作规程,绿灯说明不了它该不该成为规程。
- **发现自己已经挂上了怎么办**:把 PR 转回 **draft** 是唯一能可靠退出合并队列的动作 —— 只调 `disable_pr_auto_merge` 会摘掉 auto-merge 但**不取消队列成员资格**,两个都要做。⚠️ 只回收**你自己**挂上的:本仓多 agent 共用同一 GitHub 身份,不是你设置的状态就属于别的 actor —— 去问、去报告,别替他回退。

**本仓的机械兜底只有一件,而且它现在只报告、不拦截 —— 别读成一道拦得住的门,也别再读成「什么都没有」。** 本仓仍然没有 CODEOWNERS(核实:仓内不存在该文件),受管面上也没有钩子;但 `.github/workflows/governed-surface-guard.yml`(check 名 `Governed Surface Queue Guard`,判定逻辑在 `scripts/check-governed-queue-guard.mjs`)**是活的**:`pull_request` 腿是早期告警、**故意 exit 0**(受管 PR 停在 draft 正是健康终态,所以**绿不等于不受管**),`merge_group` 腿才是会拒绝的那条 —— 它要求 `GOVERNED_APPROVERS`(`os-zhuang` / `hotlong`)里某个账号的一条 latest-decisive APPROVED review,**留在哪个 commit 上都算**;DISMISSED 与被顶掉的批准(同一 reviewer 后续给了 CHANGES_REQUESTED)不算,该集合之外账号的 APPROVED 不算,review 列表为空或读不到则 fail closed —— 判定读的是**有没有一条人工批准记录**,不问它是对哪些字节给的(维护者 2026-09-04 裁,逐字未译:「你的门禁有问题,只需要有人工批准记录就行,不需要卡最新的提交。」;sha pin 是**退休**不是放宽,守卫里已没有任何判定读 `commit_id`)。⚠️ 已接受的代价:批准之后的 push 不再被这道门重审,一个已批准的受管 PR 可以带着批准者没读过的字节落地 —— 维护者接受这一点,而这道拒绝先印的补救仍是转回 draft、交人类合并。⚠️ 而这条拒绝腿上的判据**不止上面那一条** —— #9018(PR #9212)给 `merge_group` 腿加了**第二条、与受管面完全无关的**判据:它枚举该 merge group 要落地的**每一个** PR(逐 commit 分解,不是只读 `merge_group.head_ref`),读每个 PR 的 label,其中任何一个仍挂着 `needs:contract-review` 就拒绝(**exit 6**);label 读不到、或该 group 指不出任何 PR,同样拒绝(**exit 7**)。⚠️ 这条判据读的是**挂在 PR 上的 label**,不读任何路径、不问 diff 命中了什么 —— ⇒ **一个 diff 完全没碰受管面的 merge group,现在也可能被这道 check 拒绝**;`pull_request` 腿不受影响,不读 label。⚠️ 但它**尚未**是 required context:ruleset 开关只有维护者能翻(#6596,`pm:awaiting-maintainer`),**在翻转之前,那条拒绝腿只报告、不阻止队列**。事后一侧:`../objectstack` 的 report-only 合并后审计(`scripts/pm/check-governed-merges.mjs`)自 objectstack#9619 起**已覆盖本仓**(四个受管仓一次扫完),它把受管面的合并列出来,但同样不阻止任何事。⇒ 违规不再完全静默,但**仍然没有任何东西会替你拦下它**,这条规则的效力主要还是在于你读到了它并照做。**⛔ 别再把本段当成兜底工具的完整清单** —— 覆盖面以脚本自己的 `GOVERNED_SURFACES` 为准(它随树变化,本段不会);⚠️ 该清单曾与上面的受管面清单**并不一致**(脚本的集合含已发布 `skills/**`),这一分歧**已裁**:维护者第 5 场决裁批 #7 采 **Option A**(#6866 评论 5469339478)—— 已发布 `skills/**` **受管**,守卫的读法才是裁定的那个;该裁决**已随本段上方的清单落地**(#6866):上面五项已含 `skills/**`,与脚本的 `GOVERNED_SURFACES` 一致,曾经那条「仓根 `skills/**` 不受管、自行入队」的豁免**已作废**,⛔ 别再照它行事。
**本仓的机械兜底只有一件,它的两条腿意思不同:`merge_group` 腿拦得住合并队列(2026-09-27 起,见下),`pull_request` 腿只告警 —— 别把 PR 页上的绿读成不受管,也别再读成「什么都没有」。** 本仓仍然没有 CODEOWNERS(核实:仓内不存在该文件),受管面上也没有钩子;但 `.github/workflows/governed-surface-guard.yml`(check 名 `Governed Surface Queue Guard`,判定逻辑在 `scripts/check-governed-queue-guard.mjs`)**是活的**:`pull_request` 腿是早期告警、**故意 exit 0**(受管 PR 停在 draft 正是健康终态,所以**绿不等于不受管**),`merge_group` 腿才是会拒绝的那条 —— 它要求 `GOVERNED_APPROVERS`(`os-zhuang` / `hotlong`)里某个账号的一条 latest-decisive APPROVED review,**留在哪个 commit 上都算**;DISMISSED 与被顶掉的批准(同一 reviewer 后续给了 CHANGES_REQUESTED)不算,该集合之外账号的 APPROVED 不算,review 列表为空或读不到则 fail closed —— 判定读的是**有没有一条人工批准记录**,不问它是对哪些字节给的(维护者 2026-09-04 裁,逐字未译:「你的门禁有问题,只需要有人工批准记录就行,不需要卡最新的提交。」;sha pin 是**退休**不是放宽,守卫里已没有任何判定读 `commit_id`)。⚠️ 已接受的代价:批准之后的 push 不再被这道门重审,一个已批准的受管 PR 可以带着批准者没读过的字节落地 —— 维护者接受这一点,而这道拒绝先印的补救仍是转回 draft、交人类合并。⚠️ 而这条拒绝腿上的判据**不止上面那一条** —— #9018(PR #9212)给 `merge_group` 腿加了**第二条、与受管面完全无关的**判据:它枚举该 merge group 要落地的**每一个** PR(逐 commit 分解,不是只读 `merge_group.head_ref`),读每个 PR 的 label,其中任何一个仍挂着 `needs:contract-review` 就拒绝(**exit 6**);label 读不到、或该 group 指不出任何 PR,同样拒绝(**exit 7**)。⚠️ 这条判据读的是**挂在 PR 上的 label**,不读任何路径、不问 diff 命中了什么 —— ⇒ **一个 diff 完全没碰受管面的 merge group,现在也可能被这道 check 拒绝**;`pull_request` 腿不受影响,不读 label。⚠️ 它拦得住,是因为这个 check 名进了 ruleset 的 required 集合 —— 下面是一段有日期的历史,不是本段替你复核的现状:ruleset 开关只有维护者能翻;2026-09-14 的读数里它还不在集合中,那时这条拒绝腿只报告、不阻止队列;ruleset 的最后一次编辑在 2026-09-27,三分钟后 objectui#6596 以 completed 关闭,2026-09-28 实测它已在集合里。**在集合里,它的拒绝(上面的 exit 6 / exit 7 同样)就会把 PR 踢出合并队列。** 它现在还在不在,跑 `node scripts/check-required-check-set.mjs` 读,⛔ 别把它的答案抄进本段。⚠️ 这是按配置推出的,不是观察到的:登记后到 2026-09-28 的 `merge_group` 运行全部通过,拒绝路径还没在队列里真正触发过一次。事后一侧:`../objectstack` 的 report-only 合并后审计(`scripts/pm/check-governed-merges.mjs`)自 objectstack#9619 起**已覆盖本仓**(四个受管仓一次扫完),它把受管面的合并列出来,但它不阻止任何事。⇒ 一个没有获授权批准记录的受管 PR 经合并队列落不了地,这一半队列替你拦;**拦不住的是批准本身**:席位共用一个 GitHub 身份,守卫分不出那条 APPROVED 是不是人留的,绕过 ruleset 的人也不经这道门(谁能绕过,见上面 ruleset 那一条)—— 所以第五条禁令的效力仍然全在于你读到了它并照做。**⛔ 别再把本段当成兜底工具的完整清单** —— 覆盖面以脚本自己的 `GOVERNED_SURFACES` 为准(它随树变化,本段不会);⚠️ 该清单曾与上面的受管面清单**并不一致**(脚本的集合含已发布 `skills/**`),这一分歧**已裁**:维护者第 5 场决裁批 #7 采 **Option A**(#6866 评论 5469339478)—— 已发布 `skills/**` **受管**,守卫的读法才是裁定的那个;该裁决**已随本段上方的清单落地**(#6866):上面五项已含 `skills/**`,与脚本的 `GOVERNED_SURFACES` 一致,曾经那条「仓根 `skills/**` 不受管、自行入队」的豁免**已作废**,⛔ 别再照它行事。

### 服务纪律(本仓库与 `../objectstack` 多 agent 并行开发)

Expand Down
49 changes: 32 additions & 17 deletions scripts/__tests__/check-required-check-set.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -358,11 +358,17 @@ describe('check-required-check-set — the wiring', () => {
* stale the next time a maintainer edits the ruleset.
*
* ⚠️ The population is named in words: the three files objectui#9502 repaired.
* `AGENTS.md` carries a fourth instance and is deliberately NOT in that set —
* it is governed surface, and its parenthetical also covers who may bypass the
* ruleset, which the endpoint this gate reads does not carry. That exclusion is
* what makes the control below real: the same detector, run over `AGENTS.md`,
* must FIRE. A zero from a detector never observed firing is decoration.
* `AGENTS.md` carried a fourth instance and is deliberately NOT in that set.
* objectui#9502 left it standing — it is governed surface, and its
* parenthetical also covered who may bypass the ruleset, which the endpoint
* this gate reads does not carry — and objectui#9520 then split it along that
* line: its required-checks half points at this gate, and "从仓内读不到" stays
* on the bypass-actors half alone, where it was measured true. So it still
* cannot join `REPAIRED` (the "makes no cannot-be-read claim" case would go red
* on that true half, which is exactly what Leg 2 below asserts it carries), and
* it is still what makes the control below real: the same detector, run over
* `AGENTS.md`, must FIRE. A zero from a detector never observed firing is
* decoration.
*/
describe('check-required-check-set — the prose points here instead of answering (#9502)', () => {
/** The three files objectui#9502 repaired. */
Expand All @@ -372,8 +378,12 @@ describe('check-required-check-set — the prose points here instead of answerin
'scripts/dependabot-merge-gate.mjs',
];

/** The carrier left standing on purpose — and this block's positive control. */
const LEFT_STANDING = 'AGENTS.md';
/**
* The carrier objectui#9502 left standing and objectui#9520 split: the one
* file that still writes "从仓内读不到", on its bypass-actors half, where it is
* true — and this block's positive control.
*/
const SPLIT_CARRIER = 'AGENTS.md';

/**
* The claim being hunted: the ruleset cannot be READ from this repository.
Expand All @@ -393,15 +403,20 @@ describe('check-required-check-set — the prose points here instead of answerin
expect(CANNOT_READ.test('that set is a surface nothing here can change')).toBe(false);

// Leg 2, the same detector over real content: `AGENTS.md` still carries the
// claim, deliberately. If this leg ever goes red the governed carrier was
// ruled on and repaired — which is a legitimate change, not a bug here. The
// remedy is to move the inventory docblock in the gate with it and retire
// this leg, NOT to weaken the detector.
// phrase, deliberately, on the bypass-actors half of its ruleset bullet —
// the one place in the tree it is true, which is where objectui#9520 kept it
// when it split that bullet. ⚠️ Said per AGENTS.md #9 rather than left to be
// assumed: this detector cannot tell which half a phrase sits on, so this
// leg does NOT hold the bullet's required-checks half to its pointer, and
// nothing else in this file does either. If this leg ever goes red the bypass
// half was re-worded or ruled on again — a legitimate change, not a bug
// here. The remedy is to move the inventory docblock in the gate with it and
// retire this leg, NOT to weaken the detector.
expect(
CANNOT_READ.test(flatten(LEFT_STANDING)),
`${LEFT_STANDING} no longer carries the claim this detector hunts. If that carrier was ` +
`repaired, update the inventory docblock in ${GATE} to match and drop this leg. ` +
'Leg 1 above keeps the detector honest either way.',
CANNOT_READ.test(flatten(SPLIT_CARRIER)),
`${SPLIT_CARRIER} no longer carries the phrase this detector hunts (its bypass-actors half did, ` +
`deliberately). If that half was re-worded, update the inventory docblock in ${GATE} to match ` +
'and drop this leg. Leg 1 above keeps the detector honest either way.',
).toBe(true);
});

Expand Down Expand Up @@ -440,9 +455,9 @@ describe('check-required-check-set — the prose points here instead of answerin
}
});

it("the gate's own docblock inventories every repaired carrier, and the one left standing", () => {
it("the gate's own docblock inventories every repaired carrier, and the one objectui#9520 split", () => {
const docblock = fs.readFileSync(path.join(ROOT, GATE), 'utf8').slice(0, 4000);
for (const rel of [...REPAIRED, LEFT_STANDING]) {
for (const rel of [...REPAIRED, SPLIT_CARRIER]) {
expect(docblock, `${GATE} no longer names ${rel} in its inventory`).toContain(rel);
}
// The write half is the reason the sentences were not simply deleted.
Expand Down
17 changes: 11 additions & 6 deletions scripts/check-required-check-set.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,21 @@
* .github/workflows/dependabot-auto-merge.yml the header's "does NOT do" list
* scripts/dependabot-merge-gate.mjs "declared = enforced"
*
* ⛔ One carrier is deliberately left standing, and it is not an oversight:
* ⛔ One carrier was deliberately left standing by objectui#9502, and it was
* not an oversight:
*
* AGENTS.md "从仓内读不到"
*
* AGENTS.md is GOVERNED surface -- an agent drafts it, an authorised approver
* lands it -- so objectui#9502 did not touch it. It may also be true on a leg
* the repaired sentences never had: its parenthetical covers who may BYPASS the
* ruleset as well as the required-context list, and bypass actors are not
* carried by `GET /rules/branches/{branch}`, the endpoint this file reads.
* ⛔ Ruling on it is a separate, governed decision and is not made here.
* lands it -- so objectui#9502 did not touch it. Its parenthetical also covered
* who may BYPASS the ruleset, and bypass actors are not carried by
* `GET /rules/branches/{branch}`, the endpoint this file reads. objectui#9520
* ruled on it as a governed change of its own and split it along that line:
* the required-checks half now points at this file, and "从仓内读不到" stays on
* the bypass-actors half alone, where it was measured true -- the ruleset
* endpoint answers a seat's token with `current_user_can_bypass` and no
* `bypass_actors` key. ⛔ That kept half is not a missed repair. Nothing in this
* tree re-derives it, either.
*
* The WRITE half of every one of them is still true and this file does not
* touch it. ⛔ This script never writes: no enrolment, no removal, no ruleset
Expand Down
Loading