mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-11-01 14:57:38 +00:00
refactor(kernelboot,kernelmod): ID:13 删死包 kernelboot/modules,整机启动图判据迁到 kernelmod/all
## 决策
删除 internal/kernelboot/modules(registry.go + registry_test.go),
把整机启动图判据迁到 internal/kernelmod/all(真实聚合入口)。
文件头那条 TODO(死包清理) 随包一起消失。
## 为什么这个包该删
实测零生产调用:
· 生产代码里 import 它的包数 = 0
· Register() / BuildModules() / Names() 的外部调用点 = 0
· 它的 Register/BuildModules 是 internal/kernelmod 那套注册表的**第二份实现**
(留着只会让人下次接错口子)
· kernelmod/all/facilities_sync_test.go 的注释里早已写明
「kernelmod 才是真实注册表(模块编译期工厂加载)」
## 判据迁移(能力未削弱,逐条实测)
- 判据 1–4(整机装配/层拓扑/严格逆序关闭/无残留): 只换宿主,
用 kernelmod.Registered() 作为装配输入不变,实测 5 层 / 17 模块 /
关闭顺序 [4 3 2 1 0] / 3 轮 goroutine 差值 0 / 订阅者 2→2
- 判据 5(TestEveryRegisteredModuleIsCompiledIn): 真相源是扫 `..` 源码树的
「目录+类型名 复合键 + 四方法齐全」,与宿主包无关。scanModuleTypes 的
modRoot 从 "../../kernelmod" 改为 ".."(本文件现在在 kernelmod/all/ 下)
- 删掉 `_ ".../kernelmod/all"` 自引用(判据现在与聚合入口同包,
本包被编译即聚合 import 生效 —— 反而更贴近生产同一条 import 链)
## 「新模块登记了但漏 import」仍咬得住(3 个变异亲测)
A 从 all.go 删 multimodal 聚合 import(叶子模块,无人依赖)
→ TestEveryRegisteredModuleIsCompiledIn 红(指名 multimodal)
→ TestEveryModuleDirIsImported 红
B 删 multimodal 的 init()(Register 调用点消失,真相源不能是被测入口)
→ TestEveryRegisteredModuleIsCompiledIn 仍红(源码侧多数一个类型)
C supervisor 的 Name() 改成非字面量
→ 判据显式失败(不静默跳过 ⇒ 不会变空转)
## 悬空引用清零
全仓 grep kernelboot/modules:代码/文档/CI 只剩 ci.yml 里一条
「那个包零生产调用,已删除」的历史说明。同步修正:
· ci.yml「整机启动图判据」步骤 → go test ./internal/kernelmod/all/
· ci.yml -race 排除表 pkg 行目标包 → internal/kernelmod/all
(该包现在装整机 ⇒ 同样会撞 KERNEL-REGISTRY-SETLOCK;
实测不带 -race 6/6 绿,排除仍必要,单号不变)
· ci.yml 三条说明文本(含证据步骤的提示语)
· kernelboot/kernelstate.go 指向旧判据路径的注释
· docs/zh/kernel-modularization-design.md §2.7 的 declarations.go 路径
· check-ci-gates.py §4 断言目标包 → ./internal/kernelmod/all/
(否则它盯的是一个已不存在的包)
## 验证
go build ./... = 0 error,go vet ./... = 0 error
go test ./internal/kernelboot/... ./internal/kernelmod/... = ok
go test -race ./internal/kernelmod/all/ = ok(连跑 6 次无 DATA RACE)
check-ci-gates.py EXIT=0;ID:15 的 verify_v4 矩阵复跑仍 5 变异全抓 + 误报项 EXIT=0 + 基线 EXIT=0
This commit is contained in:
11
.github/scripts/check-ci-gates.py
vendored
11
.github/scripts/check-ci-gates.py
vendored
@ -158,10 +158,15 @@ else:
|
|||||||
if _run_body(_inject(_probe + "|合成探针原因|")).returncode == 0:
|
if _run_body(_inject(_probe + "|合成探针原因|")).returncode == 0:
|
||||||
fails.append("合成行「reason 有效 / ticket 空」竟通过 ⇒ ticket 守卫已失效")
|
fails.append("合成行「reason 有效 / ticket 空」竟通过 ⇒ ticket 守卫已失效")
|
||||||
|
|
||||||
# ── 4. go job:整机启动图判据必须单列一步跑 kernelboot/modules ──
|
# ── 4. go job:整机启动图判据必须单列一步跑 kernelmod/all ──
|
||||||
|
#
|
||||||
|
# ★ 目标包随 ID:13 从 internal/kernelboot/modules(死包,已删)迁到
|
||||||
|
# internal/kernelmod/all(真实聚合入口)。本断言跟着改,否则它盯的
|
||||||
|
# 是一个**已不存在的包** —— 那会变成"永远红"或(更糟)"永远绿"的空转。
|
||||||
|
GO_BOOT_PKG = "./internal/kernelmod/all/"
|
||||||
gojob = step_runs("go")
|
gojob = step_runs("go")
|
||||||
boot = [r for _n, r in gojob if "kernelboot/modules" in r and "go test" in r]
|
boot = [r for _n, r in gojob if GO_BOOT_PKG in r and "go test" in r]
|
||||||
check(bool(boot), "go job 缺少「整机启动图判据」步骤(go test ./internal/kernelboot/modules/)")
|
check(bool(boot), "go job 缺少「整机启动图判据」步骤(go test " + GO_BOOT_PKG + ")")
|
||||||
for r in boot:
|
for r in boot:
|
||||||
check("-count=1" in r, "整机启动图判据步骤必须带 -count=1(不许吃 go test 缓存)")
|
check("-count=1" in r, "整机启动图判据步骤必须带 -count=1(不许吃 go test 缓存)")
|
||||||
|
|
||||||
|
|||||||
15
.github/workflows/ci.yml
vendored
15
.github/workflows/ci.yml
vendored
@ -100,7 +100,7 @@ jobs:
|
|||||||
- name: go test ./...
|
- name: go test ./...
|
||||||
run: go test ./... -count=1 -timeout 20m
|
run: go test ./... -count=1 -timeout 20m
|
||||||
|
|
||||||
# 整机启动图判据(internal/kernelboot/modules/registry_test.go)。
|
# 整机启动图判据(internal/kernelmod/all/bootgraph_test.go)。
|
||||||
#
|
#
|
||||||
# ## 为什么单列一步(而不是只靠上面的 go test ./...)
|
# ## 为什么单列一步(而不是只靠上面的 go test ./...)
|
||||||
#
|
#
|
||||||
@ -111,11 +111,14 @@ jobs:
|
|||||||
# ② 它断言的是**跨模块的顺序与资源回收**,是"多跑几次才暴露"的那类,
|
# ② 它断言的是**跨模块的顺序与资源回收**,是"多跑几次才暴露"的那类,
|
||||||
# 单独一步便于在 flaky 时被识别(这里带 -count=1,不吃缓存)
|
# 单独一步便于在 flaky 时被识别(这里带 -count=1,不吃缓存)
|
||||||
#
|
#
|
||||||
|
# ★ 判据宿主是 internal/kernelmod/all(真实聚合入口),不是以前的
|
||||||
|
# internal/kernelboot/modules(那个包零生产调用,已删除)。
|
||||||
|
#
|
||||||
# ★ 不用 -race(那在 race job 里跑):本判据含真实端口/挂载,
|
# ★ 不用 -race(那在 race job 里跑):本判据含真实端口/挂载,
|
||||||
# -race 的插桩会让时序假设变松,两者分开放更便于定位。
|
# -race 的插桩会让时序假设变松,两者分开放更便于定位。
|
||||||
- name: 整机启动图判据(真实登记表装配 + 逆序关闭 + 无残留)
|
- name: 整机启动图判据(真实登记表装配 + 逆序关闭 + 无残留)
|
||||||
run: |
|
run: |
|
||||||
go test ./internal/kernelboot/modules/ \
|
go test ./internal/kernelmod/all/ \
|
||||||
-count=1 -timeout 10m -v
|
-count=1 -timeout 10m -v
|
||||||
|
|
||||||
# 跨平台客户端版本一致性:内核 internal/meta 是唯一事实源,
|
# 跨平台客户端版本一致性:内核 internal/meta 是唯一事实源,
|
||||||
@ -222,7 +225,7 @@ jobs:
|
|||||||
# ║ ║
|
# ║ ║
|
||||||
# ║ **整包永久排除不被接受。** 唯一出口是给 plugin.Registry 的 Set* ║
|
# ║ **整包永久排除不被接受。** 唯一出口是给 plugin.Registry 的 Set* ║
|
||||||
# ║ 加锁(修复单 KERNEL-REGISTRY-SETLOCK),修好后: ║
|
# ║ 加锁(修复单 KERNEL-REGISTRY-SETLOCK),修好后: ║
|
||||||
# ║ · 删掉本表里 kernelboot/modules 那一行 ║
|
# ║ · 删掉本表里 kernelmod/all 那一行 ║
|
||||||
# ║ · **复跑一次** -race 全仓确认整机判据在 -race 下也稳定 ║
|
# ║ · **复跑一次** -race 全仓确认整机判据在 -race 下也稳定 ║
|
||||||
# ║ · 删掉 evidence 步骤 ② 的静态断言 ║
|
# ║ · 删掉 evidence 步骤 ② 的静态断言 ║
|
||||||
# ║ 这三步没做完,本线不算收口。 ║
|
# ║ 这三步没做完,本线不算收口。 ║
|
||||||
@ -234,7 +237,7 @@ jobs:
|
|||||||
# 列名:粒度|目标|原因|修复单号 (用 | 分隔,原因内可含空格)
|
# 列名:粒度|目标|原因|修复单号 (用 | 分隔,原因内可含空格)
|
||||||
read -r -d '' TABLE <<'EOF' || true
|
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
|
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
|
pkg|internal/kernelmod/all|*(整包)|该包把真实整机装起来(bootgraph_test.go 的整机判据) ⇒ 第 4 层 agent 与 plugin-host 并行 Init 时,双方都无锁直写同一 plugin.Registry 的 7 个 Set*(SetLuaVM/SetBaseAPIKey/SetSupervisor/SetTracker/SetConfig/SetStageHost/SetIndexer,registry.go:231-284)。竞争在**内核装配**里,不在本包代码里;任何装整机的用例都会撞上。★ 目标包随 ID:13 迁到此(原 internal/kernelboot/modules 已删)|KERNEL-REGISTRY-SETLOCK
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
tests=""; pkgs=""; n=0
|
tests=""; pkgs=""; n=0
|
||||||
@ -291,7 +294,7 @@ jobs:
|
|||||||
SKIP="${{ steps.skip.outputs.tests }}"
|
SKIP="${{ steps.skip.outputs.tests }}"
|
||||||
|
|
||||||
# 从全仓包列表里剔除 pkg 级排除项(按路径后缀精确匹配,
|
# 从全仓包列表里剔除 pkg 级排除项(按路径后缀精确匹配,
|
||||||
# 不用 grep 模糊匹配 —— 否则 kernelboot 会把 kernelboot/modules 一起剔掉)。
|
# 不用 grep 模糊匹配 —— 否则 kernelmod 会把 kernelmod/all 一起剔掉)。
|
||||||
PKGS=""
|
PKGS=""
|
||||||
for p in ${{ steps.pkgs.outputs.pkgs }}; do
|
for p in ${{ steps.pkgs.outputs.pkgs }}; do
|
||||||
drop=0
|
drop=0
|
||||||
@ -379,7 +382,7 @@ jobs:
|
|||||||
done
|
done
|
||||||
echo " 仍为无锁直写的 Set*: $((total - locked))/$total"
|
echo " 仍为无锁直写的 Set*: $((total - locked))/$total"
|
||||||
if [ "$locked" -gt 0 ]; then
|
if [ "$locked" -gt 0 ]; then
|
||||||
echo "::error title=内核 setter 已加锁::KERNEL-REGISTRY-SETLOCK 可能已被修复($locked/$total 个 Set* 形态已变)—— 请从 ci.yml 排除表删掉 kernelboot/modules 那一行"
|
echo "::error title=内核 setter 已加锁::KERNEL-REGISTRY-SETLOCK 可能已被修复($locked/$total 个 Set* 形态已变)—— 请从 ci.yml 排除表删掉 kernelmod/all 那一行"
|
||||||
fail=1
|
fail=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@ -736,8 +736,9 @@ loadOne(name):
|
|||||||
|
|
||||||
### 2.7 声明层已落地(Step 4b 第一步,2026-10-16)
|
### 2.7 声明层已落地(Step 4b 第一步,2026-10-16)
|
||||||
|
|
||||||
`internal/kernelboot/modules/declarations.go` 把 **main() 里隐含在语句顺序中的
|
`internal/kernelmod/all` 的聚合 import 把**全部模块引入编译**后,各模块在自己
|
||||||
依赖关系**变成显式声明。实测分层结果:
|
文件中用 Requires() 把 **main() 里隐含在语句顺序中的依赖关系**变成显式声明。
|
||||||
|
实测分层结果(2026-10-24 复核:`go test ./internal/kernelmod/all/ -run TestLayeringYieldsParallelism -v`):
|
||||||
|
|
||||||
```
|
```
|
||||||
6 层 / 17 模块,最宽层 9 个可并行(原来是 17 步严格串行)
|
6 层 / 17 模块,最宽层 9 个可并行(原来是 17 步严格串行)
|
||||||
|
|||||||
@ -80,7 +80,9 @@ type MemoryBundle struct {
|
|||||||
// · 少一层间接 ⇒ 少一处可能出错的地方
|
// · 少一层间接 ⇒ 少一处可能出错的地方
|
||||||
//
|
//
|
||||||
// 顺序正确性由**声明**驱动:`Build()` 校验缺依赖 / 环 / 重名,
|
// 顺序正确性由**声明**驱动:`Build()` 校验缺依赖 / 环 / 重名,
|
||||||
// 判据断言关键顺序(见 internal/kernelboot/modules/registry_test.go 的
|
// `Run()` 在执行每一层之前再核一次"实际已产出的设施"是否涵盖本层依赖
|
||||||
|
// (声明对、但计划层顺序被改坏时也能拦住,见 DependencyNotReadyError)。
|
||||||
|
// 判据断言关键顺序(见 internal/kernelmod/all/bootgraph_test.go 的
|
||||||
// TestLayerTopologyIsRespected:用真实模块登记表装配整机,逐层断言层间有序)。
|
// TestLayerTopologyIsRespected:用真实模块登记表装配整机,逐层断言层间有序)。
|
||||||
//
|
//
|
||||||
// ## ★ 并发
|
// ## ★ 并发
|
||||||
|
|||||||
@ -1,104 +0,0 @@
|
|||||||
// Package modules 是内核模块的**登记表**(不含任何具体模块的实现)。
|
|
||||||
//
|
|
||||||
// ## 设计意图(用户设计)
|
|
||||||
//
|
|
||||||
// > 每个内核模块显式各自声明依赖 … 最先初始化的 core 自然不依赖任何模块,
|
|
||||||
// > 下一步初始化的依赖 core 以此类推
|
|
||||||
//
|
|
||||||
// ## 怎么加一个新模块(这是本设计的**验收标准**)
|
|
||||||
//
|
|
||||||
// ① 新建一个文件(如 modules/mymod.go)
|
|
||||||
// ② 写一个类型,实现 kernelboot.Module(Name/Requires/Provides/Init)
|
|
||||||
// ③ 在同文件的 init() 里调 modules.Register(&MyMod{})
|
|
||||||
// ④ 在 `internal/kernelmod/all/all.go` 加一行空白 import —— **仅此而已**
|
|
||||||
//
|
|
||||||
// **不需要**编辑任何中央清单:依赖写在模块**自己**的 Requires() 里。
|
|
||||||
//
|
|
||||||
// ★ 为何要"防漏 import"的判据(见 `internal/kernelboot/modules/registry_test.go`):
|
|
||||||
// 忘了 import ⇒ 模块**根本不被编译进来** ⇒ 它不参与装配 ⇒ 依赖它的模块报
|
|
||||||
// "缺依赖"(响亮),但**不依赖它的模块会静默少了那个设施**
|
|
||||||
// (如插件注册表没建 ⇒ 插件全部加载不了,而错误信息指向插件而非根因)。
|
|
||||||
//
|
|
||||||
// 判据有两条(缺一不可):
|
|
||||||
//
|
|
||||||
// · `TestEveryRegisteredModuleIsCompiledIn` —— 用 AST 扫源码树里
|
|
||||||
// 「目录+类型名 复合键 + Name/Requires/Provides/Init 四方法齐全」的类型,
|
|
||||||
// 取其 Name() 与登记表做集合比对(独立真相源)。
|
|
||||||
// ★ 刻意**不**扫 `kernelmod.Register(...)` 调用点:删掉某模块的 init() 后
|
|
||||||
// 调用点消失,源码侧与登记表侧会同时少一个 ⇒ 比对照样相等 ⇒ 假绿。
|
|
||||||
// 手写模块名清单同理(清单会跟着漏),两者都不用。
|
|
||||||
// · `TestRealRegistryAssemblesWholeMachine` 等 —— 用真实登记表装配整机、
|
|
||||||
// 断言每个模块都进图且设施就绪、并严格逆序关闭(见同目录测试文件)
|
|
||||||
package modules
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"sort"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/JianFeeeee/HologramAgent/internal/kernelboot"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
regMu sync.Mutex
|
|
||||||
registry []kernelboot.Module
|
|
||||||
seenName = map[string]bool{}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Register 由模块在自己的 init() 里调用(自注册)。
|
|
||||||
//
|
|
||||||
// 重复注册同一模块名会**记录**并在 BuildModules 时报错 —— 不用 panic:
|
|
||||||
// panic 发生在 init 阶段,栈信息对使用者毫无意义(他只想知道哪个模块重名)。
|
|
||||||
func Register(m kernelboot.Module) {
|
|
||||||
if m == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
regMu.Lock()
|
|
||||||
defer regMu.Unlock()
|
|
||||||
if seenName[m.Name()] {
|
|
||||||
dupErr = append(dupErr, m.Name())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
seenName[m.Name()] = true
|
|
||||||
registry = append(registry, m)
|
|
||||||
}
|
|
||||||
|
|
||||||
// dupErr 累积重名模块(BuildModules 时统一报告)。
|
|
||||||
var dupErr []string
|
|
||||||
|
|
||||||
// BuildModules 返回全部已登记模块(按名字排序,保证可复现)。
|
|
||||||
//
|
|
||||||
// ## 为什么排序
|
|
||||||
//
|
|
||||||
// 分层结果的**层内顺序**若依赖 map/切片顺序,同一份声明两次构建会得到
|
|
||||||
// 不同顺序 ⇒ 不可审计(设计 §2.5:图要能进 PR diff 比对)。
|
|
||||||
// 排序让结果稳定。
|
|
||||||
func BuildModules() ([]kernelboot.Module, error) {
|
|
||||||
regMu.Lock()
|
|
||||||
defer regMu.Unlock()
|
|
||||||
if len(dupErr) > 0 {
|
|
||||||
names := append([]string(nil), dupErr...)
|
|
||||||
sort.Strings(names)
|
|
||||||
return nil, fmt.Errorf("kernelboot/modules: 模块名重复注册: %v"+
|
|
||||||
"(同一个模块被 import 了两次?或两个模块取了同名)", names)
|
|
||||||
}
|
|
||||||
out := append([]kernelboot.Module(nil), registry...)
|
|
||||||
sort.Slice(out, func(i, j int) bool { return out[i].Name() < out[j].Name() })
|
|
||||||
if len(out) == 0 {
|
|
||||||
return nil, fmt.Errorf("kernelboot/modules: 没有任何模块被登记 —— " +
|
|
||||||
"通常是没有 import 模块包(见本文件顶部的'怎么加一个新模块')")
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Names 返回已登记模块名(诊断/判据用)。
|
|
||||||
func Names() []string {
|
|
||||||
regMu.Lock()
|
|
||||||
defer regMu.Unlock()
|
|
||||||
out := make([]string, 0, len(registry))
|
|
||||||
for _, m := range registry {
|
|
||||||
out = append(out, m.Name())
|
|
||||||
}
|
|
||||||
sort.Strings(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
@ -1,29 +1,21 @@
|
|||||||
package modules
|
package all
|
||||||
|
|
||||||
// 「真实登记表 → 整机启动图」的判据(2026-10-24)。
|
// 「真实登记表 → 整机启动图」的判据(2026-10-24)。
|
||||||
//
|
//
|
||||||
// ╔══════════════════════════════════════════════════════════════════════╗
|
// ## 迁移记录(ID:13,2026-10-24)
|
||||||
// ║ ★★ 本包(internal/kernelboot/modules)当前**零生产调用**,是死包。 ║
|
|
||||||
// ║ ║
|
|
||||||
// ║ 实测(2026-10-24): ║
|
|
||||||
// ║ · 生产代码里 import 本包的包数 = 0 ║
|
|
||||||
// ║ · BuildModules() / Names() / Register() 的外部调用点 = 0 ║
|
|
||||||
// ║ · 真正的内核模块登记表是 internal/kernelmod(由 kernelmod/all ║
|
|
||||||
// ║ 聚合 import,cmd/homed/kmod_runtime.go 消费) ║
|
|
||||||
// ║ ║
|
|
||||||
// ║ ⚠ 不要把本包当成主路径。它里面的 Register/BuildModules 是 kernelmod ║
|
|
||||||
// ║ 那套注册表的**第二份实现**,留着只会让人下次接错口子。 ║
|
|
||||||
// ║ ║
|
|
||||||
// ║ 本文件为什么仍挂在这里:判据用真实登记表(kernelmod.Registered)装配 ║
|
|
||||||
// ║ 整机,是**占位**位置;挪去 internal/kernelmod/all/ 并删除本包, ║
|
|
||||||
// ║ 需要单独一单(避免与本线混在一起,见下方 TODO)。 ║
|
|
||||||
// ║ 去留决策与执行:另开一单(判据迁 kernelmod/all + 删本目录 + ║
|
|
||||||
// ║ 同步改 registry.go / kernelstate.go / docs 的指引)。 ║
|
|
||||||
// ╚══════════════════════════════════════════════════════════════════════╝
|
|
||||||
//
|
//
|
||||||
// TODO(死包清理):本包零生产调用。判据已在此跑通并合入;把判据迁到
|
// 本文件原先挂在 `internal/kernelboot/modules/registry_test.go`,那个包
|
||||||
// internal/kernelmod/all/(真实聚合入口、已有同族判据)后删除整个目录。
|
// **零生产调用**(死包:Register/BuildModules 是 kernelmod 那套的**第二份
|
||||||
// 触发条件:本线合入 main 后另开单。在那之前**不要**新增对本包的生产依赖。
|
// 实现**,外部调用点 = 0),只为了安放这组判据才留着。
|
||||||
|
//
|
||||||
|
// 现迁移到**真实聚合入口** `internal/kernelmod/all`:本包本来就是
|
||||||
|
// 「把全部模块 import 进来」的那一层,判据放在这里,用的就是生产路径
|
||||||
|
// 同一条 import 链(all → 各模块 init → kernelmod.Register)。
|
||||||
|
// 死包与其第二份注册表实现已删除。
|
||||||
|
//
|
||||||
|
// ★ 迁移没有削弱任何判据的能力:下面判据 5 的真相源(扫 `..` 源码树的
|
||||||
|
// 类型大小)与宿主包无关,换宿主前后都照样咬得住「登记了但漏 import」。
|
||||||
|
// 变异亲测见本文件尾部「失败面」一节第 1–3 条。
|
||||||
//
|
//
|
||||||
// ## 这个文件补的是什么(此前完全没有的覆盖)
|
// ## 这个文件补的是什么(此前完全没有的覆盖)
|
||||||
//
|
//
|
||||||
@ -136,11 +128,10 @@ import (
|
|||||||
internalConfig "github.com/JianFeeeee/HologramAgent/internal/config"
|
internalConfig "github.com/JianFeeeee/HologramAgent/internal/config"
|
||||||
"github.com/JianFeeeee/HologramAgent/internal/kernelboot"
|
"github.com/JianFeeeee/HologramAgent/internal/kernelboot"
|
||||||
"github.com/JianFeeeee/HologramAgent/internal/kernelmod"
|
"github.com/JianFeeeee/HologramAgent/internal/kernelmod"
|
||||||
|
// ★ 判据与被测对象现在同属一个包(internal/kernelmod/all):
|
||||||
// ★ 真实登记表的**唯一入口**:聚合 import 决定"哪些模块被编译进来"。
|
// 本文件就在聚合 import 的**那一层**里。因此不需要(也不能)再
|
||||||
// 本判据刻意用与 `cmd/homed/kmod_runtime.go` 完全相同的入口
|
// 空白 import 自己 —— 只要本包被编译,all.go 的聚合 import 就已生效。
|
||||||
// (若那里漏了 import,这里同样会漏 ⇒ 两边一起红,而不是判据自说自话)。
|
// 这正是迁移的收益之一:判据用的是生产同一条 import 链,而非旁路。
|
||||||
_ "github.com/JianFeeeee/HologramAgent/internal/kernelmod/all"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ─────────────────────────── 整机夹具 ───────────────────────────
|
// ─────────────────────────── 整机夹具 ───────────────────────────
|
||||||
@ -668,7 +659,7 @@ func TestShutdownLeavesNoResidue(t *testing.T) {
|
|||||||
// · 删 all/all.go 任一聚合 import ⇒ 红(该类型四方法仍在 ⇒ 源码侧多一个)
|
// · 删 all/all.go 任一聚合 import ⇒ 红(该类型四方法仍在 ⇒ 源码侧多一个)
|
||||||
// · 删掉整个模块目录 ⇒ 反向断言红(登记表里有、源码树里没有)
|
// · 删掉整个模块目录 ⇒ 反向断言红(登记表里有、源码树里没有)
|
||||||
func TestEveryRegisteredModuleIsCompiledIn(t *testing.T) {
|
func TestEveryRegisteredModuleIsCompiledIn(t *testing.T) {
|
||||||
const modRoot = "../../kernelmod"
|
const modRoot = ".."
|
||||||
fromSource := scanModuleTypes(t, modRoot)
|
fromSource := scanModuleTypes(t, modRoot)
|
||||||
if len(fromSource) == 0 {
|
if len(fromSource) == 0 {
|
||||||
t.Fatal("★ 源码扫描一个模块都没找到 —— 扫描逻辑失效(判据会永远绿)")
|
t.Fatal("★ 源码扫描一个模块都没找到 —— 扫描逻辑失效(判据会永远绿)")
|
||||||
@ -86,7 +86,7 @@ func emptyState(t *testing.T) *kernelboot.KernelState {
|
|||||||
// 取真实 plan 的**第一层**单独跑:模块(config / kernel-api)本来就绪条件
|
// 取真实 plan 的**第一层**单独跑:模块(config / kernel-api)本来就绪条件
|
||||||
// 为空,若能通过 ⇒ 校验没把"合法的第 0 层"当成缺依赖。
|
// 为空,若能通过 ⇒ 校验没把"合法的第 0 层"当成缺依赖。
|
||||||
//
|
//
|
||||||
// (整机正序不误报由 internal/kernelboot/modules 的整机判据覆盖:
|
// (整机正序不误报由 internal/kernelmod/all 的整机判据覆盖(bootgraph_test.go):
|
||||||
// 它用真实 plan 跑完全部 5 层,装配成功后资产/关闭/泄漏全部断言。
|
// 它用真实 plan 跑完全部 5 层,装配成功后资产/关闭/泄漏全部断言。
|
||||||
// 这里只做最小正向基线,避免重复一份整机夹具。)
|
// 这里只做最小正向基线,避免重复一份整机夹具。)
|
||||||
func TestRunRuntimeGuardDoesNotFireOnCorrectPrefix(t *testing.T) {
|
func TestRunRuntimeGuardDoesNotFireOnCorrectPrefix(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user