跳转至

Flyto Agent - 细节补齐清单

归档: 已完成的 815 项见 TODO_DONE.md. 本文件只列当前未完成的 11 项, 加速日常读取.

每个细节都是"专业"和"玩具"的差距.逐一补齐,不留历史债.

优先级说明

  • 🔴 P0:安全/准确性 - 不修就是漏洞
  • 🟡 P1:功能完整性 - 不修就是残缺
  • 🟢 P2:体验/性能 - 不修就是粗糙
  • ⚪ P3:扩展性 - 不修就是僵硬

模块 0:可观测性基础设施 ✅

0.1 EngineObserver 接口体系 ✅

0.2 默认实现 ✅

0.3 StrictMode 严格模式 ✅

0.4 Engine 接入 ✅


模块 1:Bash 工具 ✅ 已完成

1.1 AST 解析替代字符串分割 ✅

1.2 环境变量前缀跳过 ✅

1.3 引号感知命令分割 ✅

1.4 进程管理 ✅

1.5 输出截断策略 ✅

1.6 后台运行 (run_in_background) ✅

1.7 命令分类 ✅


模块 2:FileEdit 工具 ✅ 已完成

2.1 Curly Quote 保留 ✅(早期方案精妙设计)

2.2 Desanitization ✅

2.3 两步验证设计 ✅

2.4 文件缓存集成 ✅

补完 ✅


模块 3:FileRead 工具 ✅ 已完成

3.1 图片处理 ✅

3.2 PDF 支持 ✅

3.3 Jupyter Notebook ✅

3.4 设备文件阻止 ✅

3.5 路径安全 ✅


模块 4:Glob & Grep ✅ 已完成(升华重构)

4.1 双引擎策略 ✅(升华设计)

4.2 多行匹配 ✅

4.3 类型过滤 ✅

4.4 分页 + 排序 ✅(升华设计)

4.5 安全加固 ✅(G8-D review)


模块 5:权限系统 🔴

5.1 自动模式分类器 ✅

5.2 Compound 命令完整处理 ✅

5.3 Sed 脚本验证 ✅

5.4 重定向目标分析 ✅

5.5 权限决策缓存 → 规则应用管道 + 去重 ✅


模块 6:上下文压缩 ✅

6.1 压缩后恢复精细度 ✅

6.2 消息分组 ✅

6.3 压缩前图像移除 ✅

6.4 多策略压缩 ✅

6.5 三层压缩降级 + 断路器改进 ✅


模块 7:查询循环 ✅

7.1 消息规范化管道 ✅(升华重构)

7.2 stop_reason 完整处理 ✅

7.3 Tool Result 配对修复 + 可观测性基础设施 ✅

7.4 Token 预算精细度 ✅

7.5 查询链追踪 ✅

7.6 查询循环防御性 ✅


模块 8:API 客户端 ✅

8.1 错误分类精细化 ✅

8.2 重试策略完善 ✅

8.3 API 预连接 ✅

8.4 SSE 边界情况 ✅


模块 9:Hook 系统 ✅

9.1 Hook 输出影响行为 ✅

9.2 pre/post-sampling hook ✅

9.3 插件级 Hook 注册 ✅


模块 10:Memory 系统 ✅

10.1 路径遍历防护 ✅

10.2 团队记忆同步 ✅

10.3 自动提取代理 ✅

10.4 新鲜度警告 ✅


模块 11:MCP 客户端 ✅ 已完成

11.0 工具名格式统一 ✅

11.1 Transport 接口 + 多传输支持 ✅

11.2 Client 重构 ✅

11.3 Schema 转换完整性 ✅

11.4 资源预取和缓存 ✅

11.5 Elicitation 处理 ✅

11.6 MCP 防御性 ✅


模块 12:Plugin 系统 ✅(核心完成)

12.1 依赖解析 ✅

  • [N/A] 语义版本约束(^1.0.0, ~1.2.3)- 不做.插件模型是叠加式(无代码级耦合), 版本约束需要 registry + 多版本共存,生态尚不存在.官方插件全部无相互依赖印证此判断. minEngineVersion 待引擎 API 稳定后再考虑.

12.2 LoadResult + 结构化错误 ✅

12.3 Claude Code 格式兼容 ✅

12.4 SDK 内置注册 ✅

12.5 Plugin 完整性校验 ✅ (原 DXT/MCPB Bundle, 2026-04-15 反转重命名)

12.6 插件配置 Schema ✅

12.7 Plugin 声明式 tool 注册 ✅ (2026-04-15 新增)

  • 驱动: 产品经理 2026-04-15 对话纠正: 原话"注册 tool 本来就是现成的功能", 指出 commit 58c3ab5 的"plugin 只能通过 MCP server 间接暴露 tool"是不完整 framing. tools.Registry.Register 本就是公开接口, plugin loader 只需补一层 manifest → pluginShellTool → Register 的数据翻译即可. 本 commit 填补了这个功能缺口.

12.8 Plugin MCP server engine 集成 ✅ (2026-04-15 新增)

  • 驱动: 上次对话 073da52 LSP 审计发现 engine 层 pluginHost 和 toolsRegistry 的 MCP wiring gap: Plugin.Hooks 有 syncPluginHooks, Plugin.Tools 有 syncPluginTools, 但 Plugin.MCPServers 无对称 sync 方法. internal/mcp 已有完整 5647 行 Manager+Client+Transport, 本任务是纯 wiring 不是造轮子.

模块 13:Agent 子进程 ✅

13.1 预定义代理类型 🟢

13.2 工具过滤精细度 🟢

13.3 Prompt Cache 共享 ✅

13.4 工具权限精细控制 ✅


模块 14:Skill 系统 ✅

14.1 SkillDef + SkillRegistry ✅

14.2 Frontmatter 完整支持 ✅

14.3 文件发现 ✅

14.4 Inline/Fork 执行 ✅

14.5 SkillTool ✅

14.P1 ✅


模块 15:系统提示词 ✅

15.1 PromptBundle + BundleRegistry ✅

15.2 缓存边界优化 ✅

15.3 默认 Bundle(claude+programming)✅

15.4 SDK 扩展 API ✅

15.7 中文 Bundle ✅

15.6 P1 补充 ✅

15.5 测试 ✅(47+ 测试)


模块 16:AutoDream(记忆巩固)✅

KAIROS 系统核心.与 C 方案自进化直接关联 - Dream 整理记忆,自进化基于记忆改进.

16.1 Dream 引擎 ✅

16.2 Dream 四阶段提示 ✅

16.3 DreamTask ✅

16.4 Stop Hook 集成 ✅

16.5 SubAgent 扩展 ✅


模块 17:UltraPlan(高级计划模式)✅

17.1 计划生成(P0)✅

17.2 计划步骤(P1)✅

:17.3 远程计划移至模块 20,仅适用于本地进程(CLI/SDK本地部署)提交计划到守护进程执行.


模块 18:Coordinator Mode(多 Agent 协调)✅

18.1 协调器角色 ✅

18.2 任务通知 ✅

18.3 Scratchpad ✅

18.4 Worker 生命周期 ✅


模块 19:Bridge Mode(远程桥接)✅

实现位置:platform/pkg/bridge/(平台层,非引擎层) 架构决策:v1/v2 是 Anthropic 私有云协议,不复刻;改为通用 BridgeTransport 接口.

19.1 核心接口 ✅

19.2 消息去重与批量上传 ✅

19.3 SSE Transport ✅


模块 20:Daemon Mode(守护进程)✅

实现位置:platform/pkg/daemon/(平台层,非引擎层) 架构决策:Go goroutine pool 替代 Node.js 子进程;Idle Timeout 替代固定 24h.

20.1 会话生命周期管理 ✅

20.2 健康检测 ✅

20.3 远程计划(来自17.3)🟢 ✅

适用于本地进程(CLI 或 SDK 本地部署)提交计划到守护进程执行. HTTP API 不需要此功能--服务端本身就是执行方,客户端调用即是"远程".


Platform HTTP API Server ✅

实现位置:platform/pkg/server/server.go(平台层,在引擎层之上) 对应:PLATFORM_TODO.md §1.1 HTTP API Server P0 全部完成


模块 21:UDS Inbox(进程间通信)🟢

21.1 内存 Inbox(同进程通信)✅

21.2 消息协议 🟢


模块 22:精妙细节(从早期方案深度分析中发现)✅

这些是产品级和玩具级的分水岭.

22.1 API 交互细节 ✅

22.2 缓存和性能细节 ✅

22.3 安全细节 ✅

22.4 消息处理细节 ✅


模块 23:SQL 工具链 ✅(2026-04-23)

面向 staging / 影子表的三件套. AI 写业务 DB 走 "Agent → staging → ML 审批 → WMS API 写生产" 三步流程 (memory project_db_ai_relationship.md) 中的 staging 一跳. TODO.md 之前把这三条归在 "消费层待实现不属于引擎层" 是 2026-04-08 立项时的保守判断; 实际业务逻辑 schema-agnostic / driver-agnostic, 通过 StagingDB newtype + *sql.DB DI 让客户端注入 driver, 引擎层仅 import database/sql 标准库, 零生产第三方依赖, 所有客户复用.

