--- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -220,13 +220,31 @@ # ║ ① 让 ~4% 的偶发红进主干(长红 ⇒ 所有人学会忽略 ⇒ 整道门失效) ║ # ║ ② 先挡住、同时派单修根因(本表选这条) ║ # ║ ║ - # ║ **整包永久排除不被接受。** 唯一出口是给 plugin.Registry 的 Set* ║ - # ║ 加锁(修复单 KERNEL-REGISTRY-SETLOCK),修好后: ║ - # ║ · 删掉本表里 kernelboot/modules 那一行 ║ - # ║ · **复跑一次** -race 全仓确认整机判据在 -race 下也稳定 ║ - # ║ · 删掉 evidence 步骤 ② 的静态断言 ║ - # ║ 这三步没做完,本线不算收口。 ║ + # ║ **整包永久排除不被接受。** 本表目前**已经没有** pkg 级条目。 ║ # ╚══════════════════════════════════════════════════════════════════╝ + # + # ── 已收口:KERNEL-REGISTRY-SETLOCK(2026-10-24)── + # + # 原先这里有一条 pkg 级排除 `internal/kernelboot/modules`: + # 该包把真实整机装起来 ⇒ 第 4 层 agent 与 plugin-host 并行 Init 时, + # 双方都无锁直写同一 plugin.Registry 的 7 个 Set*。竞争在**内核装配**里, + # 不在该包代码里,所以任何装整机的用例都会撞上(实测约 4%)。 + # + # 已修(分支 fix/registry-setlock):plugin.Registry 的 24 个 Set* 全部取写锁, + # 读侧(buildSDK)改为"一次 RLock 取整组快照后立刻放锁",读侧仍是 RLock。 + # 并按"必要性不对称"去掉了 agent.wirePluginSDK 与 pluginhost.injectBaseDeps + # 的重复注入(保留 pluginhost 侧 —— 它不依赖 agent,是"插件不等 agent 35 秒" + # 的那半边;agent 侧只留 SetStatusProvider)。 + # + # 收口三步**已全部做完**,故本表删掉了那一行: + # · 删掉 kernelboot/modules 排除行 ✔(本提交) + # · 复跑 -race 确认整机判据在 -race 下稳定 ✔(25/25 无 DATA RACE; + # 修前同条件 2/25 报红、累计 14 条) + # · 删掉 evidence 步骤 ② 的内核 setter 静态断言 ✔(本提交) + # + # ★ 注意:**去重不能替代加锁**(已实测)。去重后 agent 侧仍写 + # SetStatusProvider,而 pluginhost 侧可能同时 buildSDK 读 status ⇒ + # 写-读竞争仍在(实测报 1 条 DATA RACE)。所以 registry 侧的锁才是修复。 - name: 计算 -race 排除表并打印 id: skip run: | @@ -234,7 +252,6 @@ # 列名:粒度|目标|原因|修复单号 (用 | 分隔,原因内可含空格) read -r -d '' TABLE <<'EOF' || true test|internal/plugins/webui|TestOpenAIStreamIsActuallyStreaming|gzipResponseWriter 的 mode/wroteHeader/buf 无锁;handler goroutine(发 finish/[DONE])与增量写 goroutine 并发写同一 ResponseWriter(gzip.go:139 读 mode vs gzip.go:206 写 mode)|WEBUI-GZIP-RACE - pkg|internal/kernelboot/modules|*(整包)|该包把真实整机装起来 ⇒ 第 4 层 agent 与 plugin-host 并行 Init 时,双方都无锁直写同一 plugin.Registry 的 7 个 Set*(SetLuaVM/SetBaseAPIKey/SetSupervisor/SetTracker/SetConfig/SetStageHost/SetIndexer,registry.go:231-284)。竞争在**内核装配**里,不在本包代码里;任何装整机的用例都会撞上|KERNEL-REGISTRY-SETLOCK EOF tests=""; pkgs=""; n=0 @@ -359,34 +376,10 @@ -run 'TestOpenAIStreamIsActuallyStreaming' -count=1 -timeout 5m 2>&1 || true) echo " (参考)本次 DATA RACE 报告数: $(echo "$out" | grep -c 'WARNING: DATA RACE')(偶发,0 不代表已修)" - echo "=== ② kernel: agent↔plugin-host 无锁直写 plugin.Registry(修复单 KERNEL-REGISTRY-SETLOCK)===" - # - # ★ 这里**不跑**偶发竞争来当证据 —— 实测该竞争单步跑 6/6 次撞不到 - # (它要 92 包并发压满才容易命中),拿它当断言必然假绿。 - # 改成**静态断言**:直接查那 7 个 Set* 是否仍是"单行无锁直写"形态。 - # 修好(改成取锁)后形态变化 ⇒ 本条失败 ⇒ 提醒删排除表。因果稳定。 - SETTERS="SetLuaVM SetBaseAPIKey SetSupervisor SetTracker SetConfig SetStageHost SetIndexer" - REG=internal/plugin/registry.go - locked=0; total=0 - for fn in $SETTERS; do - total=$((total + 1)) - if grep -qE "^func \\(r \\*Registry\\) ${fn}\\([^)]*\\)[[:space:]]*\\{[^}]*\\}[[:space:]]*$" "$REG"; then - : # 仍是单行直写(未加锁) - else - echo " 已不再是无锁单行: $fn" - locked=$((locked + 1)) - fi - done - echo " 仍为无锁直写的 Set*: $((total - locked))/$total" - if [ "$locked" -gt 0 ]; then - echo "::error title=内核 setter 已加锁::KERNEL-REGISTRY-SETLOCK 可能已被修复($locked/$total 个 Set* 形态已变)—— 请从 ci.yml 排除表删掉 kernelboot/modules 那一行" - fail=1 - fi - if [ "$fail" -ne 0 ]; then exit 1 fi - echo " 证据核对完成:两个排除项对应的缺陷均仍在(排除表有效)" + echo " 证据核对完成:webui 排除项对应的缺陷仍在(排除表有效)" # ── 交叉编译:可在无 cgo 下构建的客户端/工具 ── #