Skip to content
Draft
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
59 changes: 59 additions & 0 deletions AGENTS.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -283,6 +283,65 @@ AGENTS.md 的「只跑受影响的包」指的是**用上面的路径过滤缩
- **不必为了合并去 rebase 其他在途分支** —— 队列自己会在当前 `main` 上重建,旧版「串行合并、合下一个前先 rebase 在途分支」那套编排已是历史。**但队列只拦得住文本冲突和 CI 看得见的破坏**:两个各自全绿的 PR 仍可能**语义冲突**(改了同一约定的两端;一边删掉了另一边刚开始用的导出)。所以动**共享面**(barrel/注册表/公共类型/跨包约定)时,合并前扫一眼在途 PR(`gh pr list`),有交叠就在 PR 正文里写清交叠点与取并集的办法(先例:PR #3458 对 #3456 同文件交叠的说明)。
- ruleset 的**具体配置**(谁可绕过、required checks 清单)本文不写 —— 从仓内读不到,别照抄任何推断。上面几条写的都是实测到的可观测行为。

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

这条已经造成过实际损害(objectui#6069):一个 agent 被要求**删掉**一条虚假的安全工具声明,却被一张
建立在注册表读数上的卡**指去写上另一条同样虚假的安全工具声明**。那条虚假声明写在 #5408 的 dispatch
评论里(不在它的 diff 里),而 #5408 已随 PR #5963 合并。它没有酿成更大的事,只因为实施的 dev 拿
`main` 核对了替换文本,而不是信任那张卡。

**注册表按「该 workflow 在任意 ref 上的第一次运行」建条目 —— 与默认分支无关,条目此后一直留着。**
不是 push 建的,也不是合进 `main` 建的。实测:四个样本、跨越七个月,注册表条目的 `created_at` 与该
workflow **最早一次 run** 的 `created_at` **精确到秒相同**;push 被一个决定性的负例排除 ——
`pre-install-import-graph.yml` 推上分支后在**未注册状态下停了 7 分 45 秒**,直到 PR 打开、第一次 run
被调度的那一刻,条目才出现。

于是:**一个 workflow 文件只要在任何 PR 分支上跑过一次,就永久登记在册** —— 哪怕它从未进过 `main`,
哪怕那条分支早已废弃。2026-08-24 复测(`c677fe3b8`):在册的文件型条目 32 个(另有 4 个 `dynamic/*`),
`main` 上的 workflow 文件 26 个,**「在册但不在 `main`」6 个,「在 `main` 但不在册」0 个** —— 分歧是
单向的。

**`state: "active"` 的意思是「没有被 disable」。它不是关于 `main` 的任何断言。** 这就是那个 false
friend 的全部:读到 `active` 就以为「这个扫描在本仓生效」,是把「曾经跑过一次」当成了「现在在跑」。

⛔ **别据此写一个「注册表 vs `main`」的交叉校验门禁 ——「应该在册」没有可靠定义。** 「在册但不在
`main`」正是**在 PR 分支上跑过一个新 workflow 的正常结果**:每一个新增 workflow 的 PR 在合并前都会造
出这样一条。活例子:`pre-install-import-graph.yml` 于 `21:41Z` 注册,本轮测量开始时它是「在册但不在
`main`」的第 7 条;测量进行到一半,它随 PR #6159 于 `22:58Z` 合并进 `main`,这一条自己就消失了 —— 在
那 77 分钟里开着的门禁,会红在一个完全健康的 PR 上。反方向「在 `main` 但不在册」则结构性地近乎恒空:
在 `main` 上的 workflow 会跑,而一跑就注册,只有「合并到首次运行」之间的时间窗能填充它,那是竞态不是
缺陷。要把「废弃」和「在途」分开,只能靠一个分支存活性的猜测 —— 正是这张卡自己警告过的那种猜测。

⚠️ **删除方向本仓没有数据,别外推。** 「workflow 文件从默认分支被删掉之后,注册表条目会怎样」在本仓
**从未发生过、也未经测试**:`git log origin/main --diff-filter=D --name-only -- '.github/workflows/*'`
返回**空**,而 `main` 历史上出现过的路径集合与今天在册的完全一致(复测:26 = 26)。所以上面那句「条
目此后一直留着」只对**从未进过 `main`** 的文件成立 —— 它们根本没有「从默认分支删除」这个事件可供触
发。⛔ 别把它读成关于删除行为的结论。

⚠️ **API 与人类看到的 Actions 标签页是否一致,本仓无法确定 —— 这是个未解问题,不是已答问题。**
`https://github.com/objectstack-ai/objectui/actions` 与 `api.github.com` 对这些会话都返回 **403**,
MCP 工具是唯一能到达的注册表视图,所以两者的差异既没被证实也没被排除。

#### ⭐ 通用规则:相信任何「不存在」之前,先把 `total_count` 和返回数组的长度比一下

**这不是 workflow 专属的 —— 它对每一个分页列表都成立。** `list_workflows` **忽略 `per_page`**,固定
返回 30 条,同时在**同一个 JSON body 里**如实报告真实的 `total_count`。实测:`total_count: 36`,第 1
页 30 条,第 2 页 6 条,`30 + 6 = 36` —— **并集**才是完整的。

只读数组、不读计数,一次截断的列表就变成一份「确信的缺席」。#6069 那张卡本身就是这么错的:它的整个
论点正是「枚举会给出自信的错误答案」,而它自己只读了 36 条里的 30 条,把两个**已注册**的 workflow
写成了「注册表看不见它们」。**从一页被截断的结果里读出来的「没有」,根本不是一次读数。**

#### 「本仓到底跑不跑 X?」—— 一条命令,不查注册表,不上 CI

```bash
git cat-file -e origin/main:.github/workflows/X.yml # 退出 0 = 真的在 main 上,真的会跑
git cat-file -e origin/main:.github/workflows/ci.yml # 阳性对照:必须解析成功
```

**阳性对照不是可选项。** 没有它,一个打错的路径和一次真实的缺席给出完全相同的退出码,而你会把前者读成
后者。

### ⛔ 受管面(governed surface):agent 起草,人类合并

维护者裁决(2026-08-18),**原文照录、不翻译** —— 提问明确点名了本仓:
Expand Down