Skip to content
Merged
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
82 changes: 81 additions & 1 deletion .agents/docs/2026-08-30-graphics-stack-coverage-design.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# mcpp 图形栈:从「能跑通」到「能开发」的覆盖面设计

Date: 2026-08-30 · 前置:[`2026-08-30-gbm-cross-repo-closed-loop-plan.md`](2026-08-30-gbm-cross-repo-closed-loop-plan.md) §19/§20 · 状态:待 review
Date: 2026-08-30 · 前置:[`2026-08-30-gbm-cross-repo-closed-loop-plan.md`](2026-08-30-gbm-cross-repo-closed-loop-plan.md) §19/§20 · **状态:已实现并闭环验证(v1.0,见 §11 交付总账)**

## 0. 这份文档解决什么

Expand Down Expand Up @@ -930,3 +930,83 @@ xim#732 加 `XKB_CONFIG_ROOT` 那天就出了回归(xim#733 修):`mesa` 一直

G6 仍是需要决策的:合成器可以在「已有 DRM master」的前提下开发(从 TTY 直接启动、或
`SEATD_SOCK`),把 session 管理留到最后。

---

## 11. 交付总账(v1.0)

### 11.1 完整变更集

八个 PR,两个仓,全部已合入:

| 仓 | PR | 内容 | 性质 |
|---|---|---|---|
| mcpp-index | #298 | libinput 测试成员 + 描述符四个 bug | 功能 |
| mcpp-index | #301 | 沙箱脚本纳入输入链 / RMLVO / quirks 断言 + 文档 | 验证 |
| xim | #731 | nvidia-gl-host-link 声明 Vulkan ICD | 功能 |
| xim | #732 | xkeyboard-config 键盘布局数据集 | 功能 |
| xim | #733 | mesa 只声明自己填的路径 | **#732 的回归** |
| xim | #734 | libinput-quirks 设备 quirks 数据集 | 功能 |
| xim | #735 | consumer_envs 跳过标量 + `libs/**` 纳入 CI | **#732/#734 的回归** |
| xim | #736 | publish-artifact 也看 `libs/**`,并把自己列进 paths | **#735 的交付缺口** |

### 11.2 为什么不是单 PR —— 两条硬约束和一处我的失误

**硬约束一:跨仓。** mcpp-index 打包代码,xim 发布数据目录与运行期发现变量;一个
GitHub PR 无法跨两个仓库。这个切分不是选择,§10.9 论证过它是两个索引各做各建模的
事 —— 而**正是这条约束**决定了这套东西必须至少两个 PR。

**硬约束二:发布顺序。** #301 的沙箱脚本从**已发布索引**解析 `compat.libinput`
(它只声明一个索引键,见脚本注释),所以它只能在 #298 合入**并且索引产物发布之后**
才可能被验证。合成一个 PR 会让脚本引用一个还不存在的包。

**我的失误:#733 与 #735 本可以不存在。** 它们修的是 #732 引入的回归,而找到它们
靠的是自我 review 而非新信息 —— 也就是说,如果我在合 #732 之前就跑完那份检查清单,
两处都会在 #732 里。清单本身是被这两次回归**逼出来**的(§10.10.1 与下节),这正是
代价:**规则是从事故里学的,而事故已经进了历史**。

### 11.3 `libs/**` 三处缺席 —— 本轮最可迁移的发现

`libs/graphics.lua` 被四个 recipe `import`,是这个索引里波及面最大的文件,而它同时
是**唯一完全不被看见**的一类改动:

| workflow | 缺席 `libs/**` 的代价 |
|---|---|
| `ci-test.yml` | 评审覆盖 —— 改动不跑任何检查 |
| `ci-xpkg-test.yml` | 评审覆盖 |
| **`publish-artifact.yml`** | **交付** —— 产物不重出,用户拿到旧版 |

第三处最隐蔽:#735 合进 main、11 个检查全绿、gitee 镜像同步到 `ea36f6b`,而
`xlings update` 仍在发 `xim-index-f1702a1.tar.gz` —— 修复**之前**那一版。夜间
cron 最终会补上,所以是**慢漏**不是断供,但「合了、绿了、用户拿到的还是旧的」骗过了
每一个常规检查。

**这解释了本轮四处问题为什么全部靠自我 review 发现、没有一处是 CI 抓到的** ——
CI 从来没有机会看那个文件。

两条操作性结论:

1. 判断共享文件有没有被覆盖,**读 `on.paths`,别看 workflow 名字**。反查:
`grep -rln "pkgs/\*\*" .github/workflows/ | xargs grep -L "libs/\*\*"`
2. 修复合入后**核验产物,不要核验 PR 状态**:`xlings update` 之后直接 grep 本地
索引里那段代码。「PR merged + CI green」不蕴含「用户拿到了」。

发布器现在把**自己**也列进 paths(沿用 `ci-test.yml` 既有做法),于是改发布器的 PR
自验 —— 实测:合入后立刻自触发一次 `push` 发布,而合入前同样的改动零触发。

### 11.4 加一行 DISCOVERY = 三项检查

被 #733 与 #735 各逼出来一项:

1. 新提供方 `config()` 里要有**两个**调用(`declare_*` 放置 + `declare_subos_env`
声明)—— 只声明会让变量指向不存在的目录(§10.9.1);
2. 回头看每个 `declare_subos_env` 调用点传没传集合 —— 不传 `only` 就是「声明每一
行」,新行会被现有提供方自动继承(§10.10.1);
3. 回头看 `consumer_envs()` 的消费者 —— 新行会自动进入**每一个** shim,而 shim 侧
**没有 `op`**,标量会被拼成冒号路径(#735)。

### 11.5 最终状态

发现变量表六格全部有提供方,第七格经核实不该补(§10.9.4)。闭环验证一次跑完
(§10.8.2),用户侧产物已核验为 `xim-index-ea36f6b.tar.gz`(含全部修复),两份数据
归档的 GLOBAL/CN 镜像均已发布且逐字节一致(sha256 比对,非可达性)。