Files
HomeAgent/docs/zh
JianFeeeee 7cef7df6f2 test: 判据改为面向接口输入输出 + 文档立规 + CI 补缺口
本提交是三件事的第一批,后续按计划分步推进。

## 一、判据改造(两条实证)

### 1. turn_hint 判据原是自证循环

原测试在测试里**手动重放**了真实注入逻辑:

    f.Msgs = a.buildMessages(...)
    hint := stageHint(f.StageCtx, "turn_hint")
    f.Msgs = append(f.Msgs, Message{Role:"system", Content:"[Turn hints] "+hint})
    // 然后断言"最后一条是 system、内容含 [Turn hints]"

它断言的是**它自己刚 append 的那一行** —— 把 task.go:656-661 抄进测试
再验证抄得对不对。真实代码那段删掉,它照样绿。

改为:把 turn_hint 放进 StageCtx,跑**真实的 stepPrepare**
(newTaskFrame 起始就是 StepPrepare),检查它产出的 Msgs。

变异验证:删掉 task.go 里 `if hint := stageHint(...)` 那段 ⇒ 实测红。

### 2. webui 文件下发判据原是白盒

原测试直接调未导出的 `stageWebFile(src, isImage)`、断言 `webFilesDir`
包级变量 —— 那都是**实现方式**。

改为从**输出通道 handler** 走(`RegisterOutputChannel("webui", …)`,
agent 发图/发文件时走的就是它):

    输入:{payload: "<路径或URL>", type: "image"|"file"}
    输出:{status:"ok", url:"/files/<name>.<ext>", size:N}

4 条判据(新版 handler_files_contract_test.go):
  · 图片 → 可下载 URL(前缀/扩展名/size)
  · 远端 URL 原样透传(不下载)
  · 危险扩展名被中和(.html 保留会在 webui 域执行脚本)
  · 空 payload 被拒

变异验证:丢扩展名 ⇒ 红;远端也拷贝 ⇒ 红。

## 二、文档立规:engineering-lessons 新增第五节

