|
|
3864f36992
|
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
|
2026-10-24 12:56:37 +08:00 |
|
|
|
653c87f5f2
|
fix(ci): ID:15 排除表守卫改为「注入合成行真跑」,杜绝形似而无效的假绿
原判据(efe0cf1)剥注释后查 token:`${reason// /}` + `exit 1` 都在、提示文案都在,
但把 if 条件改成 `&& false` 后条件恒假、步骤实跑 EXIT=0,判据仍然绿 —— 假绿。
改为从 race job 里抽出「计算 -race 排除表并打印」步骤的 run 体,在临时 bash 里真跑三次:
1. 原样 → 必须 EXIT=0(正向基线)
2. 注入 reason 空的合成行 → 必须 EXIT=1
3. 注入 ticket 空的合成行 → 必须 EXIT=1
两条守卫各用只违反自己的输入单独打,不用一条顶另一条的包。
用合成行而非真实 webui 排除项 ⇒ 该排除项修好后被删掉也不会误报。
验证矩阵(verify_v4.py,5 变异 + 1 误报 + 基线):
✓ reason 守卫失效(token 全在 + && false) EXIT=1
✓ ticket 守卫失效(token 全在 + && false) EXIT=1
✓ 删 cmd/gui 剔除 EXIT=1
✓ 启动图判据步去掉 -count=1 EXIT=1
✓ race 退回写死两包 EXIT=1
✓ 删 webui 排除行(误报检查) EXIT=0
✓ 还原后基线 EXIT=0
go build ./... = 0 error,go vet ./... = 0 error
|
2026-10-24 12:41:51 +08:00 |
|
|
|
efe0cf119b
|
fix(ci): 排除表断言从「查提示文案」改为「查可执行判空逻辑」
## 缺陷(真实存在,已实测复现)
check-ci-gates.py 里「排除表每项必须写原因」这条断言原本写成:
check(any("没有写原因" in r for _n, r in race), ...)
它只检查**提示文案还在**,完全不看校验逻辑本身。
实测取证:把 ci.yml 第 245 行的
if [ -z "${reason// /}" ]; then
改成
if false; then
(提示文案一字未动、校验实际彻底失效):
· 该步骤实跑 EXIT=0 ——「无原因必须失败」这条保证彻底破掉
· 而 check-ci-gates.py 仍然通过(GATES_EXIT=0)
也就是说:判据被文案顶包了。这正是本脚本 docstring 里批判的
「run: | 里的 # 是字符串不是注释 ⇒ 注释能骗过断言」,自己又犯了一次。
## 修法
断言改为在**剥掉 shell 注释后的可执行行**上,同时要求:
· `${reason// /}`(判空变量)存在
· `exit 1` 存在
只有文案、没有真逻辑 ⇒ 不通过。
## 验证(先证能红,再证不误伤)
注入 if false 变异(保留文案)→ 修复前 EXIT=0(漏),修复后 **EXIT=1**(咬住)
还原 → EXIT=0
回归另两个变异(去掉 cmd/gui 剔除、去掉 -count=1)→ 均仍 EXIT=1,未被这次修改削弱
## 门禁
go build ./... = 0|go vet ./... = 0|go test ./internal/kernelboot/modules/ = ok
check-ci-gates.py = 0|gofmt 本线文件干净
边界:只改这一个脚本文件,未动 ci.yml、未动业务逻辑;未 push、未开 PR。
|
2026-10-24 11:59:00 +08:00 |
|
|
|
ed8f28ea6c
|
test(kernelboot,ci): 用真实启动图装配整机补判据,race 门由写死两包扩到全仓
背景:老大指出「CI 测试不够灵活、覆盖不到内核方方面面,应从启动图入手」。
先定位 CI 失败:六个门禁在基线上本地全绿,复现不出红 —— 实质是**覆盖面太窄**
而非配置写错(原 race 门只跑 ./internal/agent/core/ 与 ./cmd/waiter/ 两个包,
kernelboot 下 scheduler/syncbus/stress 的并发用例全在裸奔,绿是因为没测到)。
## 一、新增 internal/kernelboot/modules/registry_test.go(913 行,5 条判据)
kernelboot 自身判据很密(图编解码/分层/调度/迁移回滚/并发压测),但**全部用
假模块**在图上验算法。「真实 17 个模块按真实登记表装配成整机、逐层启动、
再严格逆序关闭」一条用例都没有。这中间藏着一整类失败:算法对、单模块对,
但装配起来不对(漏挂模块 / 某层关闭函数没登记 / 关闭顺序反了)。
5 条判据(零假模块,全部基于 kernelmod.Registered() 的 17 个真模块):
- TestRealRegistryAssemblesWholeMachine:每模块进图、设施真就绪(不是"登记过")
- TestLayerTopologyIsRespected:层间严格有序、层内两两无依赖、分层真带来并行
- TestShutdownIsStrictlyReverseLayerOrder:关闭严格逆序(实测哨兵序 [4 3 2 1 0])
- TestShutdownLeavesNoResidue:连续启停 3 轮,goroutine 回基线 + 临时目录删净
+ syncbus 订阅者不增长
- TestEveryRegisteredModuleIsCompiledIn:源码树扫描与登记表**双向**集合比对
### 判据 5 的真相源为何是"类型+四方法集"而不是 Register 调用点(初版栽过)
初版扫 `kernelmod.Register(...)` 调用点。实测:删掉叶子模块 multimodal 的
init() 后,**5 条判据全绿** —— 调用点消失 ⇒ 源码侧与登记表侧同时少一个 ⇒
集合比对照样相等。而"忘记初始化注册"恰是它声称要抓的场景。
根因是把「要检测的对象」当成了「检测的入口」,入口一被破坏就失去视野。
改成扫「实现了 Name/Requires/Provides/Init 四方法的类型」:模块哪怕不
Register、不被 import,四个方法仍写在源码里。
坑:本仓大量类型都叫 Module,只按类型名做键会撞成一堆(实测撞到只剩 2 个)
⇒ 用「目录+类型名」复合键;agent 是指针接收者 *Module,要处理 StarExpr。
修后恰扫出 17 个类型,与登记表 17 个名字一一对应,零误报。
### 失败面逐条实测(不是注释声称)
M1 删叶子模块 init() → 只有判据 5 红(1–4 全绿,这正是它存在的理由)
M2 CloseAll 逆序改正序 → 判据 3 红
M3 删聚合 import → 判据 5 红
其余:跳过某层会让内核 panic(llm 拿到 nil LuaVM,SIGSEGV);层顺序改逆序
红在装配失败(依赖数据为 nil)而非顺序断言 —— 这两种能发现但报错可读性差,
已写入文件注释,修它属业务改动(另派)。
## 二、.github/workflows/ci.yml
- race 门:写死两包 → `go list ./...` 全仓动态枚举(剔除 cmd/gui,
枚举 <50 包则响亮失败),实测 92 包
- 新增 -skip 排除表:每项必须写「原因 + 修复单号」,无原因该步骤自身失败;
每次 CI 打印完整清单,避免"排除项静默腐烂"
- 新增「已知红的可核对证据」步:用**静态断言**检查两处已知缺陷是否仍在
(webui gzipResponseWriter 无锁、kernel Set* 无锁直写)。
不用"DATA RACE 报告数>0"当断言 —— 实测这些竞争偶发(正向 0 条、
另一次 8 条),那样必然假绿。
- cross 门:cmd 清单由写死 → 逐包 `CGO_ENABLED=0 go build` 实测枚举
(实测 4 个纯 Go / 8 个依赖 cgo;homed-key-rotate 一直是纯 Go 却从没进过
清单,白少一个覆盖);矩阵补 windows/arm64 与 linux/386
- go job:单列「整机启动图判据」一步(带 -count=1,便于失败定位)
## 三、新增 .github/scripts/check-ci-gates.py(180 行)
官方复核时发现:第一版判据只跑 gofmt/build/vet/go test,**完全不校验 ci.yml**,
而 ci.yml 正是改动占比最大的部分 —— 用「删掉 cmd/gui 剔除」这个变异咬出了该缺口。
本脚本用 YAML 解析断言 6 类门禁机制仍在,并给每个 run 步骤做 bash -n。
关键细节:`run: |` 块里的 `# ...` 是**字符串不是 YAML 注释**,`"cmd/gui" in run`
会被一句说明性注释满足 —— 真把 grep -v 删掉断言仍绿。故剥 shell 注释后再断言,
并给剥注释函数**本身**加了自测。
## 四、两处纯注释纠偏
- modules/registry.go:文档写「防漏 import 判据见 registry_test.go」(main 上该
文件当时不存在)、第④步指向 `modules/imports.go`(真实位置是
internal/kernelmod/all/all.go)。已纠正并落地为两条判据的指引。
- kernelboot/kernelstate.go:把「判据见 modules」具体化为函数名。
## 门禁(全部 -count=1,无缓存)
go build ./... = 0
go vet ./... = 0
go test ./... = 0(59 ok / 0 FAIL)
go test -race 91 包 = 0(58 ok / 0 FAIL)
check-ci-gates.py = 0
证据步骤正向 = 0;反向(给 SetLuaVM 加锁)= 1(证明是活断言不是死代码)
## 已知红(均待另开线,本线不越界改业务)
1. 内核:agent.wirePluginSDK 与 pluginhost.injectBaseDeps 在第 4 层并行重复
写同一批 plugin.Registry 字段,而 Set* 全不取锁(r.mu 存在但没用)。
实测偶发约 4%(1/25)。当前以 pkg 级排除临时挡住,**定性为临时手段**;
唯一出口是给 Set* 加锁(修复单 KERNEL-REGISTRY-SETLOCK)。
2. webui:gzipResponseWriter 的 mode/wroteHeader/buf 无锁,handler goroutine
与增量写 goroutine 并发写同一 ResponseWriter(修复单 WEBUI-GZIP-RACE)。
3. internal/agent/core TestStress_Nesting... 时序偶发(-race 放大 300ms 假设,
无 DATA RACE 报告),未 skip,复现命令记在 ci.yml 注释里。
边界:未改任何内核业务逻辑(两个业务文件 diff 逐行只有注释);未 push、未开 PR。
|
2026-10-24 11:30:51 +08:00 |
|
|
|
777f2f810d
|
fix(ci): gitcode 目标仓前置检查加有界重试(网络抖动不再误判)
2026-10-21 的 main 流水线(dfc4e16)里,「前置检查 gitcode 目标仓是否存在」
首次执行时 gitcode 没有给出预期响应,一次 ls-remote 判失败 ⇒ sync job 红;
重跑立刻全绿。把一次网络抖动翻译成「镜像同步坏了」,是纯噪声。
改法:只对**网络层**失败重试,其余立即失败。
- 网络层(DNS/连接重置/TLS/超时/EOF)→ 最多 3 次,退避 5s、10s
- 仓不存在(gitcode 实测:could not be found + **403**,不是 404)
→ 立即失败,不重试。重试只会把「去建仓」拖成十几秒空等
- 凭据问题(401/403 但不带 not-found 特征)→ 立即失败,不重试
(token 错了等 15 秒也不会变对)
- 分类顺序上 notfound 必须先于 403:gitcode 的「仓不存在」响应里
**同时**含 could not be found 与 error: 403,先判 403 会把真问题贴上
「凭据问题」的错误标签
- 每次尝试打一行「第 N/M 次探测失败(类别):摘要」;重试提示打到 stderr
(⏳ … 秒后重试),Actions 日志里一眼看得出发生过重试
- 耗尽重试时整段回放每次尝试的摘要,并明确写「判定:网络问题」
—— 与「仓不存在」/「凭据问题」的判定措辞互不相同,可直接 grep 区分
同口径:**抽成 .github/scripts/check-gitcode-repo.sh,两个 workflow 共用同一个
文件**,而不是两边各写一份「逐字一致」的脚本 —— 后者迟早会漂。ci.yml 的
sync-gitcode-main 与 release.yml 的 sync-gitcode 各留 1 行 bash 调用。
(抽得出公共片段,故没有采用「两边逐字改」的方案。)
token 安全:GC_URL 含 token,永不 echo;所有输出先过 redact()。脱敏两道:
先按字面量遮 token(BRE 元字符先转义),再按 URL 结构遮 userinfo@。
结构式的 password 段**不能排除 `/`** —— 真实 token 可以含 `/`,第一版写成
[^@/[:space:]]* 时 `https://user:tok/en@host` 匹配不上,只剩字面量一道兜住
(判据 C6 首次就是红的)。
判据与变异(9 组正向 / 8 条变异,全部咬住):
判据脚本与变异脚本见 commondev 台账 criteria/b7-gitcode-precheck-retry{,.mutations}.sh。
正向覆盖:重试次数上限(恰好 3 次探测)、仓不存在时 1 次即失败无重试痕迹、
首次成功无重试噪声、抖动后第 2 次成功、凭据问题不重试、token 不进日志、
默认退避真的是 5s/10s(用假 sleep 记录入参,不真等 15 秒)、
两处 workflow 都调同一脚本事 yaml 可解析 + bash -n。
变异 8/8 咬住:默认次数 3→1、退避→0、仓不存在也重试、去掉上限、
去掉重试提示、清空 notfound 特征串、去掉结构式脱敏、ci.yml 退回内联 ls-remote。
过程中修掉三处自己判据的盲区(都不是改产物去迁就判据,而是补判据):
1. M2(退避→0/0)首次未被咬住 ⇒ 补 C7「默认退避 = 5 10」(假 sleep 记录入参)
2. M7(删结构式脱敏)首次未被咬住 ⇒ 补 C7b:userinfo 密码用**别的** secret
且**含 `/`**。C6 用当前 token 的字面量做输入,字面量替换本身就能抹掉,
于是结构式规则删了也照样绿;且密码不含 `/` 时兜底的裸 user@ 规则会顶上,
同样验不出来
3. C7b 第一版断言 grep 'JianFeeeee' 会误报 —— 脚本正常输出里就有
gitcode.com/JianFeeeee/HologramAgent(那是路径不是凭据),改为断言
userinfo 形态(://JianFeeeee[:@])
真实网络实测(不是只跑假 git):
- 仓存在:目标仓可访问,rc=0
- 仓不存在:1 次探测即失败,日志含 could not be found + 403,rc=1
- 网络失败:3 次探测 + 2 次退避,总耗时 5.7s(退避调成 1s/1s),rc=1
yaml.safe_load 三个 workflow 全部可解析;bash -n 通过。
未改动(留待裁决):deploy/scripts/upload_assets.py docstring 里
「SDK 仓传 JianFeeeee/homeagent-sdk」指 gitcode 侧、与实测一致
(gitcode: homeagent-sdk=200 / homeagentsdk=404;GitHub 相反),保持原样。
|
2026-10-21 23:46:01 +08:00 |
|