23.1 SQL 只读校验器 ✅ (commit 79670c7)

  • 纯字符串解析, 零 DB 依赖
  • 规则: 非 SELECT/WITH/EXPLAIN 拒 / 多语句拒 / LIMIT 可注入或校验 / 表名白名单
  • quote-aware 扫描 (字符串 / identifier quote 内的 --/* 不被误识)
  • schema-qualified 表名 (public.orders) 支持

23.2 SQL CAS 乐观锁 ✅ (commit bf31278)

  • StagingDB newtype + NewSQLCASTool(db, maxRetries) DI (构造处强制显式声明 staging 作用域)
  • maxRetries 默认 0 (fail-fast; AI Agent 看 version 冲突应重 plan 非 silent retry)
  • version 非 int 运行时 reject (避免 timestamp / CDC 复制场景 silent 失效)
  • identifier [a-zA-Z_]\w* 白名单拒 quoted, 所有 value 走 ? 参数化防注入
  • modernc.org/sqlite test-only dep (core 第一条非图像处理第三方依赖, 仅 _test.go import)

23.3 SQL Dry-run 三路 ✅ (commit a935604)

  • 方案 E (before + after 都 SELECT), UPDATE/DELETE/INSERT 按 operation 刻意不对称
  • LLM 传 preview_predicate (工具不 parse SQL), 一致性检查标 mismatch / after_predicate_mismatch 信号
  • 100 行 truncate 显式提示 (非 silent sampling 的 approval theatre)
  • DryRunResult 3 字段首次 write point (SQLDryRunTool 填), 外部 UI / audit 反序列化消费 (pull API 归档状态保持不变)

基础设施层(服务端能力)🔴

早期方案是客户端,背后有 Anthropic 服务端.我们是客户端+服务端,必须自建这些基础设施. 原则:涉及代码面越广的越先做,否则后面改动太大.

INF-1 可观测性(EngineObserver)✅(与模块 0 相同,详见顶部)

INF-2 文件历史/回滚 + ToolCapability 协议 ✅

INF-3 优雅关闭 ✅

INF-4 会话活动追踪 ✅

INF-7 引擎竞态修复 ✅(2026-04-07)

INF-5 安全审计 ✅

INF-6 版本兼容 ✅

INF-7 数据安全(文件/DB/API 三维度)📄 文档完成

文档:docs/data-safety.md(已完成,含凭据安全章节) 代码:消费层实现,引擎层接口已就绪

引擎层--框架质量修复(代码审查)

引擎层--凭据安全

引擎层--SDK 编排能力

  • [x] 🟡 L952b → L407: platform 消费层文档自动化三件套 ✅(2026-04-26, commit C0-C6: 89c38d3 C0 path bug fix /v1/* → /api/v1/* (Caddyfile + ADR-0002 + server.go 8 mux 路由 + server_test.go ~25 处 + main.go 注释); 26bc732 C1 server.go 8 handler swag 注解 + 3 named response type (HealthResponse/StatusResponse/ListToolsResponse) 替代 ad-hoc map + swag.go seed file + docs/{swagger.json 22.5K 12 schema, swagger.yaml 12.3K, docs.go 23.1K Go embed} 首版; bce7670 C2 cmd/common --swagger flag + Swagger UI endpoint (httpSwagger v2 + side-effect import docs, authMiddleware/rateLimit allow-list 加 /swagger/); 22b6388 C3 core/Makefile 4 docs target (docs-swag/docs-grpc/docs-consumers/docs-all) + tool install pin (swag@v1.16.6 + protoc-gen-doc@v1.5.1) + grpc-api.md 首版 278 行 + .gitea/release.yml docs drift gate (apt-get protoc + make docs-install + docs-swag/grpc + git diff --exit-code); a9b04ab C4 docs/CONSUMERS.md 顶层 wrapper 133 行 (端口拓扑/env/flag/OIDC auth 流程图/业务 REST 一次性+多轮会话 curl 例子/观测 gRPC SafetyChain 指引/进一步阅读链); bf5af1d C5 Caddyfile handle /swagger/ → common:8080 + docker-compose --swagger flag (HK-133 lab 默认开, 生产另份 compose 关); 本 commit C6 TODO/CHANGELOG/CLAUDE.md 同步). 三件套全产: 业务 REST → docs/swagger.{json,yaml,docs.go} (12 schema, swag init 产物); 观测 gRPC → docs/grpc-api.md (HealthService + SafetyChainService 字段表); 顶层 docs/CONSUMERS.md (133 行 wrapper, 链 swagger.json + grpc-api.md 不重复). CI drift gate 在 release.yml dead-field-ratchet 后插, push tag 时 install protoc + swag + protoc-gen-doc 跑 docs-swag/docs-grpc + git diff --exit-code, 偏差 fail tag 构建 (业界对照: Stripe / Anthropic / OpenAI 都对 OpenAPI spec 跑同等闸). 意外+顺手修 (C0): 上一会话 commit 5 (c35e761) Caddyfile handle /api/v1/* 不剥前缀, server.go mux 注册 /v1/* 不匹配, 经 hub.flytoex.net 全 404, L407 之前先打通真实部署链 (memory feedback_validate_network_path_before_deploy 警告再次成立). smoke: ANTHROPIC_API_KEY=fake go run ./cmd/common --rest-addr=:18080 --swagger → /api/v1/health 200 + /swagger/index.html 200 + /swagger/doc.json 200 返回 commit 1 嵌入 spec; -race 全绿. 不做* (rule of two): SDK 自动生成 (Stainless 模式, 等 5+ 语言客户端); .proto 字段注释完善 (health.proto 部分字段 description 列空, protoc-gen-doc 自动反映, 后续工作).
  • [ ] 🟢 L952c: 场景化编排 Go 使用教程 (P3, 2026-04-17 拆自 L952). 产出: core/examples/orchestration/{ssh_deploy,db_migration,system_config}/main.go 三个可跑示例 + core/docs/orchestration_scenarios.md 指向它们. 演示 Checkpoint / Reversible / DryRun / SecretStore 组合用法. 成本: 500-1500 行 Go. 不紧急: 当前无明确第三方集成需求, 投机未来回报率低. 等有实际集成方催再补不迟.

引擎层(已就绪,无需再实现)

CLI TUI 消费层(ccm/tui/ - P0+P1 完成, 新功能冻结 2026-04-15)

agent-engine CLI 修复(2026-04-08 发现)

server / transport 低优先问题(2026-04-08 Review 发现)

  • ~~internal/server/ 已删除,迁移至 platform/ 从零开始~~

消费层集成测试(2026-04-08 立项)

消费层待实现(不属于引擎层,由平台/消费者完成)

  • [ ] 🟡 ML 验证器接入(diff 序列化 → ML 推理 → 通过/拒绝) — core 接口就绪 (Validator / LLMValidator / CompositeValidator / AlwaysApprove + NewValidatedTool nil fail-fast, 见 L699+). platform 接外部 ML backend 实现 + 装配即可启用.
  • [ ] 🟡 熔断器(连续 N 次 ML 拒绝 → 暂停 AI 写权限 + 告警) — core 三态 breaker + VerdictSink 桥接就绪 (commit 3342425). platform 选作用域 (全熔/只熔写/每工具一熔) 并 wire ValidatedTool sink 即可启用.
  • [x] 🟡 Staging 表管理(决策包级 pending_tech/rejected_tech/pending_ml/rejected_ml/approved/executed/failed 7 状态机 + 混合控制: staging 主动 ValidateTech/Biz + 外部推 MarkExecuted/MarkFailed + 可插拔 DependencyGuard + InMemoryStore 参考实现) ✅(2026-04-24, commit 1/2/3: d9992d5/2c38e46/本 commit). 复用 validator.Validator 为两层 slot, reflector.EvaluatorAsValidator 适配 Evaluator 接入. pull-only query API. SQL-backed Store 由平台层实现 (contract 见 core/pkg/staging/store.go).
  • [x] 🟡 影子表管理(多轮推理场景:session 级镜像表 + 会话结束清理) ✅(2026-04-25, commit 1/2/3: 2c84209/e140952/本 commit). 方案 C 列标记隔离 (pkg/shadowdb/): 物理 shadow 表加 session_id VARCHAR(64) NOT NULL 列, 按 session_id filter 做跨 session 隔离. 规避 PG TEMP TABLE 在 pooled *sql.DB 下 temp 表蒸发 + driver 分裂问题. 零 DDL 纯 INSERT/UPDATE/DELETE/SELECT + ? 参数, PG/MySQL/SQLite 通吃. Opener 接口 + InMemoryOpener 参考实现 + pull-only Reap 孤儿 GC (core 不起 goroutine) + EnforceSessionFilter 三层防御中层 (quote-aware string/comment 剥离). 与 staging 边界: staging 决策级 pre-commit, shadowdb session 级推理中 scratchpad; shadow -> staging -> production 单向, shadow 永不 merge.
  • [x] 🟡 L692: platform/common 业务 REST/SSE 通道激活 ✅(2026-04-26, commit 1-6: 01f08e7/8189d05/694bd07/e1db327/c35e761/本 commit). 3-agent review reconcile (调研 11 LLM API + grpc-gateway / 质疑 6 道硬题 / 设计 4 备选), PM 拍板方案 A 修正版: 激活 server.go 1263 行已写好的业务 REST/SSE + grpc-gateway 砍掉 (admin/server.go 已实现观测面 REST handler) + ADR-0002 立 "REST 业务 / gRPC 观测" bifurcation (与 9 天前 memory feedback_architecture_principle_over_rule_of_two.md 的 "gRPC 优 HTTP" 是分层不是反悔). commit 顺序: 01f08e7 C1 server.go 拆 Serve+wrapper 让出 signal handling; 8189d05 C2 Verifier 替代 BearerToken 走 auth.HTTPMiddleware (raw shared-secret + ConstantTimeCompare 删除, OIDC 与 admin 一致); 694bd07 C3 Attach + HandlePermission 拆 anthropic provider 写死, server 不再读 ANTHROPIC_API_KEY 不再 hard-code 模型; e1db327 C4 cmd/common 加 --rest-addr flag 装配 anthropic provider + engine + s.Attach + 第三 listener wire (signal handler 三路协调: grpc.GracefulStop / httpSrv.Shutdown / restCancel); c35e761 C5 docker-compose expose 8080 + Caddyfile /api/v1/* (flush_interval -1 + response_header_timeout 0 SSE 透传, ANTHROPIC_API_KEY ${...:?...} 必填); 本 commit C6 ADR-0002 + TODO/CHANGELOG/CLAUDE 同步. Tool 级安全链装饰刻意不在 cmd/common 装 — 一刀切 AlwaysApprove + DefaultExtractor 会把所有 Tool 锁同一组合或 fan out 到不匹配 Tool 语义的 wrap. common 保持纯 transport, verdictStore 接线就绪, 由行业驱动代码 (logistics 等) 在 Tool 注册时装饰并写数据 (ADR-0002 § Decision 第 3 条记录这个分工). 业界对照: 11 LLM API (Anthropic / OpenAI / Bedrock / Cohere / Mistral / Replicate / LM Studio / Ollama / LangServe / Together / Fireworks) 全单通道 REST/SSE; Vertex AI 是 gRPC-first 例外, 但 GCP transcoding 模式不仿照. ADR-0002 引用 11 框架矩阵 + grpc-gateway / gRPC-Web 现状 + ConnectRPC 替代论证. 测试: server_test.go 既有 546 行用 httptest 直接打 handler 无须改, 4 个 raw bearer TestAuth_* 删除 (auth.HTTPMiddleware 自身覆盖在 internal/auth 包测试), 新加 TestServer_Serve_RespectsCtx ctx 取消干净返回. -race 全绿. cmd/common binary 集成验证 --help 显示 --rest-addr. 实测 (docker exec curl + SSE chunk timing) 留 push tag 前按 memory feedback_validate_network_path_before_deploy 走.
  • [ ] 🟢 AuditSink 数据库实现(写入 PostgreSQL,按 session_id 查询,支持批量回滚)
  • [ ] 🟢 WMS 波次建立参考实现(状态机新增"任务已建待确认"状态)
  • [x] 🟡 L693: 业务 REST 多副本 SessionStore interface ✅(2026-04-26, commit 1-4: eec48fe/beaff60/96e893a/本 commit). 3-agent review reconcile (调研 LangGraph/Vercel/Temporal/Express/Django 业界 prior art / 质疑 6 道击中 3 道 / 设计 3 alternatives 选 typed Alt 2), PM 拍板"整个 platform 都 Postgres". 真相: 多副本真阻塞不是 sessions map, 是三层进程内 pin (server.permCh + Session.pendingPermissions + engine.sessionState), 后两层在 core 引擎层平台层不能解, 必须 LB sticky routing; SessionStore 价值降级为"replica 重启不丢元数据 + 滚动部署 drain + Postgres audit". commit 顺序: eec48fe C1 SessionStore 接口 (Create/Get/Delete 三方法, 不要 Touch/List 投机) + InMemoryStore drop-in 替换 server.sessions map + 4 handler 改造 + TOCTOU race 折叠 (319 行 + 改 server.go 182 行); beaff60 C2 Postgres 后端 + internal/db/ 共享池 (中央 schema 权威 platformMigrations + Migrate 启动期 idempotent) + --postgres-dsn flag + docker-compose pg service + healthcheck + persistent volume + release.yml POSTGRES_PASSWORD secret + testcontainers-go 真 pg 测试 (~510 行 + 改 main.go 53 行 + docker-compose 48 行); 96e893a C3 ADR-0003 (387 行, 8 节, 三层 pin 物理事实分析 + sticky routing phase 1 ip_hash + phase 2 X-Session-ID 升级路径 + cache miss 503 fallback) + Caddyfile 注释 (单副本 vs 多副本部署区别); 本 commit C4 TODO + CHANGELOG + CLAUDE.md 同步. 关键决策: drop Redis 档 (元数据 payload 太薄, 共享 staging pg 池更经济); drop staging Postgres 后端 (PM 接受 YAGNI 跳过, 等 staging 真有消费者再做, ADR-0003 § 5.5 登记触发条件); engine.SnapshotStore 接线 cache miss 自动恢复历史留 follow-up. 不引正式 migration 工具 (1 张表, plain CREATE IF NOT EXISTS 自检足够; 等 ≥ 5 张 + schema 稳定再引). 测试: 6 InMemoryStore 测试 + 5 testcontainers Postgres 测试 + 2 db pool 测试, 全模块 -race 全绿; dead-field-scanner baseline 220 不变 (scanner 只扫 core/). PM 部署侧必做 (v0.4 release 前): Gitea secrets 配 POSTGRES_PASSWORD (跟 ANTHROPIC_API_KEY 同位).
  • [ ] ⚪ L694: gRPC + REST cross-transport request-id / trace 串通 (P3, 2026-04-26 登记自 L692 ADR-0002 tracked debt). 背景: server.go 自己生成 request-id (server.go:331 getRequestID), gRPC 侧没有. 用户从 logistics C# 发 gRPC ListVerdicts 想看 "我的请求触发了什么 verdict" — request-id 串不起来. 产出: OpenTelemetry trace span 跨 transport 注入 (HTTP header + gRPC metadata), Tempo / Jaeger 一类观测后端落. 成本: 引入 OpenTelemetry SDK + collector / exporter wire, 中等. 不阻塞 v0.3: 当前单租户 dogfood 不需要, 上量后才有 ROI.
  • [ ] ⚪ L695: SSE 1000 单/s 带宽监控 (P3, 2026-04-26 登记自 L692 质疑 agent Q1.3). 背景: SSE framing (event:...\ndata:...\n\n 文本) 比 gRPC binary protobuf 多 5-10x overhead. 业界共识 (LLM API 全 SSE) 接受这个代价, 但 logistics 1000 单/s × 平均 20 events/单 = 20K events/s 真上量后跨 region 流量成本要量化. 产出: prometheus exporter 暴露 sse_bytes_per_second / sse_events_per_second metrics, Grafana dashboard 跟踪. 不优化: 监控只是为了量化, 真到瓶颈再优化 (压缩 / batch / fallback gRPC streaming, 都是 v1.0+ 课题).

INF-8 公共契约包 + Provider 工厂 ✅(2026-04-07)

INF-8.1 pkg/flyto 公共契约包 ✅

INF-8.2 内部协议适配器

INF-8.3 Provider 工厂实现 ✅

INF-8.4 SSE 架构重构 ✅(2026-04)

INF-8.5 待完成(P1)

  • [ ] 🟢 P2: provider 静态模型表自动更新工具(爬取 Anthropic/MiniMax/OpenAI 文档页面检测变化)

INF-8.6 能力层(基于探测矩阵驱动引擎行为)

核心原则:能力差异在引擎层抹平,消费者写标准代码,引擎负责 provider 适配. 细节决定成败--$ref 展开,schema 约束裁剪,工具数量保护,每一项都是引擎的护城河. 探测数据(2026-04):Anthropic Sonnet/Opus 缓存阈值 1024t,Haiku 4096t,MiniMax ~1024t. OpenRouter → Anthropic 路径 cache_control 无法透传(probe 确认 cr=0@7200t). $ref 双重序列化 bug:MiniMax 直连 + OpenRouter→Gemini 均复现,DeepSeek/GPT-4o 正常.

CAP-1: flyto.Request 意图字段 ✅(2026-04)

CAP-2: Provider 自动 Caching 决策 ✅(2026-04)

CAP-3: StructuredOut Provider 自适应 ✅(2026-04)

CAP-5: Tool Schema $ref 自动展开 ✅(2026-04)

probe 实测(2026-04):MiniMax 直连 + OpenRouter→Gemini 均有双重序列化 bug. $ref 字段值返回 JSON 字符串而非 object,下游 map[string]any 拿到 string → 静默数据损坏.

CAP-6: Tool Schema 约束 Provider 适配 ✅(2026-04)

各 provider 对 JSON Schema 约束支持不一,消费者写标准 schema,引擎自动裁剪不支持的约束.

CAP-7: Tool 数量上限保护 ✅(2026-04)

超出上限直接报 400,消费者无提示.引擎提前检测,给出可读错误.

CAP-8: Schema 复杂度限制保护 ✅(2026-04)

OpenAI strict 有隐藏限制,消费者超出时只拿到 400 和晦涩错误信息.

CAP-4: 能力探测自动化 🟢 P2

  • [ ] 通过 Flyto CLI 无头模式自消费(不依赖外部脚本调用)
  • [ ] CI/CD 集成:新模型上线后自动触发探测,更新 capabilities.json
  • [ ] 探测前强制 WebSearch 最新文档,规范阈值和测试范围(防止训练数据过期导致参数设置错误)

evolve v0.2+ 接口矩阵实现 🟢 P2

战略背景见 docs/evolve-strategy.md. 接口契约已在 core/pkg/evolve/interfaces.go 落定 (commit bf6c3ad), 本段追踪每个接口的引用实现进度. 引擎只提供文件实现, SQL 多租户实现属 platform 层.

  • [x] ParameterStore -- FileParameterStore (本地文件 + 版本化 + Lock + Watch, in-process 订阅)
  • [x] Generator -- LLMGenerator (窄 LLMClient 抽象 + text/template prompt + JSON array 解析, 支持 markdown 围栏剥离)
  • [x] Evaluator -- WeightedEvaluator (加权线性, feature/weight 一一校验, raw breakdown, normalize 可选) + FuncEvaluator (非线性 escape hatch)
  • [x] Reflector -- AggregatorReflector (entity×metric 描述统计 + pending 追踪 + RWMutex 并发安全) + FuncReflector (escape hatch)
  • [x] ParameterEvolver -- DefaultParameterEvolver (ProposerFunc 注入 + Apply approved/rejected 两分支 + AuditLogger 钩子)
  • [x] LogSource -- FileLogSource (JSONL + UTC 日期分片 + Append/Read/Days, O_APPEND 原子写)
  • [x] LogReplayer -- DefaultLogReplayer (懒 per-entity feedback 缓存 + first-touch per-metric 配对, ReplayerOption 模式)
  • [x] FeedbackChannel -- FileFeedbackChannel (二级分片 entity/UTC-date, Report/Query, url.PathEscape entity)
  • [x] ShadowRunner -- DefaultShadowRunner (CandidateSampler + ParamApplier 注入, 相对 divergence ∈ [0,1], Meta 审计链路)

代码质量 / 技术债(G1 Review 发现)

来源:2026-04-08 G1 Review(session + normalize + audit + worktree 批次)

已修复(本批 5 项)

已修复(Session 生命周期 4 项,2026-04-09)

待修复(低/中优先级)

可靠性

规范化管道

历史债 / 魔法数字

抽象设计(需讨论,不直接改)

G2 Review 发现(Dream + Plan + QueryChain + TokenBudget + Elicitation)

来源:2026-04-08 G2 Review

已修复

待修复

并发 / 可靠性

历史债

抽象设计

G3 Review 发现(pkg/engine/ 第三部分:agent/skill/scratchpad/team/observer)

来源:2026-04-08 G3 Review

已修复

待修复

安全

Bug / 可靠性

历史债


G4 Review 发现(memory/context/permission/config/flyto/hooks/inbox)

来源:2026-04-09 G4 Review

已修复

待修复

接口设计

  • [x] 🟢 反事实工作流引擎级 enforcement (2026-04-16 登记 → 2026-04-25 缩水做 6 commit). 3-agent review reconcile 否决原方案 A 完整版 (引擎级 8 模块 1500-2500 行), 走 Option 2-Plus 缩水方案 4 件套 + ADR-0001 归档. 实施: (b)+(c)+(d) 子集 + 进化沉淀接线 + reference hook. commit 顺序: 248253e C1 core/pkg/counterfactual/ Deliverable schema (五步反向思维标准化数据结构, MetadataKey "flyto.counterfactual.deliverable" 跨包 Record.Metadata 落点); 0385efd C2 core/pkg/skills/reverse_think/ MiniMax Anthropic 兼容端点 Go 化 client (替代 ~/.claude/skills/reverse-think/SKILL.md 软性 markdown 在生产 Agent 不可调的 gap); c4b755f C3 tools.Metadata.RequiresReverseThinking opt-in flag (与 RequiresCheckpoint 同源 rule of two); 4618b79 C4 Deliverable.AsReplayEventevolve.LogReplayer 跨包 adapter (counterfactual import evolve 单向, evolve schema-agnostic 不感知 counterfactual; 关上 PM 担心的 "各行业自写 skill 进化结果停在哪里" 循环); 40aae31 C5 platform/common/safetychain/ReverseThinkingHook reference hook (走 hooks.PreToolUse 不 wrap Tool, 与 Assemble 平行, fail-open advisory LLM 错不阻断业务); 本 commit C6 文档 + ADR-0001 同步. 方案 A 完整版否决理由 (写入 ADR-0001): 与 hooks/permission/validator/staging/RequiresCheckpoint 5 套现有 gate 概念重叠 + 业界 8 框架 (Claude Agent SDK / OpenAI Assistants / Vertex ADK / LangGraph / AutoGen / CrewAI / Semantic Kernel / Cline) 全部走 prompt-level + tool desc + 开发者自填策略零先例 engine-level 强制 reasoning gate + 范畴错位 (dev review 协议错配 runtime gate) + 吞吐数学不成立 (logistics 1000 单/s × 即使 10% 触发 → MiniMax token plan call 数被打爆). 3-agent 并行 review 流程: 调研 agent (8 框架矩阵 + 决策前 gate 形态调查 + 业界共识 reasoning) / 质疑 agent (5 道硬质疑含 RequiresCheckpoint 已实装 anchor 修正) / 设计 agent (4 备选 + 边界图 + 拍板假设矩阵). PM 拍板 Option 2-Plus (在原 Option 1 不做 + Option 2 缩水间, 加一件 evolve 接线解决 "进化结果停在哪" 担忧). 总量: ~2,131 行 (含双语 godoc + 测试), 6 commit. 测试: counterfactual 13 + reverse_think 12 + tools 1 新 + safetychain 9 = 35 测试 -race 全绿. core + platform/common 双 module build 干净.

  • [ ] 🟢 微信 ClawBot 接入 (platform 层) (P3 低优, 2026-04-16 登记, 不属于 core 引擎层). 背景: 腾讯 2026-03-22 开放 WeChat ClawBot 插件 + iLink Bot API, 中国大陆唯一合法的个人微信第三方 Bot 通道. Hermes Agent v0.9.0 (2026-04-13) 已接入. 架构定位: 属于消费层 (platform/) 不进 core/, 遵守宪法原则 9 "引擎核心不假设特定场景" — 微信是腾讯产品 × 中国市场 × ClawBot 商业条款, 三重非中立. 代码位置: platform/internal/messaging/weixin/ilink_bot.go (新建), 实现 core/pkg/bridge/transport.BridgeTransport 接口. 技术栈: HTTP/JSON long-poll 35s, AES-128-ECB, user-bound token (扫码登录), 不需要公网 IP. 商务门槛≈0: 不需要 Tencent 开发者证, user-bound 不是 platform-bound. 部署模式: 国内直连 ilinkai.weixin.qq.com / 海外 BYOT (Bring Your Own Token) + 域名隔离规避深圳南山司法管辖. 战略价值: 14 亿用户入口, 中国 to C 市场 unblocker. 优先级 P3: 不属于引擎层, 平台层做; 排在 platform 层其他工作之后. : core/docs/agent_teams_competitive.md §4.6. (P3, 属于 platform/ 层不属于 core/)


G5 Review 发现(pkg/tools/builtin/)

来源:2026-04-09 G5 Review

已修复

待修复

安全 / 可靠性

Bug

历史债


G6 Review 发现(pkg/providers/ - 7 个 provider,20 个文件)

来源:2026-04-09 G6 Review

已修复

待修复

安全

一致性 / 历史债


G7 Review 发现(internal/ - bash/mcp/cache/logger/server/diff/tokenizer/git)

来源:2026-04-08 G7 Review

已修复

待修复

安全 P0

安全 P1

正确性 P2

P3


G9-G12 架构审查修复(2026-04-09)

G9 系列:引擎层 Anthropic 解耦

G10 系列:Prompt/Context/Config 层解耦

G11 系列:Permission/Query/Wire 层

G12 系列:工具类包

G8 P2 补充修复

Phase 2 架构清理(2026-04-09)


实现债务 (dead-field scanner 追踪)

2026-04-19 新增, 同日 A 档 4 条 wire 完毕. core/tools/dead-field-scan 扫到 26 条已声明但扫描模块内从未被读的 exported field, MiniMax M2.7 triage 过, spot-check 4/4 通过 (见 commit 6c01d15 的 scan-baseline.json). 这些字段不是"可删垃圾", 是"设计意图先行、实现未连"的 feature gap. 类比 engine.Config.MCPServers (57a06c6) 和 Config.Plugins (07fe345) 的修复思路: wire, 不是 delete.

A 档 wire 同日完成 (2026-04-19): 两轮 commit 达成真 wire, 不是"让 scanner 满意"的形式实现.

第一轮 (commit d2c647c) — 满足 scanner read site: - 3 APIError 字段经扩 Error() 方法展示 Hint / TokenGap / Body (256B preview). Hint 到位 (日志即用户看诊断); Body 半到位 (preview 在日志, 完整 body 可 SelectorExpr 直访); TokenGap 仅 print 数字. - Config.Verbose 经 engine.New 尾 verbose_startup 事件, 只一条启动标记.

第二轮 (commit 本轮) — 真设计意图兑现 (用户反向挑战"scanner 满足 ≠ wire 完成"后补): - TokenGap 接 compactor: agentctx.CompactTiered(..., WithTokenGap(n)) + 内部 truncateAndRetryCompact 精确跳步分支 (累积最旧组直到 >= tokenGap 一次丢掉) + TestTruncateAndRetryCompact_TokenGapStride + _ZeroSkipsStride 两条 regression guard. engine runLoop 从 *api.APIError 解 TokenGap 透传给 forceCompact. 字段 godoc 承诺的 "压缩模块一步跳过 N token" 从声明兑现到行为. - Verbose 加第二处 trace: runLoop 入口 verbose_run_loop_started 快照含 model / message_count / tool_count / prompt_len / max_turns / is_skill_prompt. 默认 observer 零行为影响 (新 event 名, 不 gate 现有 event), verbose 模式有完整 trace 起点.

baseline 438 → 434 自第一轮起锁定, 第二轮不变动 baseline (两个字段已被读过, scanner 视角已 alive) 但实质意图层面从"形式"升到"真"wire.

原 A 档第 5 条 LLMCallOpts.Temperature 经 verify 发现是 scanner 间接假阳性 (test 验 forward, 公开 API, 字段真的 wire, 但 FlytoLLMClient adapter 刻意忽略), 降档到 C 档重评估.

ratchet 规则 (见 make dead-field-scan-ratchet): baseline 只能减不能增. 新加 dead field → CI fail. 修完一条 → baseline shrink, 提示 commit. 下面 26 条是当前 debt list, 每 wire 一条就从 baseline.json 少一行, 对应条目从本 section 勾掉.

A 档 (v0.1 要做, 小 wire) — 4 条 (全部已 wire)

  • [x] transport.APIError.Body — 扩 Error() 把 body 前 256 字节带入日志行 (2026-04-19)
  • [x] transport.APIError.HintError() 附 hint 字段 (2026-04-19)
  • [x] transport.APIError.TokenGapError()(token_gap=N) + 真设计意图达成: compactor truncateAndRetryCompactWithTokenGap option, 从 API 精确溢出值一步跳步 (字段 godoc 承诺的"让压缩模块一步跳过 N token 的消息组"兑现), 带 TestTruncateAndRetryCompact_TokenGapStride 锁定 (2026-04-19)
  • [x] engine.Config.Verbose — engine.New 尾 verbose_startup + runLoop 尾 verbose_run_loop_started trace (model / message_count / tool_count / prompt_len), 非 Verbose 路径零行为影响; "详细日志模式"真意图兑现 (2026-04-19)

B 档 (v1.0 要做, 特性框架 wire) — 17 条 (Skill 3 条 2026-04-19 从 C 档移入)

  • [x] internal/syslib/bash.Node.Background — bash & backgrounded job 语义真 wire (2026-04-20): 比 heredoc 深一级 — parser 从不写入 Background, 字段定义但永远为零值. 4 sub-claim 端到端: (a) parser.go parseList op=="&" → list.Background=true + 尾部孤立 & wrap 单 child NodeList (Bash 语义: cmd & 把 cmd 放后台). (b) extract.go extractContext 加 background bool, 两处 NodeList 分支 (extractFromNode + extractAllFromNode) 实现 "left 继承 / right 清掉" 传播: cmd1 & cmd2 cmd1 bg cmd2 fg; cmd1 && cmd2 & cmd3 整个 && 组 bg cmd3 fg. (c) CommandInfo 新增 Background bool 字段 (双语 godoc 说明 "escapes foreground control" 风险), extractSimpleCommand 从 ctx.background 填入. (d) bash_security.go AnalyzeDanger 新 helper amplifyBackground(info, c) 在所有 danger return 点调用: Reason 附 "backgrounded -- escapes foreground control", Pattern 前缀 "& ". 把 bash.CommandInfo.Background 从 parser 装饰属性接入安全决策面 — 过去 rm -rf / &rm -rf / 同样报警无区分, 现在 TUI/审计能告诉用户哪个能通过取消会话中止哪个不能. 4 条 regression test 一 sub-claim 一 test (3 bash 包 / 1 permission 包): TestParseList_BackgroundWriteSite (4 子 case 覆盖 & 正向和 &&/;/| 反向) / TestExtractCommands_BackgroundPropagation (简单 + nested) / TestExtractCommands_BackgroundFieldPopulated (字段暴露 + 反向无误标) / TestAnalyzeDanger_BackgroundCommand_Annotated (含前台对照). baseline 391 → 390
  • [x] internal/syslib/bash.Node.HeredocBody + HeredocQuoted + HeredocStripTabs + HeredocTag — heredoc 4 字段真 wire 端到端 (2026-04-20): RedirectionInfo 扩 4 字段 (Tag/StripTabs/Quoted/Body) 为 canonical 门面, extract.go NodeHeredoc 分支填入 + Target 回填自 Tag + IsStatic 重算. extractAllFromNode 对 unquoted heredoc body 递归解析 (Quoted=false 时 Parse body 抓嵌套 $(rm -rf /) 类绕过, 仿 Assignments.Value precedent). bash 包新增公共 helper ResolveHeredocBody(redir): <<- 形式返回 strip 行首 tab 后的 body (匹配 bash 运行时), 普通 << 原样返回, nil/非 heredoc 返回 "". bash_security.go IsDangerousCommand + AnalyzeDanger 用 ResolveHeredocBody 拼 checkStr, 对 body 做 dangerous-file 字面检测 (过去 cat > out <<EOF\n.ssh/authorized_keys\nEOF 全透明). AnalyzeDanger 真实消费 redir.HeredocTag + redir.HeredocQuoted: 当危险字面量源于 heredoc body 时 Pattern 带 <<TAG: 前缀, Reason 对 Quoted/Unquoted 分别附 "inert literal" / "runtime expansion possible" 措辞 — TUI 多 heredoc 场景能定位触发源并告诉用户是字面 payload 还是动态展开. 工作流教训: 第一轮只扩 RedirectionInfo 字段 + test 锁 shape, scanner 净 -2 (Node 4 条 drop, RI 层 3 条新增 dead), 用户反向挑战 "没消费者不代表没用"+advisor 同诊断但走了"删字段"捷径 — 拒绝删, 改走"补真 cross-package consumer" 路径 (ResolveHeredocBody + DangerInfo Tag/Quoted 措辞), 字段全部保留并每条有真语义读 site. 8 条 regression test 一 sub-claim 一 test (5 在 bash 包 / 3 在 permission 包), assert 的是 consumer 行为不是字段填值. baseline 397 → 391 (heredoc 4 条 + RedirectionInfo.Operator / Target bonus 激活)
  • [x] internal/syslib/bash.Node.Quoted — 字符串引号语义真 wire 端到端 (2026-04-20): CommandInfo 新增 ArgQuoted []bool parallel slice 与 Args 同长度 (双语 godoc 说明 "inert literal vs possibly expanded" 与 heredoc Quoted 契约镜像), extractSimpleCommand 收集 word 时读 child.Quoted (parser 三处写入: 单引号 true / 双引号 false / ANSI-C $'...' true 的唯一产线读点) 填 wordQuoted 并切出 ArgQuoted. bash_security.go AnalyzeDanger dangerousFile 命中分支新增 arg-level attribution: 当危险字面量不在 heredoc body 内时, 查找含字面量的 arg index, 按 c.ArgQuoted[i] 对 Reason 附 "literal arg -- no runtime expansion" (单引号/ANSI-C) 或 "arg may expand at runtime" (双引号/unquoted), 与 heredoc Quoted 分支同形 TUI shape. 选 B 重量方案 (跨包真消费) 而非 A 轻量 (canonical 路径选择 resolveWordValue 改读 node.Quoted) — 后者同构于"走捷径过 scanner"被 feedback_scanner_pass_vs_intent_done 禁止. Args []string 保公共 API 兼容不动, ArgQuoted 为新增 surface. 4 sub-claim 一 test: TestExtractSimpleCommand_ArgQuotedParallel (regime 1+3+4 extract 层映射: 单/双/ANSI-C/unquoted + 无 arg nil 对照) / TestAnalyzeDanger_ArgQuoted_LiteralInReason (sub-claim 2+4 permission 消费: 单引号 vs unquoted) / TestAnalyzeDanger_ArgQuoted_ANSIC_LiteralInReason (sub-claim 3: ANSI-C 走完 extract→permission 往返 Quoted=true 不失真). baseline 390 → 389
  • [x] internal/transport/retry.RetryContext.QuerySource — 真 wire 端到端: (1) retry.WithQuerySource / QuerySourceFromCtx ctx helpers. (2) internal/transport/client.go 在 rctx 构造时读 ctx 填 QuerySource (write site). (3) Retryer.Do 构造 CannotRetryError.Reason 时调 reasonWithSource(rctx) 嵌入 (source=xxx) 后缀 — 真 SelectorExpr read site, 让运维看错误流时能按来源归因 (不是诊断事件那种 observer-dependent, 而是错误消息本身即带信息). (4) 7 处 provider.Stream call site 注入 ctx: BuildAndStream=main_thread, subagent=sub_agent, tool_summary=summary, memory query=background, compact="compact", classifier="classifier", evolve="evolve". pkg/context 等包无法 import pkg/engine 的枚举, 按 godoc "free-form string" 用小写标签. godoc 升级到完整 Wire 段 (inject/write/read/future-policy-branching 四步). 3 条 regression test: WithQuerySource_RoundTrip / EmbedsQuerySourceInCannotRetryReason / EmptyQuerySource_ReasonUnchanged (后者锁降级: 未注入时 Reason 干净不带 stray "(source=)" 尾). advisor 警示 "写不算读" 后补第三步 reasonWithSource, 避免 scanner-pass-not-intent 陷阱 (2026-04-19)
  • [x] pkg/context.Section.Static — 真 wire 为 runtime invariant: BuildPromptBlocks 遍历 StaticSections() / DynamicSections() 时读 s.Static, 违规 (非 Static section 放进 static bucket, 或 Static section 放进 dynamic bucket) 发 section_contract_violation observer 事件携带 {name, bucket, expected_static, actual_static}. SDK 消费者手搓 &Section{...} 放错 bucket 时 wire time 立即暴露, 不用等数周后 cache 行为回归. 诊断非 fatal, 计算仍继续. godoc 从"内容全局不变...适合 global cache scope"升级到完整三层说明 (runtime invariant check + 未来 Anthropic global cache-scope 分支点 + SectionRegistry cache 字符串分配避免). SectionRegistry.emitEvent 复用 observer 字段 (commit 前序引入). 3 条 regression test: StaticBucket_NonStaticSection_EmitsViolation / DynamicBucket_StaticSection_EmitsViolation / DefaultBundle_NoViolations (2026-04-19)
  • [x] pkg/context.Section.NoCacheReason — 真 wire 三层: (1) SectionRegistry.Compute CacheBreak 分支发 section_cache_break observer 事件携带 {name, reason}, engine 经 buildBundleAndSectionRegistry(observer) 透传 observer (NewSectionRegistryWithObserver 新构造器保持 NewSectionRegistry 向后兼容). (2) 默认 bundle 新增首个生产 VolatileSection: default_bundle.go 追加 mcp_servers volatile section, engine.buildSystemPromptWithContextmcpMgr.ClientNames() + GetClient(...).IsAlive() 收集状态经 SetMCPServerStatuses 注入 — 真实生产路径 (非 overlay 嫌疑) 每轮触发 CacheBreak 分支. (3) godoc 从"文档和代码审查"升级到"文档 + 代码审查 + 运行时诊断 + 未来事件驱动失效". 5 条 regression test 锁意图 (CacheBreak_EmitsDiagnosticWithReason / NonCacheBreak_NoDiagnosticEvent / NilObserver_NoCrash / DefaultBundle_MCPServersVolatile_EndToEnd / DefaultBundle_MCPServersVolatile_EmptyStatuses_NoBlock). 用户反向挑战"scanner 过 + test 绿 ≠ runtime 真触发"后补第二层 — 彻底根除"死代码真 wire"嫌疑 (2026-04-19)
  • [x] pkg/daemon.DaemonConfig.CrashRecovery — daemon 崩溃恢复配置真 wire 端到端 (2026-04-20): 子组件 CrashRecovery 独立完整但 DaemonManager 从不实例化, 字段被声明却零消费. DaemonManager 新增非导出 crashRecovery *CrashRecovery 字段, NewDaemonManager 里 if cfg.CrashRecovery != nil → NewCrashRecovery(*cfg.CrashRecovery) (唯一产线 SelectorExpr read 点, 字段透传: MaxRetries/InitialDelay/MaxDelay/Multiplier/OnCrash/OnGiveUp). 消费点 receiveMessages 的 ClientMessagePrompt 分支: 若 crashRecovery 非 nil 则 dm.crashRecovery.RunWithRecovery(dm.ctx, sessionID, runFn) 包裹 handlePrompt 调用, 超过 MaxRetries 后 log "gave up". 新公共 helper runWithRecover(fn func()) error 是 panic→error 桥接: 正常完成返 nil (CrashRecovery 不重试成功 prompt, 防御 "每次 prompt 附赠 3 次重试" 类回归), panic 被 defer-recover 捕获转 fmt.Errorf("prompt handler panicked: ..."). 无 crashRecovery 时保持原语义 (panic 继续传播, daemon 级监管决定去留). 5 条 regression test 一 sub-claim 一 test: TestDaemonManager_CrashRecoveryDisabled_NoWrapper (nil policy 不构造) / TestDaemonManager_CrashRecoveryEnabled_ForwardsConfig (4 字段 MaxRetries/InitialDelay/MaxDelay/Multiplier 透传) / TestRunWithRecover_NormalReturnsNil (正常 fn 返 nil, 防御错误重试) / TestRunWithRecover_PanicToError (panic 冒泡 non-nil, CrashRecovery 才能触发) / TestCrashRecovery_WrapsPanickingPromptRun (端到端: runWithRecover + RunWithRecovery 组合, OnCrash 每 attempt 一次 × 3 + OnGiveUp × 1 + fn 调用 MaxRetries+1=3 次). 选 C1 panic-recover 路径而非 C3 形式 wire (后者同构"走捷径过 scanner"被禁). baseline 387 → 386
  • [x] pkg/daemon.DaemonConfig.HeartbeatInterval + HeartbeatTimeout — DaemonManager 心跳 2 字段真 wire 端到端 (2026-04-20): 子组件 HeartbeatService 此前独立完整但 DaemonManager 构造器从不启动它, 两字段被声明却零消费. DaemonManager 新增非导出 heartbeat *HeartbeatService 字段, NewDaemonManager 里 if cfg.HeartbeatInterval > 0 → NewHeartbeatService({Interval: cfg.HeartbeatInterval, Timeout: cfg.HeartbeatTimeout}, dm.closeSession) (两字段唯一产线 SelectorExpr read 点). 生命周期钩子全 wire: Start 里 heartbeat.Start (启 ticker goroutine), Shutdown 里 heartbeat.Stop (取消 ticker ctx), getOrCreateSession 尾部 heartbeat.Register (加入扫描 map), closeSession 头部 heartbeat.Unregister (正常关闭路径不留 stale ID, 否则下次 tick 对已失踪会话再调 closeSession), receiveMessages 收到客户端消息时 heartbeat.Beat (任何 inbound 视作活性). 运行时行为真改变: 半死连接被 ticker 扫出并真 close (释放 pool 槽 + 清 isolation + 断 conn). 3 条 regression test 一 sub-claim 一 test: TestDaemonManager_HeartbeatDisabled_NoService (interval=0 不泄漏 HeartbeatService goroutine) / TestDaemonManager_HeartbeatEnabled_ForwardsConfig (interval 和 timeout 两字段均被读并透传到 HeartbeatConfig) / TestDaemonManager_CloseSession_UnregistersFromHeartbeat (生命周期钩子真流到 heartbeat 面). baseline 389 → 387
  • [x] pkg/engine.Config.ElicitationHandler + pkg/engine.ElicitationField.Default — MCP elicitation 端到端接线 (2026-04-19): 新增 engine/elicitation_adapter.go 桥接 mcp.ElicitationHandler ↔ engine.ElicitationHandler (mcp 包不能 import engine, 必须 adapter); engine.Newmcp.NewManager 后立即 mgr.SetElicitationHandler(adaptElicitationHandler(cfg.ElicitationHandler)) (nil 接 NoopElicitationHandler 退化为 auto-cancel, 之前是 silent dead). 正向转 mcp.ElicitationProperty.Default (any) → engine.ElicitationField.Default (string) (string 透传 / number/bool 走 fmt.Sprint / 复合走 json.Marshal 兜底); 反向 accept 时 schema-defaulted optional fields 缺失自动 fill (web 表单 UX, MCP spec auto-fill 未规定但便利消费层); decline/cancel 透传空 Content. 6 个 sub test 锁 5 个 godoc 承诺. 顺手 wire 14 条 baseline drop (415→401): 主目标 2 + adapter 读 mcp.Elicitation{Schema,Property} 全字段 6 + 反向转换读 engine.Elicitation{Field,Response} 3 + 前序 ContentBlock.MarshalJSON commit 副作用 3
  • [x] pkg/engine.TeamConfig.PermissionHandler — Worker→Leader 权限冒泡 P1 端到端 wire (2026-04-19): SubAgent 加 permissionChecker (ModeDefault) + pendingPermissions map; bubblingHandler 把 permission.Request → MsgPermissionRequest → router.Send → 阻塞等响应; Engine.runLoop poll 加 MsgPermissionRequest 路由分支 (调 cfg.PermissionHandler / nil 自动批准 / 发 MsgPermissionResponse 回); permission.Response 加 UpdatedInput 字段; PermissionHandler interface 扩 (approved, updatedInput, reason); 顺手 wire PermissionRequestPayload × 5 + PermissionResponsePayload × 4 共 10 条 baseline drop. 端到端 test 锁 godoc 承诺 ("nil 自动批准" / "消费层实现的权限确认" 两条)
  • [x] pkg/evolve.Config.AutoApproveReadOnly — 真 wire 为审批 short-circuit: (1) Evolver struct 加 autoApproveReadOnly bool + New() 赋值. (2) Propose() 审批逻辑前置 short-circuit: autoApproveReadOnly=true && isReadOnlyEvolution(proposal) → approved=true 绕过 approvalFunc. (3) isReadOnlyEvolution helper 刻意收窄, 当前 EvolveNewSkill 符合 (只写 markdown, 不执行代码); EvolveNewTool/EvolveOptimize/EvolveSelfAdjust 即便标称 read-only 也不 auto-approve, 因其内容会执行代码或改运行时. (4) 新 evolution_auto_approved observer 事件与 evolution_approved 分离, 让安全审计可按事件名区分自动批准 vs 人工批准路径. (5) godoc 升级完整三段: 范围定义 / 安全人机工程权衡 / 其他进化类型永不放宽. 3 条 regression test (ApprovesSkillWithoutApprovalFunc / DoesNotBypassForNonReadOnlyTypes × 3 子 case / FalseKeepsHumanGate) 锁安全边界 (2026-04-19)
  • [x] pkg/engine.Skill.AgentType + Skill.FilePath + Skill.Version + pkg/engine.AgentDefinition.MaxTurns — Skill wire 四字段端到端 (2026-04-19 两轮, 从 C 档"可能残留"重分类为 B 档真 wire): 第一轮 (形式 wire, 被用户反向挑战"真实现吗"推翻): 三字段加 SetAgentRegistry/SetObserver setter + invokeFork 只填 AllowedSubAgentTypes + Invoke 发 skill_invoked. 用户核实暴露 AgentType godoc "fork 模式下使用的 Agent 类型" 只兑现 1/7 sub-claim, test 绿但锁的是形式 wire, 不是 godoc 承诺. 第二轮 (真 wire, 本条最终状态): 按 sub-claim checklist 补齐 — (a) cfg.AllowedTools = ResolveToolset(def, parentTools) 四层过滤 (parent ∩ def.AllowedTools → Background 收窄 → 去 DisallowedTools ∪ globalDisallowed) (b) DisallowedTools 随 a 生效 (c) cfg.Model fallback 到 def.Model (d) cfg.MaxTurns fallback 到 def.MaxTurns, 都 0 时兜底 10 (e) AllowedSubAgentTypes (原有) (f) skill 声明 AllowedTools 时与 def 取交集防逃逸 (g) 未命中发 skill_fork_unknown_agent_type (原有). SkillRegistry 加第三个字段 parentToolNames func()[]string + SetParentToolNames setter, engine.New 注入 func() []string { return eng.tools.Names() }. 三字段 godoc 升级双语完整 Wire 段 (AgentType 段枚举 (a)-(g) 全部). 11 条 regression test 覆盖全部 sub-claim (每条 test 锁一条 sub-claim, 防"一条 test 掩盖另一条缺失"): AppliesResolveToolsetWhenSkillSilent / SkillToolsIntersectWithDef_NoEscape / ModelFallsBackToDef / SkillModelOverridesDef / MaxTurnsFallsBackToDef / BothMaxTurnsZeroDefaultsTo10 / PopulatesAllowedSubAgentTypes / UnknownEmitsDiagnostic / EmptySkipsRegistryAndNoDiagnostic / InMemorySkill (file_path="" 显式保留) / FileSkill (路径+版本). baseline 401 → 397 (4 条实 drop: Skill.AgentType/FilePath/Version + AgentDefinition.MaxTurns side-effect 激活). 工作流教训: "先核实真实现再写 test" (feedback_verify_real_wire_before_test.md) 新增

C 档 (重评估, 可能删 / 可能 scanner 假阳性) — 5 条 ✅ 全部消除 (2026-04-25 L683 收尾)

  • [x] pkg/evolve.LLMCallOpts.Temperature — Temperature + TopP cross-provider passthrough 端到端 wire (2026-04-25, L683 子包 3 commit): flyto.Request.{Temperature,TopP} *float64 加字段 + flyto.Float() helper, 7 provider (anthropic / openai / minimax / gemini / ollama / lmstudio / openrouter) 全部接通 sampling 旋钮 (此前 7 provider 的 Config 与 Stream 一律不传, 是 day-1 产品功能缺失而非简单 wire). 纯 passthrough + 极小特例: 越界一律不在 flyto 层 clamp 由上游 4xx 自然冒泡 (业界共识 — Vercel AI SDK/LangChain/instructor 全 passthrough), 仅 1 个 in-Request 已知冲突预拦 (anthropic + NeedsThinking + Temperature != 1.0 / TopP < 0.95 → silent override 1.0 + parameter_overridden WarningEvent). 加 OpenAI o-prefix 检测 (server 4xx 已清晰, litellm drop_params 实证维护 burden 不值). 0 是合法 deterministic 值, *float64 + omitempty 干净分离 nil/0. ADR rule of two: TopK/Seed/penalty 走 follow-up TODO 等真消费者驱动. evolve 串通: LLMCallOpts.TopP + WithTopP + buildMetameta[top_p] (ParameterEvolver 与 evaluator 现可追溯完整 sampling 配置). FlytoLLMClient adapter 把 zero=未设 翻译到 nil, 非零 → flyto.Float(v). 23 tests 全绿 (17 wire/provider + 6 evolve), baseline 220 → 219 (LLMCallOpts.Temperature 实际 drain). 3-agent 并行 review (调研 7 provider API + 5 OSS prior art, 质疑扩 contract 必要性 4 道硬问题, 设计 4 备选方案 + 推荐 + 升华 + tracked debt 预测) reconcile 后开干. 上一会话归 C 档重评估的判断本会话被产品需求驱动翻盘 (provider 层产品力 day-1 缺失, evolve 进化闭环).
  • [x] internal/syslib/bash.Assignment.Name — verify 为真 write-only (parser 写, extract.go 只用 Value 不用 Name, 消费零). 不删, 改真 wire 带安全价值: pkg/permission/bash_security.godangerousEnvVarPrefixes map (LD_PRELOAD / LD_LIBRARY_PATH / DYLD_INSERT_LIBRARIES / IFS / BASH_ENV / PROMPT_COMMAND / PATH / PYTHONPATH / NODE_OPTIONS / GIT_SSH_COMMAND 等 Linux/macOS 后利用常见向量). IsDangerousCommand + AnalyzeDanger 遍历 c.Assignmentsassign.Name (case-insensitive) 拦截 env-var 前缀注入 (LD_PRELOAD=./evil.so curl ... 过去会绕过所有检查因 ExtractCommandName 剥前缀, 现在精确抓). DangerInfo.Reason 含具体 env-var 名, Pattern 含完整 NAME=VALUE 前缀供 TUI / 审计渲染. 顺带 drop CommandInfo.Assignments (同字段路径下 selector read). 14 条 regression test (9 dangerous / 3 benign 不过拦 / 1 case-insensitive / 1 AnalyzeDanger 结构化) (2026-04-19)
  • [x] pkg/context.BundleKey.ModelFamily — verify 为 struct-as-map-key 假阳性 (BundleKey 作 map[BundleKey]PromptBundle 键 + key == (BundleKey{}) 值比较, 字段参与 hash/eq 但无 SelectorExpr read). 真 wire 为 (k BundleKey) String() string 方法, 返回 "<family>/<scenario>" 用于日志 / 错误消息 / observer 事件 payload — 字段在 String() 内被 SelectorExpr 读, 同时给运维人类可读输出. Table-driven regression test 5 case (default / industry / zero_value / partial_empty_family / partial_empty_scenario) (2026-04-19)
  • [x] pkg/context.BundleKey.Scenario — 同上, 随 BundleKey.String() 一并 wire (2026-04-19)
  • [x] pkg/context.MessageGroup.Index — verify 为 write-only (非 test 代码字段被多处写, 0 处读). 真 wire: compact_token_gap_stride observer 事件扩 dropped_group_indices []int payload, Compressor 在 stride 分支 slice 重建之前快照每个被丢 group 的 Index 数组. 运维 grep 事件能看到哪几个 API-round group 被削 (如 [1, 2, 3]) 而非仅 count. regression test 扩断言: 有 >=1 个 index / 无 0 (preamble 保留不变量) / len(indices)==dropped_groups 同源不变量 (2026-04-19)

2026-04-23 validator 子包引入 ✅ 本 session 5 commit 内消除

Commit 2 新增 core/pkg/validator/ 子包的 RuleValidator 后, scanner 检出 8 个 validator 字段 dead (DiffInput.SourceTool + Verdict.{Approved, Details, PolicyVersion, Reason, Score, Severity, ValidatorName}). 属 "设计意图先行, 实现未连" — 接口 + 数据类型先落地, 参考实现逐步真 wire:

  • Commit 1 (30639fb) 接口 + 数据类型: 引入 8 字段, 零消费
  • Commit 2 (e6239c2) RuleValidator: 只 set 不 read, baseline 212 → 220
  • Commit 3 (ca6309e) LLMValidator: renderPrompt 读 SourceTool + parseLLMVerdict normalise 读 Severity, drain 2, baseline 220 → 218
  • Commit 4 (8b1d556) CompositeValidator: childDetail 读 Approved / Score / Details / Reason, drain 4, baseline 218 → 214
  • Commit 5 (本 commit) ValidatedTool Decorator: formatBlockedOutput 读 ValidatorName + PolicyVersion, drain 2, baseline 214 → 212

Exit criteria 达成: baseline 回到 212, 8 字段全部真 wire, 无残留 validator 字段 tracked debt. pkg/validator/ 子包 (Validator 接口 + RuleValidator / LLMValidator / CompositeValidator 3 参考实现) + pkg/tools/builtin/validated_tool.go (Decorator + VerdictSink hook) 一套基础设施就绪, 为消费层 P1 L434 ML 验证器接入 (platform 接外部 ML backend) 和 P1 L435 熔断器 (状态机消费 VerdictSink 信号) 提供 core 接口层. 这 2 条 P1 仍在 open 状态, 核心已降; 待 platform 层完成.

2026-04-24 nil Validator 严格化 + AlwaysApprove 显式 opt-out (C1 / core 安全链 enforcement)

候选 A (platform 集成 Validator + Breaker 端到端) 启动前, 先把 core 层的"显式审批不能隐式继承"契约落到编译/构造期可见 — 产品拍板: platform 装配框架无默认, industry 不 wire 就是不装. 为避免 "industry 以为开了审批实际忘 wire" 的静默安全假象, core 层追加两件强约束:

  • pkg/validator/always_approve.go 新增 AlwaysApprove{}: Validator 接口显式 opt-out 实现, 忽略 diff 永远 Approved=true, ValidatorName="always-approve" 作哨兵标识供审计 dashboard 过滤.
  • pkg/tools/builtin.NewValidatedTool 构造期对 nil Validator / nil extractor panic (而非首次 Execute 时静默 nil-deref). Industry 刻意 opt-out 必须显式传 validator.AlwaysApprove{}, code review 能抓到, 日志里 ValidatorName 能看到.

4 新 test (TestAlwaysApprove_* × 4 + TestNewValidatedTool_Nil*_Panics × 2 + TestValidatedTool_WithAlwaysApprove_PassesThrough) 全绿, baseline 212 不变 (AlwaysApprove 空 struct 无字段). 这条是 Agent Teams 3 角色 review 里"质疑"角色的 TOP#2 观察 — 独立于"装配框架做不做"决策, 属于 core 层 enforcement 职责, 无论 platform 怎么做都应落地.

消费层 L434/L435 P1 核心前置已就绪 (core 接口 + Decorator + 熔断器 + 显式 opt-out + 启动期 fail-fast), platform 层装配 / 观测 / gRPC 暴露继续 C2-C4.

2026-04-24 platform/common safetychain 装配层 (C2)

产品拍板抽象方向 (行业 platform 自选 LLM/ML backend + 熔断作用域) 后, 在 platform/common/safetychain/ 公共包落装配框架. 无默认, 显式 opt-in; Go 行业可直接 import, C# logistics 走 gRPC (C4).

  • Assemble(inner, v, extractor, scope, sink) tools.Tool: 一行装配 inner Tool + Validator + (可选) breaker scope + (可选) 观察 sink, 返回可注册的 tools.Tool. Validator / extractor 为 nil 时 core 构造期 panic (C1 保证), industry 显式 opt-out 必须传 validator.AlwaysApprove{}.
  • BreakerScopePolicy (func type): 决定每个 Tool 要哪个 breaker. 刻意选函数类型不是 interface — 质疑角色认为 3 个无状态实现抽 interface 是 YAGNI, 行业想自定义直接写同形状函数.
  • 3 个内置工厂: NoOpScope() (不挂 breaker) / DestructiveOnlyScope(cfg) (destructive tools 共享 1 breaker, 对齐 "写路径整体保护" 语义) / PerToolScope(cfg) (每 tool 独立 lazy 分配).
  • BreakerRegistry: 每个工厂返回 (policy, registry) 双元组, registry 给 C3 admin 端点消费 (Snapshot() map[string]circuitbreaker.State + Names() []string sorted).
  • fan-out 顺序契约: Assemble 同时拿到 industry sink 和 breaker sink 时, industry sink 先 (记录样本到 store 不丢), breaker sink 后 (推进状态). C3 观察 store 后消费 verdict 流水, 这个顺序保证审计不缺.

16 test (TestAssemble_* × 6 + TestFanOut_AllCombinations + scope 系列 × 9) 全绿. core baseline 212 不变 (C2 只在 platform/common 加代码, 不碰 core).

消费层 L434/L435 现在 core + common 装配层就绪, 剩 C3 观测端点 + C4 gRPC 暴露. 端到端"industry 选 backend + Assemble + gRPC 暴露"链路逐步闭合.

2026-04-24 platform/common admin 观测端点 (C3)

安全链运维可见性: VerdictStore 接口 + RingStore bounded 内存实现 + admin HTTP 2 新端点.

  • safetychain.VerdictStore 接口: Record(tool, verdict) (签名对齐 builtin.VerdictSink 可直接作为 Assemble sink 不需 wrapper) + Snapshot() []VerdictRecord 返拷贝. 实现方并发安全.
  • safetychain.RingStore: bounded 内存环形缓冲, O(1) Record 热路径无分配. 容量显式参数无默认 (各行业 Warn 流量差异大), clock 可注入 (复用 circuitbreaker.Clock, 测试 deterministic).
  • admin.New(version, verifier, opts ...Option): 改 variadic 向后兼容. 新 WithSafetyChain(store, registry) option, 任一 nil 视作不启用 (配对契约阻止接线错漏).
  • 2 新端点 GET /admin/safetychain/{verdicts,breakers}: 仅 WithSafetyChain opt-in 时挂载, 默认 404. 鉴权和 /admin/tenant 同级 (verifier 非 nil 时套 auth middleware, Verdict.Reason 可能带运营敏感信息). authed() helper 收敛鉴权策略.
  • 输出形状: verdicts → []VerdictRecord{Timestamp,ToolName,Verdict} JSON; breakers → [{name,state}] 按 name 排序 JSON (dashboard 轮询 diff 稳定). 空 snapshot 返 []null 让下游无需 null check.

11 test 全绿 (5 RingStore + 6 admin 端点, -race 通过). core baseline 212 不变. 端到端 consumer 示例 CHANGELOG 里写了.

消费层 L434/L435 现在 core + common (装配 + 观测) 全就绪, 剩 C4 gRPC 暴露给 C# logistics.

2026-04-24 platform/common 安全链 gRPC (C4) — 候选 A 端到端闭合

给 C# logistics (以及未来 Go 行业) 一条原生 gRPC 通路读安全链状态, 和 C3 admin HTTP 同源双面暴露. 沿用 HealthService 已经走的 proto + genpb + grpcapi 模式.

  • proto: platform/common/internal/api/grpc/proto/safetychain.proto, 2 RPC (ListVerdicts / ListBreakerStates), csharp_namespace 和 go_package 对齐 health.
  • gen: 一次性 install protoc-gen-go + protoc-gen-go-grpc$GOPATH/bin 后 protoc 生成到 gen/ (共享 package genpb).
  • server: grpcapi.SafetyChainServer{Store, Registry} 依赖注入, 与 admin.WithSafetyChain 消费同一对 VerdictStore + BreakerRegistry.
  • cmd/common wire: 默认构造 RingStore(1024) + NoOpScope registry, 同时挂 admin HTTP (若 --http-addr 非空) + gRPC. 默认启用让 C# stub 能即时对接.

设计决策: Verdict.Details 刻意省略 (proto3 无 map 等价); Severity / State 用 string 不用 enum (第三方 Validator 前向兼容); nil Store / Registry 返空列表不报错 (观测端点不把"没东西"和"服务挂"混淆).

6 test 全绿含 bufconn 端到端 gRPC round-trip 作为 C# stub 互操作代理证据. core baseline 212 不变.

候选 A 端到端闭合: 整条 "Agent → staging → Validator → ValidatedTool → CircuitBreaker → WMS API" 安全链 core + common (装配 + HTTP 观测 + gRPC 暴露) 全部就绪. 剩的工作落到 industry platform 侧: 选 LLM / ML backend, 装配 Tool 到 engine 消费层, 把真实 Verdict 流水填进 common 的 VerdictStore. L434/L435 P1 核心路径已清晰, 可落 platform 层实施时直接消费.

2026-04-25 L569 反事实子包引入 — 1 字段合法 tracked debt (与 shadowdb / staging 同形)

L569 缩水 6 commit 完成时, scanner 检出 1 条新 dead — tools.Metadata.RequiresReverseThinking. 该字段在 platform/common/safetychain/reverse_thinking_hook.goLookup 回调里被读 (hook 按工具名查 Metadata 看 RequiresReverseThinking 决定是否触发反向思维), 但 platform/common 是独立 module, scanner 视野不及.

1 条合法 tracked debt 终态分类: 与 shadowdb 4 + staging 4 + counterfactual Deliverable (本身经 Reflector test 读, 不入此 debt) 同模式 — core 内部无 reader, 外部 consumer (platform/common 装配层 + 行业 platform 自填 hook) 在 scanner 视野外.

tools.Metadata.RequiresReverseThinking — 与已有 RequiresCheckpoint 同形 (memory feedback_exported_field_delete_needs_review.md). Tool 自描述 opt-in flag, 让消费 hook 链 (platform/common/safetychain.ReverseThinkingHook 是 reference impl) 决定是否触发反向思维. Tool 作者声明 RequiresReverseThinking: true 即可在 opt-in hook 注册的 session 内启用. 由于 platform/common 是独立 module + 行业 platform 实现自己的 Lookup 回调, scanner 永不会从 core 内部看到 reader. 终态接受为 tracked debt, 与 staging Record 4 字段 / shadowdb Session 4 字段同分类.

baseline 219 → 220 (+1 合法 tracked debt), 不计入应 drain 指标.

2026-04-25 shadowdb 子包引入 — 4 字段合法 tracked debt (D-A 档同形)

shadowdb 子包 (core/pkg/shadowdb/) 实现 L437 影子表管理. 3-commit 落地后 (2c84209 / e140952 / 8951c6e), scanner 检出 Session struct 4 个 exported 字段 dead — 与 staging Record 4 字段 (TechVerdict/BizVerdict/ExecutionError/ExecutionProof) 同形, 是 "core 内部无 reader, 外部 consumer 在 scanner 视野外" 的合法 tracked debt, 不走 drain 路径.

4 条合法 tracked debt 终态分类 (按 memory feedback_exported_field_delete_needs_review.mdfeedback_scanner_ignores_test_readers.md):

  • Session.ID — 外部 consumer 在自己 SQL 里作 session_id filter 值 (例 WHERE session_id=? 后绑定 sess.ID). core makeCloser 走 sessionID 闭包参数不读 sess.ID, 单源真理在 opener 注册表
  • Session.CreatedAt — 外部 observability dashboard 读会话开启时刻. core Reap 走 trackedSession.createdAt 不读 sess.CreatedAt
  • Session.ShadowTable — 外部 consumer 在 SQL 里引物理表名, decorator 与 EnforceSessionFilter 也可读取. core makeCloser 走 trackedSession.shadowTable 闭包不读 sess.ShadowTable
  • Session.DB — 外部 consumer 经 sess.DB.ExecContext / QueryRowContext 跑 SQL. core 内部经 opener.db 走, 不通过 session 暴露

均为外部 SDK / 平台层 consumer 的 API surface, scanner 视野不覆盖. 无强行加 internal reader 路径 — 任何强加都是 "走捷径过 scanner" (memory feedback_scanner_pass_vs_intent_done.md), 设计本身就是 opener 持单源真理 + Session 是 immutable snapshot.

baseline 216 → 220 (+4 合法 tracked debt), 与 staging 引入时同模式 (212 → 216 +4 合法). 不计入应 drain 指标.

2026-04-24 staging 子包引入 (commit 1) — 后续 3 commit 内消除

staging 子包 (core/pkg/staging/) 实现 "Agent 决策 → staging → 审批 → 生产" 链路中 "staging" 一环的状态机与 Record 契约. commit 1 按 "设计意图先行 tracked debt" 模式 (memory feedback_design_intent_first_tracked_debt.md) 只落类型 + 状态机, reader 在 commit 2 (Store 接口 + InMemoryStore) 与 commit 3 (Engine 混合控制状态机) 里进来. baseline 212 → 223 (+11 字段).

字段 将在哪个 commit 被 reader 消费
Record.ID commit 2 ✅ (InMemoryStore.Stage 分配 + Get/List 返回)
Record.SessionID commit 2 ✅ (Stage 校验, List/ListBySession 过滤)
Record.State commit 2 ✅ (Update/Mark 方法读源状态 + 写新状态)
Record.Diff commit 3 (Engine.ValidateTech/ValidateBiz 调 Validator.Validate(ctx, r.Diff))
Record.CreatedAt commit 2 ✅ (List SortFunc 按 CreatedAt 升序)
Record.UpdatedAt commit 2 ✅ (ListStuck 过滤 + SetUpdatedAtForTest 写 + 所有 Update/Mark 写)
Record.Metadata commit 2 (Store 透传存取), commit 3 (DependencyGuard 读 hint)
Record.TechVerdict commit 3 (Engine.ValidateTech 写后 Engine test 断言 + platform 侧 audit dashboard 读)
Record.BizVerdict commit 3 (Engine.ValidateBiz 写后 Engine test 断言 + platform 侧 audit dashboard 读)
Record.ExecutionError commit 3 (Engine test 断言 MarkFailed 后 reason 保留)
Record.ExecutionProof commit 3 (Engine test 断言 MarkExecuted 后 proof 保留 + 幂等 first-write-wins 验证)
Query.SessionID / States / Since / Limit commit 2 ✅ (InMemoryStore.List 过滤实现)

commit 2 后进度 (2026-04-24): 9/11 drain, baseline 223 → 218. Commit 2 新增 4 条 dead (TechVerdict / BizVerdict / ExecutionError / ExecutionProof), 预期在 commit 3 Engine test 断言写后字段值时收割. Commit 2 后 staging 包净 dead 6 条: Record.Diff (原 commit 1) + Record.Metadata (原 commit 1) + commit 2 新增 4 条.

commit 3 后最终进度 (2026-04-24): 2 条 drain (Record.Diff 由 Engine.ValidateTech/Biz 读, Record.Metadata 由新增 TenantDenyGuard 内置实现读), baseline 218 → 216. 4 条 (TechVerdict / BizVerdict / ExecutionError / ExecutionProof) 仍 dead — 最初预估 "Engine test 读字段值就能 drain" 错, scanner 只扫非 test 代码的 reader, _test 里 read 不算.

4 条合法 tracked debt 终态分类: 按 memory feedback_exported_field_delete_needs_review.md 模式, 这 4 条是"外部 audit dashboard 消费, core module 无内部 reader"合法形态: - Record.TechVerdict / BizVerdict: 平台层审计 UI 读 Severity/Reason/ValidatorName 展示"哪一层拒绝/为何" - Record.ExecutionError: 平台层 watchdog / 审计追溯读生产失败原因 - Record.ExecutionProof: 平台层审计链完整性验证 (核对 WMS 返单号与 staging 记录一致)

均无 core 内部消费路径. 走 D-A 档 路径 (不 drain, 保留 exported 字段 + 进 tracked 但不 drain 计划).

Exit criteria 达成 (调整: 原目标 ≤ 212 不现实): staging 包 core-level scanner-clean, 剩余 4 条是 exported 字段 cross-module 外部消费 (platform 审计侧), 符合 memory 所述 "exported 字段无 consumer 是 scanner 视角局限" 合法形态.

2026-04-20 baseline 386 sweep 结论 (D 档候选清单, 待路线决策)

alpha.7/8 drain 完 A/B/C 档 26 条 (baseline 438 → 386, 净 -52 含副作用激活). 本次 sweep 对 386 剩余按 tag + struct 归约的二次 triage 结果:

事实层: - 193 / 386 条有 json tag: 按 feedback_exported_field_delete_needs_review 默认视作第 3 类 (parser-to-marshal-to-SDK/plugin downstream, scanner 视野不覆盖外部消费者). 不列入 drain, 不删. - 193 / 386 条无 tag: 真候选池, 按 struct 归约约 20-25 个主题 (见下).

D-A 档 2026-04-20 重分类结论: 初版列 8 主题 / ~36 字段, 按 push / pull / callback 三形态 verify 后 (见 docs/api-reference.md 新章节 "API 消费形态"), 7/8 条是合法外部 API 表面, 消费者在 scanner 视野外, 不是实现债务. 真 drain 对象仅 engine.SessionStats 一条 (已完成). 其余 7 条保留, 不再列入 "应 drain" 队列, 原因见每条后缀标注.

D-A 档 (已分类, 1 真债务 + 7 合法外部 API):

  • [x] engine.SessionStats (TurnCount/InputTokens/OutputTokens/CostUSD/MessageCount) — 真 wire 为 pull + push 双路径 (2026-04-20): Session.Stats() 已有 pull API 保留 (消费者随时读快照), 新增 push 通道 session_cost_threshold_crossed observer 事件. trackEvents 尾部 30 行代码抽取为新方法 applyTurn (方便测试, 也解耦 emit 逻辑). 引入包级常量 costThresholdsUSD = []float64{1,5,10,50,100} 和 helper crossedCostThresholds(last, cost) → []float64 (返回 half-open 区间 (last, cost] 内的升序档位, 支持单轮跨多档). Session 加 lastCostThresholdEmitted float64 guard 防同档重复 emit. 锁策略: 锁内构造 stats 快照 + 组 payload (字段经 stats.CostUSD / TurnCount / ... SelectorExpr read, 这正是把 5 字段从 scanner "声明未读" 升为 "运行时真读" 的依据), 释放锁后再 emit (对齐 Close() 的 unlock-then-emit 模式, 避免持锁跨消费者回调). SessionStats struct godoc 升级双语, 新段落明确两条消费路径的各自定位 + 同源不变量 ("两路径不重叠: 要快照走 pull, 要成本告警走 push"). 5 条 regression test 一 sub-claim 一 test: Stats 5 字段多轮累加准确 / 首次跨档 payload 完整 (7 key 全 assert) / 已跨档同区间不重发 / 单轮跨多档升序每档一次 ($0→$15 发 $1/$5/$10) / 无跨越无事件 (0.9 累计不 emit). baseline 386 → 381.
  • [~] engine.PlanApprovalEvent (SessionID/Plan/FilePath/Steps/Approve/Reject) — 同步回调 (callback) 形态, 合法外部 API 保留 (2026-04-20 重分类): 经 ApprovalPolicy.RequestApproval(ctx, event) 传递, Approve/Reject 是消费者调用的回调函数字段. ApprovalPolicy 接口由 CLI 终端审批 / SaaS 企业审批工作流 / 测试 NoopApprovalPolicy 等外部消费端实现, scanner 视野不含外部 policy. 见 docs/api-reference.md "API 消费形态" 章节形态三.
  • [~] engine.CheckpointSuggestedEvent (Input/RiskPattern/RiskReason/ToolCallID/ToolName) — 订阅 (push) 形态, 合法外部 API 保留 (2026-04-20 重分类): 实现 flyto.Event 接口 (EventType() string 方法), 走 Engine.Run 返回的 <-chan flyto.Event 流. 消费者 for evt := range ch + type-switch case *CheckpointSuggestedEvent 读字段. scanner 视野不含外部消费端.
  • [~] permission.DenialStats (ConsecutiveDenials/LastDeniedInput/LastDeniedTool/TotalDenials) — 调取 (pull) 形态, 合法外部 API 保留 (2026-04-20 重分类): 由 DenialTracker.Stats() DenialStats 返回, 消费者 (CLI / SaaS / 测试 harness) 主动调读字段, 和 SessionStats 同构 pull API. scanner 视野不含外部 pull 调用.
  • [~] permission.ClassifyResult (DurationMs/Stage/Thinking/Usage) — 调取 (pull) 形态 (2026-04-20 重分类): Classifier.Classify(ctx, req) (*ClassifyResult, error) 返回值, 消费者主动调. 合法外部 API.
  • [~] engine.InboxMessageEvent (Data/Meta/ToolUseID/Type) — 订阅 (push) 形态 (2026-04-20 重分类): 实现 flyto.Event, 走 Engine.Run Event channel. 消费者经 case *InboxMessageEvent 读. 合法外部 API.
  • [~] flyto.TeammateMessageReceivedEvent (From/To/Type/MessageID/PayloadSize) — 订阅 (push) 形态 (2026-04-20 重分类): Agent Teams peer-to-peer 事件, 实现 flyto.Event. router consumer 走 channel + type-switch. 合法外部 API.
  • [~] engine.ElicitationField (Description/Required/Title/Type) + ElicitationRequest (Fields/Message/ServerName) — 同步回调 (callback) 形态 (2026-04-20 重分类): 经 ElicitationHandler 接口 (NoopElicitationHandler 已实现, 生产 handler 由 CLI/SaaS 消费端提供) 消费. 合法外部 API.

D-A 档分类结论: 上述 [~] 标记条目不列为实现债务, 不计入应 drain 指标. 它们在 baseline 381 里占据 35 条, 保留于 scan-baseline.json 意为 "scanner 知道这些字段在本 module 无内部 consumer, ratchet 保证未来新加的类似位置字段不会悄悄漏掉", 不是"设计未连". 未来新增外部 API 载体时 baseline 会增长, 按 docs/api-reference.md "API 消费形态" 章节判据分类即可, 不走 drain 路径.

D-B 档 (特性框架 wire, ~12 主题 / ~70 字段) — evolve + 内部结构:

  • [x] 缓存族 3 个 struct 真 drain / 归档 (2026-04-20): 按用户 4 问法则 (接口有消费? 必要? 外部? 内部加?) 分三条路径走完:
  • engine.FileCacheEntry 6 字段真 drain: Path 消除"lruEntry.path 双重存"冗余 (RecentFiles 改读 entry.Path, 删 lruEntry 里那份, FileCacheEntry.Path 成 single source of truth); ContentHash/Size/LineCount/ReadAt/WasModified 通过新增 FileStateCache.Info(path) (FileCacheEntry, bool) pull API (返回值副本, 外部消费安全) + reminders.go CheckFileModifications 升级消费元数据 (reminder 文本带 "at-read-time: N bytes / M lines / hash XX, read T ago" + WasModified 已标记提示), 从"声明未读"激活为真 consumer read. FileChangeChecker 接口同步加 Info 签名. godoc 双语解释 6 字段消费路径 (pull via Info/Get/Peek).
  • engine.CacheStats 3 字段 (Entries/MaxSize/Evictions) [~] 归档: 已在 docs/api-reference.md 列为 pull API (和 SessionStats/DenialStats 同列), godoc 升级双语写清消费形态 "调取 pull, 消费者主动调 FileStateCache.Stats() 读字段, scanner 视野内无内部 reader 是期望状态, 不强加内部 reader 过扫描器". 不减 baseline.
  • tools/builtin.FileStateCacheEntry 5 字段 [~] 归档: form 3 callback (FileStateCacheRecorder.RecordState 由外部实现接收), 和 engine.PlanApprovalEvent/ElicitationField 同构. godoc 升级双语讲清字段给外部 Recorder 消费的契约 (ContentHash/Size/LineCount/ModTime/IsPartialView 各自用途). 不减 baseline. baseline 367 → 361 (-6, FileCacheEntry 全部 drain). 下次跑扫描器时 8 条保留 (CacheStats 3 + FileStateCacheEntry 5) 显示归档分类,不算债务.
  • [x] pkg/evolve.FeedbackRecord (Entity/Metric/Value/Confidence/Timestamp/Meta) — 5 字段 drain 走类型合并路径 (2026-04-20): 按 4 问审判发现 evolve 包内同时存在两个字段完全相同、语义重合的 struct — FeedbackRecord (ParameterEvolver.Propose evidence) 和 Feedback (FeedbackChannel.Query 返回的 KPI 反馈, reflector_impls.go 内部真消费 fb.Entity/fb.Metric). "同一语义两份类型"是典型冗余设计, scanner 把 FeedbackRecord 5 字段报 dead 因为消费端早就并行用 Feedback 了. 合并: 删 FeedbackRecord, ParameterEvolver.Propose(ctx, key, []Feedback) 签名直接收 Feedback, ProposerFunc / DefaultParameterEvolver / examples/evolve_closed_loop 全部链路改用 Feedback. 合并保留更短更 idiomatic 的 Feedback 名, 删掉 "Record" 累赘后缀. 所有 evolve test 继续通过. baseline 372 → 367.
  • [x] internal/syslib/bash.HeredocInfo (Quoted/StripTabs/BodyStart/BodyEnd) — 4 字段真 drain 端到端 (2026-04-20): 按用户 4 问法则 (接口有消费? 必要? 外部? 内部加调用?) 重新审判, 这 4 字段不是一种债务: Quoted/StripTabs 是冗余 (parser 自己从源码 token op/parseHeredocDelimiter 算, 是 source of truth; PreprocessHeredocs 的副产物无人消费), 删字段 + 对应 test 切换到 Parse(input) 验证 AST 侧 HeredocStripTabs/HeredocQuoted 真语义. BodyStart/BodyEnd 是设计意图未 wire (之前存的是 result.Len() 即 processed text 近似偏移, 作者注释自己标"近似"): 修成真原文 byte offset + 端到端 wire HeredocInfo → ast.Node.HeredocBodyStart/EndRedirectionInfo.HeredocBodyStart/EndDangerInfo.HeredocBodyStart/Endcheckpoint_suggested observer 事件 payload (heredoc_body_start / heredoc_body_end key, 零值归因非 heredoc 场景时跳过). Reason clause 同时带 "source bytes N-M" 文本让人类可读 log 也承载位置. 回归 test 一主题一 sub-claim: TestPreprocessHeredocs_BodyStartEndAddressesOriginalSource 4 case (单 heredoc / 空行 body / 多 heredoc 同行 / strip-tabs 变体) 锁 source[Start:End] == Body 不变量; TestAnalyzeDanger_HeredocBodyPos_LocatesSource 端到端断言 DangerInfo.HeredocBodyStart:HeredocBodyEnd 切回 cmd 能命中 authorized_keys + Reason 含 "source bytes". advisor 审判发现 "快扫 vs 严谨"叙述错 (两侧都是完整词法分析, 只是历史独立编写), pin consumer 用 observer payload 是真 user-visible 面 (非 formal wire). baseline 376 → 372.
  • [x] internal/syslib/bash.CommandInfo (InPipeline/InSubshell/Operator/PipePosition/Position) — 5 字段真 wire 端到端 (2026-04-20): 和 alpha.8 Background 字段同系 (bash 语法上下文标记), 同构走"安全决策面激活"路径. 先 verify extract.go ctx 传播语义 (Position 在 NodeSubshell 内 reset 从 0 算 / NodeList+NodePipeline 累加 outer+inner; PipePosition 必须配合 InPipeline 查因非管道命令零值冲突; Operator 第一个命令从父 ctx 继承最外层为空), 按实际语义升级 5 字段双语 godoc 消除"边界含糊"嫌疑. amplifyBackground 扩展重命名为 amplifySyntaxContext 承接全部 6 字段 (含 Background), 一处集中构造 clause + pattern 前缀 (结构性字段 "(subshell) " / "| " / "& " 叠加, Position/Operator 走 Reason clause), 用 "; " 拼接, 避免各字段分散拼错. 所有 6 处调用点 (AnalyzeDanger) 从旧名更新, 语义向后兼容 (Background 行为不变). 5 sub-claim 一 test + 1 regression: InSubshell / InPipeline+PipePosition / Operator / Position / Multiple 组合 ((true && rm -rf /) 同时触发 Operator/InSubshell 验证 "; " 连接和 "(subshell) " Pattern 前缀不互相覆盖). Counter case 全覆盖 (standalone 不得有对应标注防假阳性). advisor 明确要求 "verify from code not godoc" — ship 前核实 3 个关键问题代码答案才写 godoc 和 helper, 不脑补. baseline 381 → 376.
  • [x] internal/syslib/git.Info 5 字段 drain (2026-04-20): 按 4 问法则重审, 本包从 day 1 零消费者 — speculative engineering 残留 (上游 claude-code-mod 同步存在双实现, TS 原版 Claude Code 根本没 git metadata API). 真诚分层确立: syslib/git 作"引擎内部底层 git 工具 (裸 exec.Command)"定位, 不建 builtin/git tool (对模型冗余, bash tool 够), pkg/context 通过 import 直接消费. 删 Info.Root/DefaultBranch/Dirty + FindGitRoot/GetDefaultBranch 函数 (真无消费场景, Dirty 与 Status 语义冗余); pkg/context/context.go detectGitInfo 替换为 syslib/git.GetInfo 调用, IsRepo/Branch/Status 从 dead 变真消费 (注入 system prompt - Git repo: yes / - Git branch: X / - Git status: clean|N file(s) changed). GetBranch 改用 git branch --show-current (detached HEAD 返回 "" 比 rev-parse 字面 "HEAD" 更易消费). DESIGN.md sandbox 白名单 + architecture.md 包描述同步. baseline 361 → 356. 后续纠正: 原 Commit A 同时加了 Run(ctx, cwd, env, args...) 通用原语期望 sync_git 下沉, 但读 sync_git 发现它走 execenv.Executor DI 层 (ClassMemoryGit sandbox 路由), 用不了裸 Run — Run 撤回 (见 L695b).
  • [x] internal/syslib/git.Run 原语撤回 (2026-04-20): Commit A 的形式错误自我纠正. Run 本想作为 sync_git 4 runGit 变种的下沉目标, 但 sync_git.go:199-205,402-422 走的是 g.executor.Command(ctx, execenv.Spec{Class: ClassMemoryGit, ...}) — execenv.Executor DI 层, 不是裸 exec.Command. sync_git 走 ClassMemoryGit 让云端 sandbox backend 决策路由 (system pod 或 tenant VM), 下沉到裸 Run 会破坏 sandbox 路由抽象. 同时 context.detectGitInfo 只用 GetInfo 不用 Run. Run 零真实消费者, 未来 sandbox M2 "预留" 是 speculative — 和本轮全程反对的"存利息"一个模式. 撤回: 删 Run + mapToEnv + 4 个 TestRun_* + "context" import; DESIGN.md 白名单移除 "Run 原语" 字样, architecture.md 包描述同步. baseline 不变 (Run 是函数非字段). 教训: 先枚举真消费者路径 (裸 exec vs DI 层) 再设计原语, 别凭命名相近就假设下盘.
  • [x] syslib/git.ValidateRef 贯通到 pkg/memory.NewGitSyncAdapter 构造期 (2026-04-20): 独立安全债务, 与 Run 原语撤回同一 session 清理. sync_git 的 g.remote / g.branch 两个用户配置值进入 11 处 git argv (fetch/push/rebase/reset/pull/init/log/ls-files/add/commit/diff), 原无构造期校验 — remote 以 "-" 开头即 flag injection (如 --upload-pack=evil 让 git 执行远端任意代码), branch 含 ".." 即路径遍历 (git 内部 ref 解析解到 .git/refs/../.. 越狱). 修复: export validateRefValidateRef (GetDiff 内部调用点同步), NewGitSyncAdapter defaulting 块后一次调 gitlib.ValidateRef(remote) + gitlib.ValidateRef(branch), bad 值 panic (风格对齐既有 nil Executor panic). 加 2 test: TestNewGitSyncAdapter_RejectsInjectedRemote (--upload-pack=evil remote) + TestNewGitSyncAdapter_RejectsPathTraversalBranch (feat/../etc branch). baseline 不变 (纯参数校验, 无字段触及).
  • [x] turn 边界族 C1: TurnStartEvent.ContextWindowTokens + TurnEndEvent.MaxTokens 经 bridge serializer 透传 (2026-04-20): 按 4 问法则分类, 真消费路径是 engine emit → flyto.Event channel → bridge EventSerializer → SSE → 外部客户端 (TUI / Web UI). event_serializer.go 的 anonymous struct literal 是 choke point — 原 TurnStartEvent payload 只序列化 {turn, model} 丢 ContextWindowTokens, TurnEndEvent payload 只序列化 {turn, input_tokens, output_tokens, cost_usd} 丢 MaxTokens, 两个 engine 已 emit 的字段被 serializer 静默吞掉. 修复: 两 struct literal 加 ContextWindow / MaxTokens 字段 (JSON key 分别 context_window / max_tokens, omitempty 省零值避免对旧消费者添噪声). 新建 pkg/bridge/event_serializer_test.go 锁 JSON shape: 4 test (shape 断言 + omitempty 验证) × 2 event. 语义: ContextWindow 透传模型完整 context window token 数 (由 provider.Models() 提供, 消费层可渲染预算条不硬编码模型表); MaxTokens 透传本轮发给 provider 的实际 output 上限 (FastMode / reasoning 降档后的值, 消费层据此判断回复截断是命中上限还是自然停止). 教训: 新增 event 字段必须同步修 serializer anonymous struct + shape test, 否则被 bridge choke point 吃掉. baseline 356 → 354.
  • [x] 子 agent 观测链路端到端 wire (2026-04-20 本 session 主题, 3 commit 端到端闭环): 父 agent 派出的子 agent 当前对引擎是黑盒 — worktree 模式挂牌不开工 (sa.Cwd 设了但 BashTool/FileEditTool 工具箱继承父 cwd), 子 agent 事件在 5 处 spawn 路径 (agent_executor sync/background/worktree, engine.go:1938 skill fork, engine.go:4682 记忆提取故意静默, team.go:378 worker, dream.go:456 dream) 全 drop 或只读少数事件类型, SubAgent.StartTime/EndTime/Description 从未 emit. 产品后果: TUI 树形展示中间节点空白 / 平台 session 成本漏计 / 审计流水缺失 / worktree 隔离是谎言. wire 方向 3 commit:
  • commit A ✅ (ctx cwd 透传, 2026-04-20 commit 914df89): pkg/tools 新增 WithWorkdir(ctx, path) / WorkdirFromContext(ctx) string 工具 ctx helper (type-hidden key 防外部构造污染); BashTool/BashToolBackground/FileEditTool/GitignoreTool 4 个 cwd-aware 工具 Execute 开头先读 ctx 覆盖, fallback 构造期 cwd; SubAgent.runLoop 每工具调用前注入 ctx = WithWorkdir(ctx, sa.Cwd); 4 sub-claim 测试全绿 (pwd 输出真落到 override, symlink guard 用新 cwd, Gitignore 三级 fallback, 空 ctx 回退不回归). baseline 352 → 351 (SubAgent.Cwd 从 dead 变 live wired).
  • commit B ✅ (事件回流 + 生命周期, 2026-04-20): pkg/engine 新增 SubAgentStartEvent (ID/Description/Cwd/Model/StartTime) + SubAgentEndEvent (ID/Duration/Status/Result/Error) + event_emitter.go (WithEventEmitter/EventEmitterFromContext, 私有 key type 防外部伪造); engine 主工具派发前 WithEventEmitter 包 ctx 用非阻塞 select 丢弃兜底防死锁; sa.runLoop 读 ctx emitter 一次, defer emit End, for-select 头部 emit Start, 中间 13 处 ch <- &SubAgentEvent{SubAgentID: sa.ID, Inner: X} 统一替换成 pushEvt(X) 辅助 — pushEvt 把 bare X 送到 sa 自己 ch (RunSync/RunSyncWithCallback 本地消费者 type-switch 匹配 *TextEvent 等, 顺带修潜在 silent bug — 原包装后 RunSync 从没收过 TextEvent), 同时在 forward path active 时 emit 包装 SubAgentEvent 到父 Run channel. SubAgentConfig.SilentEvents 兜底 flag, runMemoryExtraction 显式设 true (后台静默任务不污染用户事件流). 7 sub-claim 测试全绿 (Start/End/Inner/Silent/NoEmitter/Concurrent/Truncate + EventEmitter 单元). baseline 351 → 358 临时上升 (+7: SubAgentStartEvent 5 + SubAgentEndEvent 5 - wire 掉的 Description/StartTime/EndTime 3), 这 11 条是 tracked transient debt 本 session Commit C 下一刀 wire 完. scan-baseline.json 已 regenerate 接纳.
  • commit C ✅ (bridge 序列化 + 文档, 2026-04-20): 重构 event_serializer.go 把 17 case switch 提取成 eventPayload(evt) (type, payload) helper, Serialize 方法调用 helper 再包上 id/timestamp/sessionID — 让 SubAgentEvent 能递归取 Inner 的 (type, payload) 做扁平合并. 新增 3 case: SubAgentStartEvent → subagent_start {subagent_id, description, cwd, model, start_time_ms}; SubAgentEndEvent → subagent_end {subagent_id, duration_ms, status, result, error}; SubAgentEvent → subagent_event {subagent_id, event_type, ...inner_payload} 扁平 shape (消费端一次 JSON.parse 即可读到归属 + 业务字段, 无 nested unwrap). mergePayload helper 处理 JSON object 键合并, 冲突时 extras 胜 (subagent_id / event_type 永远外层优先). 7 sub-claim 测试全绿: Start 五字段 shape + Start 空串 omitempty + End 五字段 shape + End error 非空保留 + Event 扁平 shape (TextEvent inner) + Event ToolUse inner (id/name/input 扁平) + Event 嵌套 SubAgent (外层 ID 胜). docs/api-reference.md push 形态清单加 SubAgentStart/Event/End 三条 + SSE 事件格式章节补 3 个事件 payload 示例; docs/tools.md worktree 模式澄清"真隔离" + 可观测性 cross-reference. baseline 358 → 347 (11 dead fields 全部 wire). 全主题净效果 baseline 352 → 347 (-5): SubAgent.Cwd (commit A 工具 cwd 透传) + SubAgent.Description/StartTime/EndTime (commit B 进 SubAgentStartEvent / SubAgentEndEvent payload, commit C bridge 消费) + SubAgentEvent.SubAgentID (commit C bridge 扁平合入). 端到端产品兑现: Agent 工具 worktree 模式真隔离 (BashTool pwd 落在 wtInfo.Path, FileEdit symlink guard 用 worktree 根); 子 agent 活动对父 engine 可见 (TUI 树形 / SSE 子 agent 事件 / observer 审计 sink / session 成本统计按 subagent_id 归属). WorkerResult.Description 单独主题 "Team observability" 分离不在本次 scope (见本档 D-B 档 subagent 族条目下独立主题记录).
  • commit D ✅ (scope 补完, 2026-04-20): advisor sign-off 前检出的 3 个尾巴一并清: (1) 后台子 agent 观测路径: sa.runLoop ctx 无 emitter 时降级到 sa.ParentEngine.Observer().Event("subagent_*", ...) — RunBackground 的 bgCtx 从 engine.Context() 派生无 emitter, 之前后台子 agent 对审计 sink / 指标系统隐形, 现在经 observer 暴露 subagent_id + event_type + lifecycle 核心字段 (lossy 元数据, 非每内层业务字段 — 对齐 Observer 审计用法, 避免重复 pkg/bridge 已有的 17 case payload builder); (2) 5 个 observer fallback 测试全绿 (Start fields / End fields / InnerEvent metadata / Channel prefers over Observer 避免双发 / SilentEvents 两路都跳过); (3) Team.runWorker 集成测试 (TestRunWorkers_ForwardsSubAgentEventsToParentChannel) 证实 Team 路径也继承 ctx emitter, Worker 启动时 sa.runLoop 把 Start/End 转发到父 Run channel 且 SubAgentID 前后串联不乱 — Dream / skill fork 走同一 sa.runLoop 代码路径推导成立, 不单独加测; (4) sa.Run() channel 契约变更 (bare events, 不再 wrap SubAgentEvent) 写进 godoc + docs/api-reference.md push 形态清单, 标 "Breaking 2026-04-20" 给直接消费 sa.Run() 的外部 SDK 用户迁移指引. 一个未验证披露: commit B 声称"顺带修 RunSync 返回空串 silent bug" (原 wrapper 导致 type switch 匹配不到 TextEvent, resultTexts 永远空) 是基于代码读取 + Go switch 实验推断, 未跑真 LLM 端到端验证 — 两种可能性都让业务变好, 但这是推断而非验证, 下次跑真 Agent 工具时观察输出即可反推. baseline 不变 347. 2026-04-20 晚续 session 验证: scripted provider mock (emit 真 flyto.TextEvent block_stop + UsageEvent end_turn) 驱动 sa.RunSync 端到端, 断言 result 含 "hello" PASS — 推断升为 validated. 见 core/pkg/engine/subagent_silentbug_test.go (commit 3edd2eb).
  • [x] internal/syslib/bash.RedirectionInfo.SourceFD 删除 (2026-04-20): 按 4 问法则审判走 internal/ 包例外删除 路径. 字段 godoc "源 fd(默认 1 for >, 0 for <)" 是描述性默认值, 非引擎行为承诺, struct 头 godoc (116-134) 只列 Heredoc 4 字段不背书 SourceFD. 三个剔除条件同时成立: (1) 包路径 internal/syslib/bash, Go 内部规则禁止外部 import, scanner 视野完整; (2) 0 reader / 0 writer (grep 全局只命中定义 + scan-baseline); (3) 同语义冗余 — parser AST bash.Node 根本无 FD 字段, Operator 已持 "2>&1" canonical 字符串, 消费者 (pkg/permission bash_security) 都读 Operator 不需要分解 FD 号. wire 路径需先补 parser + 再补消费者两层 speculative, 不走. advisor 复核通过. baseline 353 → 352.
  • [x] Metadata.Destructive/ReadOnly godoc 诚实化 (2026-04-20 晚续 session): 删"用于权限系统的快速判断"承诺 — 无 in-tree 消费者读本字段做权限判断, 权限路径由 PermissionClass 驱动. 改为 informational 元数据声明 (外部 SDK / TUI / audit 渲染标签 + evolve 工具生成器可用). 双语 godoc. 纯文档改动, baseline 不变.
  • [x] 大结果截断信号全链路补齐 (2026-04-20 晚续 session): ToolCallResult.Truncated/StoredPath 上游 orchestrator 写了, 下游两条 public surface 全漏 — ToolResultEvent (SSE 推给 HTTP 订阅者) 只 4 字段, OperationEntry (服务端操作日志给审计/rollback) 也没这 2 字段. UI 只能看截断摘要, 审计档案也缺"此次结果已截断"标记. 按 4 问 2 原则: 真 wire 缺口 (上游写下游漏) + exported SDK 可达默认保留. 修: (1) flyto.ToolResultEvent 加 Truncated + StoredPath 带双语 SECURITY note (path 是消费层 ResultProcessor 决定, 可能带 sandbox 内部结构); (2) engine.go:4278 构造 event 时填入 result.Truncated/StoredPath, 注释说明路径不脱敏 (不是工具输出, 是 ResultProcessor 落盘位置); (3) pkg/engine.OperationEntry 加同名 2 字段 + engine.go:4328 Record 时填入; (4) ToolCallResult godoc 重写 — 说明这 2 字段是 engine → Event/OperationEntry/Bridge → UI/audit 一条信任边界, 3 处 StoredPath 共享同一条安全约束; (5) bridge event_serializer.go tool_result 匿名 struct 加 truncated,omitempty + stored_path,omitempty 避免 L699 C2 同形的 choke-point 吞字段 bug. 3 sub-claim 测试: serializer TruncatedShape 断言 JSON 含 2 key + 值正确; serializer OmitsEmptyTruncation 断言 false/空串 omitempty 不加噪; OperationLog Record_PreservesTruncation 断言往返保真. baseline 净抵消 216 → 216: ToolCallResult 侧 -2 drain, OperationEntry 侧 +2 归档 pull API (外部审计 SDK/报表等激活消费者, rollback 不读). scope: 只补截断信号全链路, tools 族其他 11 字段 (ToolCapability 4 / DryRunResult 3 / Metadata 2 / Result.Data 1 / UndoInfo.Description 1 / OperationEntry 新 2) 各自独立审判不塞本 commit.
  • [x] Team observability (worker 可见性): WorkerResult.Description wire 进 task-notification XML (2026-04-20 晚续 session): 按 4 问 + 反向思考 审判. godoc "任务描述(用于追踪)" 意图两路消费 — (A) 调用方读 []WorkerResult SDK exported 默认合法, (B) Leader 读 task-notification XML 反向识别"这是哪个业务任务". 路径 B 原是上游 wire 缺口: runWorker (team.go:435) 填了 Description: desc, RunWorkers 构造 XML 时只用 WorkerID/status/summary/AgentType/duration (team.go:321-327), Leader 看到的 task-id 是 random hex — 三个并发跑不同 prompt 的 Explore Worker 在 Leader 收件箱完全同形, 无法分辨. 修复: taskNotificationTmpl<description>%s</description> 字段, RunWorkers 注入时 xmlEscape(r.Description) 参数一并塞入. 2 sub-claim 测试: TestTaskNotificationTmpl_FormatOK 扩断言 <description>南路径探索</description> 转义后出现; 新增 TestRunWorkers_TaskNotification_CarriesDescription 用 pre-canceled ctx 路径 (Worker 快速失败仍走 enqueue 分支) drain notifications 断言 2 条 XML 各含不同 description. 队列原说 "Worker Start/End 事件缺" 信息过期 — Worker 是 SubAgent, Start/End 已由 subagent_start/subagent_end 闭环 (commit D 集成测试证实 team_test.go:285 路径), Team 层无需重复 Worker 事件. baseline 217 → 216. scope 纪律: 只改 team.go + team_test.go + baseline, 不扩到 WorkerEvent 虚构需求.
  • [x] turn 边界族 C2: TokenWarningState.IsAtBlockingLimit 严重优先分发 + godoc 诚实化 (2026-04-20): 按 advisor 3 问验证 — (1) blocking 可达但已过锋 (post-turn 时本轮已完成, 下轮 pre-turn maybeCompact + ShouldCompact 兜底, 下层 provider context_length_exceeded 归类为 ErrContextOverflow); (2) 原 emit else-if 链从最弱阈值判起, 新加 blocking elif 会被 critical 分支吞 — 正是 IsAtBlockingLimit 成 dead 字段的根因; (3) godoc 承诺"无法继续对话"是引擎行为, 实际引擎不在此拒绝下轮, 语义造假. 修复: 提取 PickWarningCode(state) string pure function 严重优先分发 (blocking → critical → warning → ""), engine.go:3944 warning emit 用 switch-on-code 替换原 else-if, 新增 context_window_blocked code 带 "上下文窗口已占满, 下轮 pre-compact 将被强制触发" 消息 (可观测信号, 非引擎行为承诺). godoc 重写 IsAtBlockingLimit: 从"无法继续对话" → "100% 占满, 仅 observability signal, 真正兜底是下轮 maybeCompact / forceCompact, 严格蕴含 Error 和 Warning 阈值 true". 加 2 test: TestCalculateWarningState_BlockingImpliesCriticalAndWarning 锁 implication chain (blocking → Error + Warning 同 true), TestPickWarningCode_SeverityOrder 5 case 穷举 nil / empty / warning-only / critical / blocking 各自返回值. scope 纪律: 不改引擎真拒下轮 (pre-compact 路径已有, 改 engine 行为算另一主题). baseline 354 → 353.

  • [x] tools 族深审 (2026-04-21, 12 字段分档归位, 1 真 wire + 11 归档, baseline 213 → 212): 按 4 问 + 反向思考 审判 scanner 标 dead 的 12 字段. 原计划 1 commit 纯归档, 产品经理反向挑战"引擎该 gate 不该甩锅给外部 UI", 拆成 2 commit:

  • commit 1 (f814bd5) MinConfidence safety gate 真 wire: 从 "pull API 归档" 改分类为 "真实现债务". 预检发现 ToolCapability.MinConfidence godoc "最低置信度要求" 暗示引擎 gate 但零 gate site = 造假, 且 LLM 经 prompt 自报置信度是既有能力不需要模型新特性. 三层 wire: (1) capability.go 新增 const ConfidenceInputField = "_flyto_confidence" + helper CheckConfidenceGate(cap, input) (MinConfidence=0 原样放行, >0 时解析/拒绝/剥除); MinConfidence godoc 双语 Wire 段明述 buildToolDefs 提示 + orchestrator gate 两层消费路径. (2) orchestrator.executeSingletool.Execute 前调 GetCapability + CheckConfidenceGate, gate fail → ToolCallResult IsError=true + 诊断消息, gate pass → stripped input 传给工具. (3) engine.buildToolDefs 对 MinConfidence>0 工具追加 "[Safety] requires _flyto_confidence in input, min=N" 到 Description, 让 LLM 在工具定义层就看到契约. 10 sub-claim test (7 helper + 3 orchestrator 集成 + 1 engine Description 注入). 保留字段 vs schema 字段: 选前者 (下划线前缀表框架字段) 避免侵入工具 InputSchema. 非隐式 100: gate enabled 且字段缺失视为 fail 防 LLM 选择性不声明. builtin 工具不填 MinConfidence 保持零影响, 具体阈值是运营决策.
  • commit 2 (本轮) 11 字段 pull API 归档 + 2 字段 godoc 诚实化:
    • B1 合法 pull API 归档 (9 字段): ToolCapability.AffectedResources + DryRunResult 3 (WouldAffect / Preview / EstimatedImpact) + UndoInfo.Description + OperationEntry.Output/TurnNumber + ImageResult.Width/Height. 消费者在 core 外: audit UI / preview UI / safety dashboard / 外部 rollback UI / 外部审计 SDK. tests 锁 forward (capability_test.go / fileedit_test.go / filewrite_test.go 等), UndoInfo.Description 话术明确"tests 构造不断言 -- exported 字段给外部 rollback UI, 无 internal assert-site" (feedback_verify_real_wire_before_test). struct/字段级 godoc 升级双语 + 写清 pull 消费路径. 和 engine.CacheStats / FileStateCacheEntry / PlanApprovalEvent / CheckpointSuggestedEvent[~] 归档同列, 未来 session 不再重审.
    • B2 godoc 诚实化 (2 字段 UndoMethod/UndoToolName): 原 godoc "tool"=调用撤销工具 暗示引擎 dispatcher 读本字段选 undo 路径, 实际 rollback 走 UndoInfo.ToolName (动态, GenerateUndo 捕获) 不读 ToolCapability 静态版. advisor 挑战后诚实化为 "informational 静态声明, 静态给 UI 执行前分类 / 动态给引擎执行时派发, 互补非冗余". 不是删字段 -- 静态和动态都有独立价值 (静态让 UI 不跑也能渲染 "此工具可撤销").
    • B3 OperationEntry.Output/TurnNumber 两路径澄清: struct godoc 明述 "OperationLog.GetByMessage() 调取 vs observer 事件 opEventRecorded 里的 turn_number 推送是两个互不相交的 sink, 非冗余". 下次 reader 看到 audit_observer.go:167 也读 turn_number 不会误判为"已有消费者".
    • B4 ImageResult.Width/Height 话术: 采纳 advisor 建议 "外部 type-assert 表面 (SDK consumer 经 if img, ok := res.Data.(*ImageResult); ok 读)", 不写 "future log/validator" (speculative).
  • baseline 213 → 212 (MinConfidence 从 dead 升 live, 归档 11 字段保留于 scan-baseline.json 意为 "scanner 知道这些在本 module 无 internal consumer, ratchet 保证未来新加的类似位置不悄悄漏掉", 不走 drain).

  • [x] evolve 族深审 (2026-04-21, 11 字段分档归位, baseline 不降): 按 4 问 + 反向思考 审判 scanner 标 dead 的 14 字段 (其中 LLMCallOpts.Temperature C 档已独立归档, FeedbackRecord 5 字段上 session 已合并到 Feedback, 本 sweep 处理剩余 11 字段). 分两档结案:

  • A1 合法 pull API 归档 (7 字段): AggregatedStats.Sum + ChangeEvent.Change + ChangeEvent.IsLock + ShadowResult.BaselineBreakdown + ShadowResult.CandidateBreakdown + ShadowResult.Meta + ReplayEvent.Meta. 消费者在 core 外 (watch-only 面板读 Stats() / 审计 dashboard 订阅 Watch / 灰度 UI 看 breakdown / 外部 LogReplayer 填 replay 上下文). tests 锁 forward propagation (reflector_impls_test.go/parameter_store_file_test.go/shadow_runner_default_test.go), 两个 Meta 是扩展槽不锁 test. 各 struct godoc 升级双语 + 写清 pull 消费路径 (commit 22478e6). 未来 session 不再重审, 和 engine.CacheStats/FileStateCacheEntry/PlanApprovalEvent/CheckpointSuggestedEvent[~] 归档同列.
  • A2 C 方案 evolve→engine adapter 缺口 (6 字段, 独立主题, 等业务场景触发): RuntimeToolMetadata 4 (ConcurrencySafe/ReadOnly/IsEvolved/Version) + ToolResult 2 (Output/IsError). 整条 "Agent 自造工具 → Engine 工具注册表 → 模型可见" 能力在 core 内从未接. engine_integration.go:13 原注释示范 for _, t := range evolver.EngineTools() 是造假 — Evolver 类型无 EngineTools() 方法, 无 engine.Tools().Register() 调用点. commit 22478e6 改顶注释陈述现状: subagent / skill / memory 三路已覆盖 "能力沉淀" 需求, C 方案独有价值是"让模型在工具面板直接看见自己积累的招式" (skill 在 prompt 层积累思路, tool 在注册层积累能力), 在行业 platform (logistics/erp) 遇到 RPA 形态反复任务时显现. 字段保留 exported 让未来 adapter 直接复用 shape. 不排期到当前 sweep, 等真实业务场景 (客户反复跑相同物流查询 / 报表流水) 触发再独立主题接入.
  • LLMCallOpts.Temperature (C 档剩 1 条, 要扩 flyto.Request 跨 provider) 不触, 保持 open 等独立设计决策.
  • baseline 216 → 216 (全字段保留, 无 drain; 分类话术从 "placeholder" 升为 "已分档结案").
  • 缓存族: 本 session 全部处理完毕 — engine.FileCacheEntry 6 字段真 drain, engine.CacheStats 3 字段 [~] pull API 归档, tools/builtin.FileStateCacheEntry 5 字段 [~] callback form 归档.
  • ~~subagent 族~~ 重分类: 非 drain 候选, 是上游消费 bug. 2026-04-20 审判发现 6 字段全部是"上游调用端该读不读": SubAgent.Cwd 被 set 但 BashTool/FileEditTool 继承父 cwd 导致 worktree 隔离失效; SubAgentID 被 set 但 3 处消费路径 (RunSync/RunBackground/记忆提取) 全 drop SubAgentEvent 导致子 agent 对父 engine 完全不可见; StartTime/EndTime 从没进 observer/session stats; Description 从没 emit. 下一主题 "子 agent 观测链路端到端 wire" 处理 (见下).
  • ~~独立主题 Team observability~~: WorkerResult.Description 2026-04-20 晚续 session 已完成 (wire 进 task-notification XML, 见 L707 上方新增条目). Worker Start/End 事件不单独实现 — Worker 是 SubAgent, commit D 集成测试证实 team_test.go:285 路径下 subagent_start/subagent_end 已闭环, 重复加 Worker*Event 是虚构需求.
  • 计划进度族 (PlanStep/StepProgress/PlanProgressEvent/PlanProgressSnapshot 共 5 字段): 进度事件 subscribe 端未读.
  • bash 剩余: 全部处理完毕 — alpha.8 wire 主字段 (HeredocTag/Body/StripTabs/Quoted), CommandInfo 5 + HeredocInfo 4 (2026-04-20 前半 session) drain, RedirectionInfo.SourceFD (2026-04-20 本 session) 经 internal/ 包例外删除 (0 消费者 + parser AST 无 FD 支持 + Operator 已持 canonical 字符串 "2>&1", 冗余).
  • git (git.Info 5): 本 session 已 drain + Run 原语落地 (详见 L695 下 internal/syslib/git.Info 条目).
  • tools 族: 全部处理完毕 — 2026-04-20 Metadata.Destructive/ReadOnly godoc 诚实化 + ToolCallResult.Truncated/StoredPath drain, 2026-04-21 Result.Data vision wire drain + MinConfidence safety gate 真 wire + 余 11 字段分档归档 (详见上方 "tools 族深审 (2026-04-21)" 条目).
  • cache (ToolStabilityReport 4): 工具稳定性报告未 surface.
  • permission 次要 (Response 2 + DenyRule 1 + LearningStats 1 + SedCheckResult 1 + SuggestedRule 1).
  • builtin 次要 (ImageResult 2 [~] 归档 + FileEditResultData 2 + GrepResult 2 + SkillEntryDesc 2 + SedEditInfo 1). ImageResult.MediaType/Base64 2026-04-21 vision wire drain, Width/Height 2 字段同日 [~] pull API 归档 (见 tools 族深审).
  • 杂项: context.RestoreItem/CompactResult, plugin.Plugin/Skill/ValidationResult, execenv.Spec, engine.ProcessedInput/SessionInfo/OperationEntry/FileSnapshot/WorktreeInfo/AgentDefinition.

D-C 档 (注释标 LEGACY 但仍需真 wire, 4 主题 / ~17 字段) — 注释历史措辞不等于"可删". 字段一旦声明就是契约, 默认 wire 保留, 只在枚举完所有 external consumer (SDK/plugin/CLI/platform) 都确认不需要后才考虑 deprecate, 本 sweep 不走该路径:

  • transport.StreamEvent (9): client.go:14 注释标 "LEGACY: StreamEvent/StreamEventType/UsageInfo 类型仍保留" + "StreamEvent 类型从公共 API 消失". public stream 已换 flyto.Event, 但内部 SSE 解析器产物仍是 StreamEvent — wire 方向: 内部 parser → flyto.Event 的 translator 读字段 (BlockID/BlockName/PartialJSON/Usage 等), 再发出 flyto.Event, 让 translator 真正读这些字段.
  • transport.StreamStats (3) + transport.RetryInfo (1): 同 LEGACY 族, stream guard / retry 层诊断字段, 应接 observer 事件或错误消息 surface.
  • query.StreamEvent (4): pkg/query SDK 类型, 即便 internal transport 已换 flyto.Event, query 层消费端应真读字段 (Block/Delta/Type/Usage).
  • retry.OverflowInfo (1): retry 溢出信息, 错误消息或诊断事件 surface.

决策点 (flag, 未启动 drain):

剩 ~20 主题级真债务, 主题密度比 alpha.7 (26 条) 低一档, drain 走 alpha.8 节奏估 2-4 个 session. 选项:

  1. 启动 D 档 drain — 按 D-A → D-B → D-C 顺序一主题一 commit, 节奏同 alpha.8 (sub-claim checklist + 一 sub-claim 一 test). alpha.9+ 周期.
  2. 锁 386 切 TODO.md P1 消费层 7 条 (SQL 只读校验器 / DB Dry-run / ML 验证器 / CAS / 熔断 / Staging / 影子表) — 消费层是端到端产品价值点, 真债务是内部洁净度, 优先级由 user 判.
  3. 局部 drain D-A 档 3-5 高价值主题后切 P1 — 兼顾 (SessionStats/PlanApprovalEvent/DenialStats/ClassifyResult 这类 user-facing 信号先落地, 其余留档).

pkg/evolve.LLMCallOpts.Temperature (C 档剩 1 条) 设计决策 (扩 flyto.Request 跨 provider) 与本 sweep 正交, 保留 open 不强制打勾.


代码风格 / Lint


统计

最后更新: 2026-04-26 (grep 精确计数 ^[[:space:]]*- \[x\] / ^[[:space:]]*- \[ \], 包括嵌套子条目, 文件内口径可随时 grep -c 核验)

状态 数量
✅ 已完成 (文件内保留) 53 项
🔴 P0 未完成 0 项
🟡 P1 未完成 2 项 (ML 验证 / 熔断, 全 platform 消费层; L407 文档自动化 + L693 SessionStore 2026-04-26 完成)
🟢/⚪ P2/P3 未完成 10 项
总未完成 12 项
总计 65 项 (文件内)

关键观察: 无 P0 阻塞, 核心引擎 22 模块 + 模块 23 SQL 工具链全部 ✅. v0.3.0 于 2026-04-26 发版 (见 CHANGELOG.md "v0.3.0 (2026-04-26)" 段; 上版 v0.2.0 于 2026-04-24, v0.1.0 于 2026-04-18). v0.3 周期含 L569 反事实缩水 + L437 shadowdb + L683 Temp/TopP + L692 业务 REST/SSE + L407 文档自动化三件套 + L693 SessionStore + Postgres 平台化奠基 + ADR-0001/ADR-0002/ADR-0003 立项. 剩 12 项不阻塞 v0.4 骨架, 待后续完成.

剩 12 项分布 (诚实分组):

  • core 引擎内部 3 项:
  • L488/489/490: CAP-4 自动化 × 3 (Flyto CLI 无头自消费 / CI 触发 / WebSearch 前置, 低优工具效率)
  • ~~L569: 反事实工作流引擎级 enforcement~~ (2026-04-25 缩水做 6 commit, ADR-0001 归档原方案 A 否决理由)
  • provider 扩展 1 项:
  • L454: provider 静态模型表自动更新 (P2, 爬 Anthropic/MiniMax/OpenAI 文档)
  • ~~L683: Temperature/TopP cross-provider passthrough~~ (2026-04-25 完成, 7 provider 全接通)
  • platform 消费层 8 项:
  • L434/435: P1 × 2 — ML 验证 / 熔断 (L436 Staging 2026-04-24 / L437 影子表 2026-04-25 / L692 业务 REST 通道 + L407 文档自动化三件套 + L693 SessionStore 2026-04-26 完成)
  • L408: L952c 场景化编排 Go 教程 (P3)
  • L438: AuditSink DB 实现 (P3)
  • L439: WMS 波次建立参考实现 (P3)
  • L571: 微信 ClawBot 接入 (P3)
  • ~~L693: 业务 REST 多副本 SessionStore interface~~ (2026-04-26 完成, C1-C4 commit chain eec48fe → beaff60 → 96e893a → C4. ADR-0003 立, 三层 pin + sticky routing 落档. staging Postgres 跳过待真消费者驱动)
  • L694: gRPC + REST cross-transport request-id / trace 串通 (P3, 2026-04-26 自 L692 ADR-0002 tracked debt)
  • L695: SSE 1000 单/s 带宽监控 (P3, 2026-04-26 自 L692 质疑 agent Q1.3)

口径说明 (2026-04-23 统一):

2026-04-14 及之前用的 "累计 809 / 未完成 56" 口径包含已 archive 出文件的历史完成项, 每次 update 需手动维护易 drift. 2026-04-23 改用文件内 grep 精确口径 (grep -cE "^[[:space:]]*- \[x\]" core/TODO.md), 可随时 verify 永不 drift. CLAUDE.md 同步切换到文件内口径, 不再维护累计历史口径.

近期主要动作 (按 commit 顺序):

  • 2026-04-26 L693 SessionStore + Postgres 平台化奠基 (4 commit): 3-agent 并行 review (调研 LangGraph/Vercel/Temporal/Express/Django 业界 prior art / 质疑 6 道击中 3 道含 permCh 物理不可移动 + YAGNI + dead-field-scanner debt / 设计 3 alternatives 选 typed Alt 2) reconcile 后 PM 三轮决策 (Postgres 肯定用 → 整个 platform psql → 拒绝迁移工具+sqlite 走 plain SQL+testcontainers → C3 staging Postgres 跳过待真消费者). commit 顺序: eec48fe C1 SessionStore 接口 (Create/Get/Delete 三方法) + InMemoryStore drop-in 替换 server.sessions map + 4 handler 改造 + TOCTOU race 折叠 (319 行 sessionstore + 改 server.go 182 行); beaff60 C2 Postgres 后端 + internal/db/ 共享池 + --postgres-dsn flag + docker-compose pg + healthcheck + persistent volume + release.yml POSTGRES_PASSWORD secret + testcontainers-go 真 pg 测试 (~510 行 + main.go 53 行 + docker-compose 48 行); 96e893a C3 ADR-0003 (387 行 8 节, 三层 pin 物理事实 + sticky routing phase 1 ip_hash + phase 2 X-Session-ID 升级路径 + cache miss 503 fallback) + Caddyfile 注释; 本 commit C4 TODO + CHANGELOG + CLAUDE.md 同步. 真相: 多副本真阻塞是三层进程内 pin (server.permCh + Session.pendingPermissions + engine.sessionState), 后两层在 core 引擎层平台层不能解, 必须 LB sticky routing; SessionStore 价值降级为"replica 重启不丢元数据 + 滚动部署 drain + Postgres audit". 关键决策: drop Redis 档 (元数据 payload 太薄, 共享 staging pg 池更经济); drop staging Postgres 后端 (PM 接受 YAGNI, ADR-0003 § 5.5 登记触发条件 = staging 真接入消费者); engine.SnapshotStore 接线 cache miss 自动恢复历史留 follow-up. 不引正式 migration 工具 (1 张表 plain CREATE IF NOT EXISTS 自检足够). 测试: 6 InMemoryStore + 5 testcontainers Postgres + 2 db pool, 全模块 -race 全绿; baseline 220 不变 (scanner 只扫 core/, 不扫 platform). PM 部署侧必做 (v0.4 release 前): Gitea secrets 配 POSTGRES_PASSWORD.
  • v0.3.3 发版 (2026-04-26): L407 follow-up 三件套 — A README 文档地图 (commit fea0d27, 70 行 markdown 串 27+ markdown + 4 入口 + 7 读者分组) + B godoc.flytoex.net 子域 (pkgsite Go API HTML, 子域因 pkgsite href 绝对路径不能挂 sub-path; Dockerfile.godoc 多阶段拷整 monorepo 源 + go mod download all + ENTRYPOINT pkgsite -list=false; 不鉴权对齐 pkg.go.dev) + C docs.flytoex.net 子域 (mkdocs-material@9.5.31 整合站, Dockerfile.docs python:3.12-alpine + nginx:alpine 两阶段; mkdocs.yml docs_dir=/work + config /config/mkdocs.yml sibling 绕开 mkdocs "docs_dir 不能是 config 父目录" 约束; nav 12 分组串 README+CONSUMERS+CONTRIBUTING+FLYTO+ADR-0001/2+CHANGELOG+TODO+7 provider+architecture+writing-guide). DNS 加 2 条 A 记录 (godoc.flytoex.net + docs.flytoex.net → 45.145.229.197 DNS-only 灰云) 经 Cloudflare API token PUT /zones//dns_records, 不走 PM web UI. release.yml 加 godoc + docs 两个 build-push step. 本地 docker build 两 image 实测通 (godoc 61.8s + curl /git.flytoex.net/... 200; docs 5.83s mkdocs build + nginx serve / 200 中文 title 渲染). baseline 219 不变 (纯文档 + deploy infra). 上一变更 v0.3.0/0.3.1/0.3.2 发版 (2026-04-26): CHANGELOG Unreleased (v0.3-dev)## v0.3.0 (2026-04-26) 结构化重组对齐 v0.2.0 段 8 节格式; v0.3.1 hotfix go.sum tidy (golang.org/x/net v0.50.0 transitive missing, GOWORK=off Dockerfile build fail); v0.3.2 hotfix #2 (release.yml deploy script export ANTHROPIC_API_KEY + Gitea API PUT secret, 用 ~/.git-credentials 里 yuanwei token 自动配). 覆盖面: counterfactual + reverse_think + shadowdb + Temp/TopP + L692 业务 REST/SSE + L407 文档自动化三件套 + ADR-0001/ADR-0002 立项. push tag v0.3.0/0.3.1/0.3.2 触发 release.yml: dead-field-ratchet → docs drift gate (v0.3.0 新加) → buildx 4 image (含本版加的 godoc + docs) → SSH HK-133 deploy → caddy restart 让 /swagger/* + godoc.flytoex.net + docs.flytoex.net 立即生效.
  • 2026-04-26 L407 消费层文档自动化三件套 (7 commit, 含 1 path bug 顺手修): queued task 锁定 swag (注解式 OpenAPI 2.0) > huma (code-first 要重写 1263 行 server.go) 路线; SDK 自动生成 (Stainless 模式) 不做 rule of two 等 5+ 语言客户端. commit 顺序: 89c38d3 C0 顺手修部署 path bug /v1/* → /api/v1/* (Caddyfile handle /api/v1/* 不剥前缀致 server.go 注册 /v1/* 经 hub.flytoex.net 全 404, 上一会话 commit 5 没真实经 Caddy 实测, 触发 memory feedback_validate_network_path_before_deploy); 26bc732 C1 server.go 8 handler swag 注解 + 3 named response type (HealthResponse/StatusResponse/ListToolsResponse) 替代 ad-hoc map + swag.go seed + docs/{swagger.json 22.5K 12 schema, swagger.yaml 12.3K, docs.go 23.1K Go embed} 首版; bce7670 C2 cmd/common --swagger flag + Swagger UI endpoint (httpSwagger v2 包内嵌, side-effect import docs, authMiddleware/rateLimit allow-list 加 /swagger/); 22b6388 C3 core/Makefile docs-swag/grpc/consumers/all 4 target + tool install (swag@v1.16.6 + protoc-gen-doc@v1.5.1 pin 与 staticcheck@v0.7.0 同模式) + ROOT git rev-parse + 顶部 export PATH 含 GOPATH/bin + grpc-api.md 首版 278 行 + .gitea/release.yml docs drift gate (apt-get protoc + make docs-install + docs-swag/grpc + git diff --exit-code 4 产物); a9b04ab C4 docs/CONSUMERS.md 顶层 wrapper 133 行 (端口拓扑/env/flag/OIDC auth 流程图含 allow-list/业务 REST 一次性+多轮会话 curl 例子/观测 gRPC SafetyChain 指引/进一步阅读链); bf5af1d C5 Caddyfile handle /swagger/ → common:8080 + docker-compose --swagger flag (HK-133 lab 默认开, 生产另份 compose 关); 本 commit C6 TODO/CHANGELOG/CLAUDE.md 同步. CI drift gate 与 dead-field-ratchet 平级在 build-push job 开头, 业界对照 Stripe/Anthropic/OpenAI 都对 OpenAPI spec 跑同等闸 (PR 必须 commit 生成产物). smoke: ANTHROPIC_API_KEY=fake go run ./cmd/common --rest-addr=:18080 --swagger → /api/v1/health 200 + /swagger/index.html 200 + /swagger/doc.json 200 返回 commit 1 嵌入 spec; build + -race 全绿. 三件套全产: 业务 REST swagger.json (12 schema) + 观测 gRPC grpc-api.md (HealthService + SafetyChainService 字段表) + CONSUMERS.md (端口/env/flag/auth 流程/curl 例子). 不做*: SDK 自动生成 (Stainless 模式 rule of two, 等 5+ 语言客户端); .proto 字段注释完善 (health.proto 部分字段 description 列空, 后续工作).
  • 2026-04-26 L692 platform/common 业务 REST/SSE 通道激活 (6 commit): 3-agent review reconcile (调研 11 LLM API + grpc-gateway 健康度 / 质疑 6 道含 raw bearer auth 与 OIDC 不兼容致命 + grpc-gateway 与 admin 重复致命 / 设计 4 备选 + 推荐 A 修正版), PM 拍板. commit 顺序: 01f08e7 C1 server.go 拆 buildHandler/Serve/ListenAndServe-wrapper 让出 signal handling; 8189d05 C2 Verifier 替代 BearerToken 走 auth.HTTPMiddleware (raw shared-secret + ConstantTimeCompare 删除, dev 模式 Verifier=nil 与 admin 一致); 694bd07 C3 server.New 接受外部 engine 不再内嵌 anthropic provider 写死, 加 Attach + HandlePermission 两 API; e1db327 C4 cmd/common 加 --rest-addr flag, 装配 anthropic provider + engine + s.Attach + 第三 listener wire (signal handler 三路协调 grpc.GracefulStop / httpSrv.Shutdown / restCancel, errCh capacity 升 3); c35e761 C5 docker-compose expose 8080 + command 加 --rest-addr=:8080 + ANTHROPIC_API_KEY 必填 environment, Caddyfile /api/v1/* → common:8080 (flush_interval -1 + response_header_timeout 0 SSE 透传), README topology + curl 例子同步; 本 commit C6 ADR-0002 + 文档同步. 核心决策: REST/SSE 唯一业务通道 + gRPC 仅观测面 (SafetyChain / Health) + 不为 C# 单独加业务 RPC + 不加 grpc-gateway (admin 已有观测面 REST handler) + Tool 级 SafetyChain 装饰留给行业 platform (cmd/common 保持纯 transport, verdictStore 接线就绪等行业写数据). 业界对照: 11 LLM API 全 REST/SSE 单通道, 业界共识与 PM 方向对齐. 4 tracked debt 登记 (L693 多副本 SessionStore P2 / L694 cross-transport request-id P3 / L695 SSE 带宽监控 P3 / L407 Swagger 与 ADR-0002 关联). 测试: server_test.go 既有 546 行用 httptest 直接打 handler 不动, 删 4 raw bearer TestAuth_*, 加 1 ctx 测试. -race 全绿. cmd/common --help 验证 --rest-addr flag 出现.
  • v0.2.0 发版 (2026-04-24): CHANGELOG Unreleased (v0.2-dev)## v0.2.0 (2026-04-24) 结构化重组对齐 v0.1.0 段格式 (核心新增 / platform/common 层 / 关键设计原则 / 已知限制 / 发布事实 / 详细变更); 新起 ## Unreleased (v0.3-dev) 空段. 覆盖面: evolve 9/9 矩阵 + SQL 工具链 × 3 + validator/circuitbreaker/reflector/staging 4 新子包 + safety chain C1-C4 platform/common 装配+观测+gRPC. Baseline 212 → 216 (+4 合法 tracked debt). TODO 46 → 47 done / 15 → 14 open.
  • (2026-04-24, staging 子包): pkg/staging/ commit 3/3 完成 (L436 check off)Engine 混合控制状态机 (Stage / ValidateTech / ValidateBiz 内部主动推进, MarkExecuted / MarkFailed 外部推 arc 薄转发) + fail-closed 语义 (Validator error / DependencyGuard error 均合成 Block verdict 或 ErrDependencyDenied, 不静默放行) + 内置 TenantDenyGuard 示例 (metadata-driven guard 典型形态). ~530 行 (含 14 engine test + 内置 guard). baseline 218 → 216 (Record.Diff / Record.Metadata drain); 剩余 4 条 (TechVerdict/BizVerdict/ExecutionError/ExecutionProof) 合法 tracked debt — "外部 audit dashboard 消费, core 无内部 reader" 形态, 按 memory feedback_exported_field_delete_needs_review.md.
  • 2c38e46 (2026-04-24): pkg/staging/ commit 2/3 — Store 接口 (9 方法) + InMemoryStore 参考实现. MarkExecuted 幂等 first-write-wins 保 proof, MarkFailed 幂等保 reason. 17 test 含 2 race 并发 (ID collision / CAS race). baseline 223 → 218 (9 drain + 4 新 dead).
  • d9992d5 (2026-04-24): pkg/staging/ commit 1/3 — 7 状态机 + Record + DependencyGuard interface + AllowAlwaysGuard. 决策包级 record, 做法 I 整体打包. baseline 212 → 223 (tracked debt 登记 + exit criteria 声明).
  • 979a304 (2026-04-24): pkg/reflector/ umbrella 包 — 表达 "反射器" 产品抽象, 不引新接口, 4 adapter (ValidatorAsEvaluator / EvaluatorAsValidator / ValidatorAsReflector / EvaluatorAsReflector) 跨家族互转. 反向 Reflector → Validator/Evaluator 刻意不做. Option d 胜 Option a (Agent Teams 3 角色 review: 真合并违 Go 惯例 + sync/async 语义不可同一接口). tracked debt: GA 场景若需 "按 fitness 级别映射 Severity" 的连续分级, 需加 WithSeverityFunc(func(fitness) Severity) option; 当前二值 (Warn/Block) 是 validator.Severity 的物理约束.
  • 1a08c47 (2026-04-24): C4 platform/common 安全链 gRPC 暴露 — safetychain.proto (2 RPC: ListVerdicts / ListBreakerStates) + SafetyChainServer + cmd/common wire. 候选 A 端到端闭合 (core + common 装配 + HTTP 观测 + gRPC). 工具链 install protoc-gen-go / protoc-gen-go-grpc 到 $GOPATH/bin (一次性).
  • 753ec03 (2026-04-24): C3 platform/common admin 观测端点 — VerdictStore + RingStore + admin.WithSafetyChain + 2 新 HTTP 端点 (verdicts / breakers). 鉴权和 /admin/tenant 同级, opt-in 才挂载默认 404.
  • cb8cd2f (2026-04-24): C2 platform/common safetychain 装配层 — Assemble() 一行装配 + BreakerScopePolicy 三工厂 (NoOp/DestructiveOnly/PerTool) + BreakerRegistry. 无默认显式 opt-in, 行业 platform 自选 backend + 作用域.
  • 1b0a860 (2026-04-24): C1 core 安全链 enforcement — validator.AlwaysApprove{} 显式 opt-out + NewValidatedTool 构造期 nil panic. 候选 A (platform 装配框架) 启动前的 core-level enforcement, 消灭 "以为开了审批实际没开" 的静默安全假象.
  • 981a2ea (2026-04-23): 模块 23 立项归档 + 消费层分类翻盘 (SQL 3 件套从 "不属于引擎层" 挪入核心)
  • a935604 (2026-04-23): SQL Dry-run 三路 (UPDATE/DELETE/INSERT, 方案 E before+after SELECT)
  • bf31278 (2026-04-23): SQL CAS 乐观锁 (StagingDB newtype + maxRetries=0 fail-fast, modernc.org/sqlite test-only dep)
  • 79670c7 (2026-04-23): SQL 只读校验器 (纯字符串 quote-aware 解析 0 DB 依赖)
  • 2026-04-23 doc drift 修复 (本 commit): CHANGELOG "已知限制" 移除 SQL 条 + v0.2-dev 段加 SQL 工具链纪念段; 本统计块切换文件内口径; CLAUDE.md 分层统计对齐 (引擎 4 / provider 2 / platform 9); memory project_version_roadmap.md v0.1 发版事实更新.