原来只有「判据会骗人」,没有「判据本身形状错了」。新增 6 小节:

    5.1  判据必须把被测模块当黑箱(含"如果重写内部实现、接口不变,
         这条判据还会绿吗"这个可操作判据)
    5.2  禁止读源码做断言(附正确替代表)
    5.2.1 判据不得以文档为依据
    5.3  测试文件与业务代码分离(现状:521 文件 / 11.2 万行,
         agent/core 一个包 196 文件里 144 个是测试)
    5.4  CI 必须覆盖每个独立 module
    5.5  CI 与本地必须跑同一套门

§5.2.1 的直接触发:`interrupt_invariant_test.go` 里有一条判据写着
「事实清单(来自探测,见 docs/zh/interrupt-subsystem-split-plan.md §1.5)」
—— 被引用方自己标着「状态:计划(未实施)」,而那条判据的实际断言
只是"入队后队列里 1 条",与声称的"反向依赖现状快照"毫无关系。
该判据已删除(入队行为已由 scheduler_preempt_test 的黑箱断言覆盖)。

## 三、CI 补两个缺口

### gofmt 门

提交纪律要求 `gofmt -l` 干净,但三个 workflow 里 `gofmt` 出现 **0 次**
—— 纪律只靠人自觉。补上后未格式化的代码进不了主干。

只查本仓自管目录:SDK 是独立仓,格式由它自己的 CI 管。

### SDK 判据步骤

`third_party/hologramagentsdk/` 有自己独立的 go.mod:

    go list ./... | grep third_party   →  0 个

主仓 `go test ./...` **永远不包含** SDK 任何测试。实测 SDK 有 30 个判据
文件,主仓 CI 一次都没跑过。

补的步骤只跑主仓 git 跟踪的文件(CI 上只有 22 个 vendored 文件,
工作机上是完整独立仓)—— 实测 `git archive` 出 22 个文件后
`go test ./sdk/ ./meta/` 全绿。

## 四、SDK 仓测试迁移(独立仓,已单独提交 3a38ef5)

`sdk/{plugin,proxy,stress}_test.go` → `sdk/test/`,包名 `sdk` → `test`。

关键:原测试直接写 `&PluginSDK{regStage: X, name: "test"}` —— **私有字段**。
而 `New(name, sett, regTool, regStage, regAPI, regOutput)` 本来就把所有
registrar 作为参数,等价且是生产路径。共改 19 处,0 处剩余。

主仓同步更新了跟踪路径(这三个文件主仓也 vendor 了一份)。

## 验证

    go build ./...            PASS
    go vet ./...              PASS
    gofmt -l                  干净
    go test ./... -count=1    62 包全过,0 FAIL
    干净检出(worktree)       build + 改造后的判据全绿

## 后续(按计划分步)

  步骤 2  webui 29 个测试分类迁移(已实测出精确分类)
  步骤 3  agent/core 739 处白盒引用先分类再动
  步骤 4  SDK tools/hmapdev(package main,无法迁出,只加说明)
  步骤 5  CI 补 coverage + staticcheck
  步骤 6  文档双语(需定术语口径)
2026-11-04 18:11:06 +08:00
..

docs/zh 文档索引

建立于 2026-10-31。目的:让读者知道该信哪一篇。

此前本目录混着三种时态 —— 当时实测、历史推演、执行计划 —— 外观完全相同。 曾有文档写着「main.go 仍走串行代码」而实际早已接完(滞后一整轮发布)。


一、本目录只放四类文档

类型 回答什么问题 有长期价值 命名
设计文档 为什么这样设计 ✅ *-design.md
操作手册 怎么做 ✅ *-runbook.md
教训 踩过什么坑 ✅ engineering-lessons.md
未做登记 还剩什么没做 ✅ future-plan.md

❌ 不放:执行计划

执行计划(*-plan.md / *-checklist.md)会随实施完结变成误导性读物 —— 它记录"当时打算做什么",而读者会读成"现在是什么"。

2026-10-31 删除了 9 份已完结的计划,删前把两类内容提出来:

内容 去向
决策与教训 engineering-lessons.md
未完成的项 future-plan.md
实测数据 并入对应设计文档(如压测数据进 toolcall-contract-and-sequence-design.md 附录 A)

唯一例外:legacy-table-retirement.md 与 interrupt-subsystem-split-plan.md 保留 —— 前者被 4 处生产代码注释当作退役依据引用,后者记录的探测事实仍被判据引用。 它们不是"执行计划",是依据。


二、怎么判断一篇文档的可信度

每篇首部都有状态行,取值只允许这四种:

状态 含义 能当事实用吗
已实现并部署 描述与代码一致,关键处给了取数命令 ✅ 直接信
部分实现 先说清哪部分生效(附实测调用点) ⚠️ 只信标了"生效"的
设计 / 未实现 是方案,代码里可能没有 ❌ 只能当设计意图
归档说明 历史记录,故意保留当时写法 ❌ 不要改、也不要信

判据名的可信度规则

文档里的 TestXxx 不保证现在还存在:

  • 已实现并部署类文档里的 → 应当存在。找不到就是缺陷
  • 设计 / 未实现类文档里的 → 是规划名,可能从未实现
  • 归档说明类文档里的 → 记录当时事实,可能已改名或删除

真实案例:BootCtx 改名 KernelState 后, TestBootCtxCloseAllIsReverseOrder → TestKernelStateCloseAllIsReverseOrder, 文档没跟上。

核对命令:

for n in $(grep -rhoE '`Test[A-Z][A-Za-z0-9_]+`' docs/zh/*.md | tr -d '`' | sort -u); do
  grep -rq "func $n(" --include="*_test.go" internal cmd 2>/dev/null || echo "缺失: $n"
done

有缺失不一定是缺陷,先看它出在哪类文档里。

当前基线(2026-10-31):27 条缺失,全部已知:

篇数 文档 性质
18 commondev-*.md 「设计 · 未实现」类,判据名是规划名
7 kernel-modularization-design.md §2.7.3 的对照表左栏(有意的历史记录)
2 multimodal-space.md 视频能力未纳入契约,判据为规划名

⇒ 在「已实现」类文档正文里找不到,才是缺陷。


三、文档清单

3.1 设计文档 —— 可以直接当事实

文档 内容
kernel-modularization-design.md 装配架构权威:依赖声明、BFS 分层、Group 0、实例化粒度
kernel-secret-firewall-design.md 安全防火墙(部分实现,见文首接线状态表)
input-scheduler-design.md 两类别输入 + L1–L4 中断 + 可抢占
resident-subagent-design.md 驻留子:轻量内核、两级记忆、父子 L3/L4
toolcall-contract-and-sequence-design.md 工具调用契约(附录 A 是压测数据)
commondev-design.md 主管式多仓库开发流水线(设计 · 未实现)
commondev-agent-runtime-design.md commondev 运行时与内核接入
context-overflow-l4-design.md 上下文超页 L4(归档,术语过时)
triple-distill-llm-design.md 三元组蒸馏(已实现并部分否决)

3.2 操作手册

3.3 参考 / 实测报告

文档 内容
engineering-lessons.md 踩过的坑与共同形态(推荐先读)
kernel-performance.md 性能实测基线
production-recall-validation.md 生产召回验证(「探针 PASS 但结论是假的」)
recall-as-association.md 召回作为关联
recall-capability-tiers.md 召回能力分层(含 2 个未补缺口)
multimodal-space.md 多模态向量空间
vector-provider-decision.md 向量 provider 选型(已定)
abstention-criterion.md 弃答判据
triple-extract-attention.md 三元组抽取与注意力

3.4 依据类(不是计划,是退役/拆分的判断依据)

文档 为什么保留
legacy-table-retirement.md 被 4 处生产代码注释引用;剩余读方清单在 §八/§十二
interrupt-subsystem-split-plan.md 探测事实被判据引用;拆分本身未实施

3.5 归档说明 —— 历史记录,不要改也不要信

归档文档里的旧工具名(memory_recall 等)故意不改 —— 它们是证据快照, 改了就无法判断"这条结论是在哪个版本上得出的"。

3.6 单一登记处

文档 内容
future-plan.md 全仓唯一的「尚未实现 / 已知缺口」登记处

四、写文档的三条纪律

  1. 数字类断言必须给取数命令(如模块数用 go run ./cmd/homed-depgraph -print),不要凭记忆。 本文档集写错过一次(17 模块 vs 实际 20)。

  2. 区分「机制存在」与「接线生效」。本项目头号缺陷形态是 机制写对了、判据全绿、生产里零调用方。描述能力时写实测的生产调用点。

  3. 判据要能红。写判据时先问:把对应实现删掉,这条判据会红吗? 本仓踩过两次:security_toolgate_test.go 手动调 SetSecurity 把缺失的接线补上了;retention_test.go 手写合规文件名, 从没跑过真实产物。两者都永远绿。

详见 engineering-lessons.md。