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, 引擎层仅 importdatabase/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)¶
StagingDBnewtype +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/sqlitetest-only dep (core 第一条非图像处理第三方依赖, 仅_test.goimport)
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)
DryRunResult3 字段首次 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:
89c38d3C0 path bug fix/v1/* → /api/v1/*(Caddyfile + ADR-0002 + server.go 8 mux 路由 + server_test.go ~25 处 + main.go 注释);26bc732C1 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} 首版;bce7670C2 cmd/common --swagger flag + Swagger UI endpoint (httpSwagger v2 + side-effect import docs, authMiddleware/rateLimit allow-list 加 /swagger/);22b6388C3 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);a9b04abC4 docs/CONSUMERS.md 顶层 wrapper 133 行 (端口拓扑/env/flag/OIDC auth 流程图/业务 REST 一次性+多轮会话 curl 例子/观测 gRPC SafetyChain 指引/进一步阅读链);bf5af1dC5 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) Caddyfilehandle /api/v1/*不剥前缀, server.go mux 注册/v1/*不匹配, 经 hub.flytoex.net 全 404, L407 之前先打通真实部署链 (memoryfeedback_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 天前 memoryfeedback_architecture_principle_over_rule_of_two.md的 "gRPC 优 HTTP" 是分层不是反悔). commit 顺序:01f08e7C1 server.go 拆 Serve+wrapper 让出 signal handling;8189d05C2 Verifier 替代 BearerToken 走 auth.HTTPMiddleware (raw shared-secret + ConstantTimeCompare 删除, OIDC 与 admin 一致);694bd07C3 Attach + HandlePermission 拆 anthropic provider 写死, server 不再读 ANTHROPIC_API_KEY 不再 hard-code 模型;e1db327C4 cmd/common 加--rest-addrflag 装配 anthropic provider + engine + s.Attach + 第三 listener wire (signal handler 三路协调: grpc.GracefulStop / httpSrv.Shutdown / restCancel);c35e761C5 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_RespectsCtxctx 取消干净返回. -race 全绿. cmd/common binary 集成验证--help显示--rest-addr. 实测 (docker exec curl + SSE chunk timing) 留 push tag 前按 memoryfeedback_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 顺序:eec48feC1 SessionStore 接口 (Create/Get/Delete 三方法, 不要 Touch/List 投机) + InMemoryStore drop-in 替换 server.sessions map + 4 handler 改造 + TOCTOU race 折叠 (319 行 + 改 server.go 182 行);beaff60C2 Postgres 后端 +internal/db/共享池 (中央 schema 权威 platformMigrations + Migrate 启动期 idempotent) +--postgres-dsnflag + docker-compose pg service + healthcheck + persistent volume + release.yml POSTGRES_PASSWORD secret + testcontainers-go 真 pg 测试 (~510 行 + 改 main.go 53 行 + docker-compose 48 行);96e893aC3 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_secondmetrics, Grafana dashboard 跟踪. 不优化: 监控只是为了量化, 真到瓶颈再优化 (压缩 / batch / fallback gRPC streaming, 都是 v1.0+ 课题). -
[x] 🟡 L696: quote-dispatch 端点上线 ✅(2026-05-03, ADR-0008 v2.2 follow-up). 把 cmd/quote-engine-probe r31 v7 实证收敛的多轮主↔子 agent 转发 loop 抽到
platform/common/quotedispatch/通用包, 物流 platform 可经POST /api/v1/billcost/dispatch(multipart xlsx + sub_prompt + main_prompt) 同步消费. 同步路径 200 + final JSON + cost; await_human_input 路径 503 + Note (HumanInputProvider 是 UnavailableProvider stub, P2 接 SSE 推 + POST 答). probe 改用 quotedispatch.Run, 与 server 字面共享 loop (跟 ADR-0008 v2.2 schema drift 单一定义点同思路). 5 新文件 (doc + dispatch + provider + engine_factory + postprocess + sheetdump) + 2 改文件 (server.go 加路由 + cfg 字段; cmd/common 加--deepseek-api-key/--quote-prompt-dirflag) + 1 server handler 文件 + 测试 30+ 单测含 fakeEngineRunner test seam. swag 注解走 server.go 同款体例,make -C core docs-swag重生 swagger 三件套. 详 CHANGELOGUnreleased (v0.5-dev)段. -
[ ] ⚪ L697: HumanInput SSE 通道实装 (P2, 2026-05-03 登记自 L696 follow-up). 背景:
quotedispatch.HumanInputProvider接口已立, server 现接UnavailableProviderstub → 触发 await_human_input 时 503. 真实装是 SSE 推 question 出去 + POST/api/v1/billcost/dispatch/answer接 channel reply 桥接 (跟 server.go 现有/sessions/{id}/permissions/{request_id}同形态). 产出: 新 endpoint + provider impl, 接到s.AttachQuoteDispatch(cfg)注入. 阻塞: UI 选型 (PM 主进程谈 UI). 范围限定: dispatch loop 不动 (HumanInputProvider 接口设计已为这一刻准备好), 只改 server 端. -
[x] 🟡 L713: 收敛前冷眼验收 agent (ADR-0012 §2.7 硬约束) ✅(2026-05-31). bb34a117 实证 §2.3 跨轮记忆让能力够的 main 也因 "我办过了" 错觉停止复查, R7 把 R4 诊断过的 25 省 base_weight 错盖章放过 (§5.3). 拆出独立第 4 个 agent, main 给 ok 时强制零历史 session 重核最终 JSON 对原表, 验收也 ok 才收敛, 验收驳回路由回 main 改 sub prompt. 可配置 (
AuditEnabled默认开 / provider-model 默认 main / 独立_audit.md) + 可观察 (source=auditor 落库 + OnEvent + 日志). plan A:MaxAuditDisagreements=2 兜底 + fail-open. 改 dispatch.go/result.go/agentprompt/store/handler/cmd-common/probe + 新_audit.md, 4 gate 测试 + 1 persist 测试 -race 绿. 详 ADR-0012 v6. - [ ] ⚪ L714: 验收 gate plan B + 验证实验 (P2, 2026-05-31 登记自 L713 follow-up). plan B: 验收↔main 反复僵持时升级
await_human_input拉人工裁定 (当前 plan A 靠 MaxRounds 终止为未收敛); 需把 dispatch 现有 await 处理抽成可复用函数让 gate 复用. fail-closed: audit transport 加固 (重试 / 降级) 后把 §2.7 的 fail-open 改 fail-closed. §5.1 实验: 验 "种子规则 + 人答 (无 main 进化 blob) 能否一遍抽对" (已在 /tmp 用 probe 初验通过, 待正式固化); §5.2 实验: 复用验证需第二张同三元组表 (改价不改结构 / 换结构换承运商), 现只有 ytosample 一张, 阻塞于真数据或派生表. - [x] 🟢 L715: await 人工答复结构化落库 (ADR-0012 §2.5 前置底料) ✅(2026-05-31). phase-1 await 实际答复此前只走
chan []string-> main session 历史, 没存离散记录, 致 (a) PM-vs-Claude 答复无从对照 (b) §2.5 复用缓存要的 "结构性人答" 无处存. 改: 每次 await 迭代 (question, answer) 对落quote_dispatch_rounds.human_answers JSONB(跟同行 main verdict 并置, 自包含带 question 文本).HumanAnswerPair+RoundTrace.HumanAnswers+buildHumanAnswerPairs同迭代 index zip + pool.goALTER ADD COLUMN+ PostgresStore/InMemoryStore wire + persistRoundTraces +review-dispatch.pyA:行. 3 测试 (TestBuildHumanAnswerPairs+TestRun_AwaitMultiQuestion扩展 +TestPersistRoundTraces_HumanAnswers) 全模块 -race 绿. §2.5 资产分层本身 (拆三层入缓存去数值) 仍 roadmap. 详 CHANGELOG + ADR-0012 §2.5. - [ ] ⚪ L716: 轮数 2 vs 4-5 系统性调查 (P2, 2026-05-31 登记, 由 L715 unblocked). 背景: deepseek-chat UI 多 session (264e8604=2 / 0c6d9898=2 / e5c48c92=4 / 5f2a7145=5 / 00586b5f=2 / f767b22e=2 / aa96c30b=2) 多数 2 偶 4-5, PM 疑系统性 (PM 一直 2, Claude 跑出 4-5) 非随机. 提示词已逐字节证相同 (bind mount). 未排除变量: await 答复措辞 (Claude pipe 的死答案 vs PM 精准答, 可能让 main 写过宽 "默认3kg" 规则 -> sub 把 3kg 渗进标准段 -> 多修轮; myrun1 4 轮审核者 R3 抓 25 省 base_weight 全渗 3000 = 真渗透实证). 产出: 拿 PM 真实答复在 probe 复跑, 对比 Claude 答复跑, 坐实是答复差异还是 sub 随机. 解锁条件: L715 落库已就绪.
- [x] 🟢 L717: 冷眼验收 agent 可单独选 provider/model (跨模型验收 wire, ADR-0012 §2.7) ✅(2026-05-31). 上轮 §2.7 留
auditProvider/auditModel/disableAuditGrammar三 struct 字段未 wire (后端dispatch.Request+ dispatch.Run fallback main 早就绪). wire 完:parseDispatchMultipart解析audit_provider/audit_model(同 sub/main registry), 未选留 nil/"" 回落 main;disableAuditGrammar按 AUDIT provider 判与 main 解耦 (deepseek 验收即便 main 本地也关 json_schema);dispatchReq设三字段;index.html验收区加 Provider 下拉 + Model 输入 (留空=跟随 main).TestParseDispatchMultipart_AuditProviderOverride-race 绿. 含义: 默认验收 main 同模型去记忆, 选不同模型 = 逃 main 共享盲区. - [x] 🟢 L718: xlsx 内嵌图通知 VLM 解析 (独立平行输出, ADR-0012 follow-up) ✅(2026-05-31). 报价表 xlsx 内嵌的调价通知/加收费公告图 (ytosample 6 张 PNG: 内蒙古加收派费 / 上海发各省中转费重量段调整) 此前被 excelize 整个漏 (只读单元格). 仿 bill-recon 迁过来但独立输出: VLM 转录作附加平行结果, 绝不进 SHEET_DUMP / sub-main-验收 (避免污染基础抽取 + 误导验收 gate, 对齐 bill-recon C6 vision 独立 AdjustmentBundle). 新
quotedispatch/embedded_images.go(archive/zip 读 xl/media, 中性不 import billrecon) +internal/server/quotedispatch_vision.go(VisionExtractor接口可配置/可注入 +MinimaxVisionExtractor复用 coreminimax.ExtractVision, 忠实转 markdown prompt) + handlerRun后提取->VLM->独立embedded_imagesfinal 字段 + 流式事件 (best-effort 单图失败不致命) + cmd/commonMINIMAX_TOKEN_PLAN_KEY注入 + index.html 流式显示.Run/sub/main/验收/SHEET_DUMP 零改. 4 测试 -race 绿. 未做/未决 (PM 拍): merge 进报价 vs 永久独立 / 落库 / 独立图片上传 (现只内嵌图、final 出不落库). 未部署 (PM 醒来拍). - [x] 🟢 L719: 内嵌图改"结构化临时加价"抽取 + 修"看不到图片解析"真因 (ADR-0012 follow-up, range A) ✅(2026-06-01). PM 纠偏 L718 (内嵌图不是旁路 markdown, 是报价重要组成部分 = 临时加价, 要抽网点/揽收-签收/重量段/delta). 推倒重做:
quotedispatch_vision.go重写VisionExtractor返*parser.AdjustmentBundle+ 复用billrecon/llm.VisionClient(additiveExtractShipCostCfgWithPrompt, 对账零影响) + billcost 单独_vision.mdprompt + per-image 90s→180s. 修真因: 图片抽取从 Run 后挪到请求入口并发 goroutine +context.Background()(旧实现坐 20 分钟 Run 后用 r.Context() 被 reap 致 6 图 context-canceled, PM "看不到图片解析" 真因) + deferred cancel+join 防 emit-after-return. 配置面板 (开关+provider/model 下拉, 非 minimax 标未接入) + per-request vision_enabled/provider/model + UI 结构化 review 替 markdown. 验证对真值: spike 6 图揽收/签收 6/6 判对 + image5 18 网点全抽 + 端到端真传部署路径 6 图实时流出. 已部署 m2max (fastpush). UI_vision.mdprompt 编辑本轮已做 (跟 _audit.md 同款 RawVisionPrompt + prompts/defaults/save, 免重启即时生效); image1 长生成瞬态EOF加 per-image 有界 retry (3 次+2s backoff) 救回. range A follow-up (未做): (1) 临时加价 merge 进最终报价 JSON (按揽收/签收时间基准 join, 同 bill-recon C7 难题) ; (2) 落库 embedded_images (现内存随 final 行出, 无 read-back 消费者故未落; merge 那步需它做输入时补 — 加 quotedispatchstore 接口方法 + JSONB 列 migration) ; (3) 多页通知 (image5/6 跨页定价+日期) 逐图独立抽取拼不起, 已知限制. - [ ] ⚪ L720: top_k / min_p 映射 Anthropic + Gemini wire (P3, 2026-06-02 登记). 背景: 2026-06-02 加了
flyto.Request.{TopK,MinP}(抗循环采样, 见 CHANGELOG), 但本轮只映射了 openai-compat wire (covers ds4 / 本地 vLLM + 6 个 openai-compat provider). 未映射 (tracked gap, 已在 flyto.Request godoc + wire 测试TestGeminiBuildRequest_TopKMinP_NotMapped显式标): (a) Anthropic native top_k — Anthropic Messages API 原生有 top_k, deepseek/anthropic provider 的api.MessageRequest没接; (b) Gemini generationConfig.topK — Gemini 有 topK 但core/internal/wire/openai.go的 Gemini buildRequest 没映射. min_p 是 vLLM/本地专属, Anthropic/OpenAI/Gemini 都无此概念故不适用. 产出: 接 Anthropic + Gemini 的 top_k 映射时, 同步更新 flyto.Request.{TopK} godoc 的 per-provider 映射表 + wire 测试 (把 NotMapped 测试改成 Mapped). 不阻塞: 当前 billcost 只用 ds4 (openai-compat), 已覆盖. - [ ] ⚪ L721: 通用 reasoning-marker 归一器 (引擎层 reasoning 契约, ADR) (P3, 2026-06-02 登记). 背景: reasoning 模型把思考漏进 content 文本通道是跨 provider 通病, marker 语法各异 (MiniMax/Qwen/deepseek-r1 =
<think>...</think>; 本地 Gemma omlx =<|channel>...<channel|>...<turn|>; gpt-oss = harmony). dispatch 的extractJSON首{末}遇 reasoning 含花括号 (抽报告常见) 即破. 本轮已修 MiniMax (provider 层reasoning_split让服务端拆到 reasoning_details, content 干净, wire 已路由成 ThinkingDeltaEvent, 见 CHANGELOG) -- 但那是 MiniMax 专属机制. 契约 (PM 2026-06-02 拍): provider 声明自己模型的 reasoning 格式 -> 引擎归一进统一 Thinking 通道 -> 消费者无感. 未做: 给"内联且无服务端拆分"的后端 (本地 Gemma omlx / gpt-oss) 在 wire 层做 marker 归一器 (跨 chunk 状态机剥<think>/<|channel>-> ThinkingDeltaEvent). 顺手退役: 现phase0的stripChannelMarkers是消费者层补丁 (抽象泄漏), 归一器落地后下沉删除. 不阻塞: 当前 billcost 用 MiniMax, reasoning_split 已覆盖. - [ ] ⚪ L722: MiniMax-China dispatch 主循环可靠性 (瞬态 EOF retry + 审核误判, ADR) (P3, 2026-06-02 登记). 背景: M3 main / M2.7 sub 真传 ytosample 跑 dispatch, MiniMax 中国节点 (api.minimaxi.com) 实测 3 跑 3 结局: converged=true /
unexpected EOF(上游流中途掐断, 本地 omlx glm-air 也中招 = 通用 transport 问题) /new_sensitive (1027)(输出内容审核误判掐流, China-region 特有). 核心 main<->sub 主循环裸奔无 retry (bbc3a76 的 EOF retry 只在 vision 内嵌图 per-image 路径). 产出: (a) 瞬态 EOF retry 放 wire 层 (OpenAICompatClient.Stream) 让所有消费者 (dispatch/vision/未来) 白嫖 -- 难点: 流式中途 EOF 已 emit 部分事件, 透明重试需 wire 缓冲/重放, 非平凡; (b)new_sensitive审核误判 -> 评估 RegionGlobal (api.minimax.io, 若 code-plan key 允许) 或 dispatch 层 retry. 影响产品决策: MiniMax-China 当前 dispatch 收敛成功率 ~1/3 (sub 抽取本身干净完整 + 对真值优秀, 是 main 端被掐). 真因部分已拆出快修: main(M3)verdict=retry轮整段重写 ~16K 字符 sub prompt = 巨型合法输出撞 64000 上限被掐 (PM dump 思考流确认; 2026-06-02 快修: per-requestmain_max_tokensUI 可设, 预填 200000, 见 CHANGELOG). L722 剩余范围: (a) 纯 mid-stream EOF (实测一次停在 ~22K token 没到上限 = 上游/网络掉线, max_tokens 治不了) -> wire 层瞬态 retry; (b)new_sensitive审核 -> region 切换/retry. 不阻塞: reasoning_split + 抽取质量 + max_tokens 快修已各自落地/验证. PM 决定 (2026-06-02): MiniMax billcost dispatch 当前放弃使用 (跟本地 ds4 一样), 但 provider 注册 + M3 spec + reasoning_split 全保留作备选 (叠加而非替换原则), 待 MiniMax 官方自修 EOF /new_sensitive审核 /2013空内容校验后现成可启用 -- 不删. 另: retry+new_prompt 空 user 消息 (2013 的直接触发) 已 provider-agnostic 修掉 (见 CHANGELOG), 与 L722 的 mid-stream EOF / 审核两项剩余范围解耦. - [x] 🟢 L723: billcost 内嵌临时加价图 overlay P1+P2+P3 完整弧 (预览/可编辑/三元组 + 落库/confirm 审核 + base+overlay 统一分析视图, ADR-0013) ✅(2026-06-03). ADR-0013 (407 行, 实现就绪) 把 L719 range A 的内嵌图能力定位为 base 报价之上的 overlay, billcost 自有 flyto 表作 bill-recon 继任者. P1 (本项, 无落库): handler
embedded_image事件加 base64-inline 图字节 (media_type+image_b64无条件含失败图) + 抽取成功 stampVisionAt(修 §2.7 audit 反转) +parseDispatchMultipart接承运商身份三元组whs_id/ship_type_id/ship_type_msn/site_no(§2.10, VLM 抽不到, P1 接住+可观测 log, P2 stamp); 新EmbeddedAdjustmentReview/AdjustmentCard(图预览 beside 可编辑表 + 三态完整度 badge §2.9 + 空-details master 可编辑 + is_target_specific 派生 + 删行/加行/采用-剔除 + 稳定 _rid key); 三元组 free-text 捕获区 (无 WMS 字典, 默认 W02);parser.ChinaProvincesgodoc 31->34 顺带改对 (§2.4 防谎言传染). 验证对真值: jsxcheck + go test -race 绿 + 真跑 ytosample 6 图 (b64 未截断/VisionAt stamp/三元组 server log) + headless Chrome 渲 6 卡三态 badge (5 已解析 + 1 缺生效日期). 已部署 m2max (force-recreate binary). P2 ✅ + P3 ✅ (2026-06-03): P2 落库 (DDL 两表进 platformMigrations +quotedispatchstoreSaveAdjustments/ListAdjustments吸收SaveShipCostCfg拷非 import, 去 fan-out 全国字面/去 file_site/whole-replace 空仍删/is_target_specific 服务端权威; confirm 端点 store-direct + reject-drop + 三元组 stamp + audit carry 回补 NULL gap + 无 409; image_name 通道决策定: billcost 自有AdjustmentRecord{ImageName,Bundle}wrapper 偏离 §7.2 字面签名是有意取舍). P3GET /analysisbase+overlay 分层按 source table 不 flatten + 前端 AnalysisView 三态 coverage +CHINA_PROVINCES_34view-time 展开全国 (F-COV). 验证对真值: 全量 go test -race (20 包) ✅ + P2 真 DB (docker psql: 全国字面/三元组/is_target_specific 服务端/audit round-trip/reject-drop/whole-replace) ✅ + P3 /analysis live + headless 渲三态 (全国 matched 4/34 展开/黑龙江 orphan/target_sites unverifiable) ✅. + vision prompt 修 (image5 target_sites 去三/四段码). deferred follow-up: ~~base 报价 master 三元组 stamp~~ ✅(2026-06-04, 用 session 旁列 whs_id/ship_type_id/ship_type_msn/site_no, 非 phase_1_result JSON 注入 — advisor 嘱旁列不 mutate blob; /analysis 加 identity 对象 + AnalysisView 显) / ~~DeepSeek thinking_mode 引擎~~ ✅(L724) / group-by facet 切换 (polish, 仍 deferred). bill-recon 整 app 抛弃是过验收后独立清理 (§7.0, 别在实现阶段删). - [x] 🟢 L724: DeepSeek thinking_mode 引擎支持 (additive flyto.Request + openai-compat wire + UI, sub/main 拆) ✅(2026-06-04, PM "全部完成" 批). DeepSeek V4 官方 /guides/thinking_mode: 顶级
thinking:{type:enabled/disabled}+reasoning_effort:high/max, 输出回reasoning_content. 关键坑 (advisor flag, 已修): thinking=enabled 时 temperature/top_p/penalty 全被忽略 -- billcost 此前对 deepseek 既不设 NeedsThinking 也不设 ThinkingBudget (common/main.go 裸 deepseek.New), 故 V4 走默认思考开, PM 调的 sub 采样 (temp/top_p/top_k/min_p) 一直静默空操作 (实锤). 产出: 新flyto.Request.ThinkingMode *string三态 (nil/enabled/disabled, 比旧 NeedsThinking bool 多 "显式关" — V4 默认思考必须能显式 disabled 才能解除采样 no-op);Effort string复用作 reasoning_effort; openai-compat wire 加thinking:{type}+reasoning_effort(omitempty, deepseek streamOpenAI 用其替换旧reasoning:{}对象 — V4 忽略那个未知字段, 旧 enable 是静默 no-op); engine.Config/EngineSpec/dispatch.Request/handler 逐层透传; BuildSubEngine 默认 ThinkingMode="disabled" (修 bug 关键默认, 让 sub 采样生效) / BuildMainEngine 透传 (main 要推理); UI sub/main 各两下拉 (默认 sub=思考关/main=思考开) + handler 白名单归一 (非法值收 "" 不 400). 验证对真值: 真 DeepSeek live 跑 (provider->wire->真 API->解析) enabled->reasoning_content 出 (ThinkingDeltaEvent) / disabled->没有, 两者 answer 都对 — 证明 body 被接受 + 开关真切换非静默 no-op; wire marshal 单测锁 JSON 形态; 全量 -race 绿. 已部署 m2max. 背景: 2026-06-03 PM 实测 sub=deepseek-chat 被 deepseek.com 拒 (别名过渡期 flaky), 显式 deepseek-v4-flash 才稳 (见 memory feedback_verify_error_source_before_theory). PM 验收剩 staging walkthrough (浏览器跑 dispatch 验 sub 翻 disabled 后仍收敛 + 采样真生效). - [x] 🟢 L725: billcost 内嵌图多页通知自动拼接 (VLM 判 page_role/page_number + 续页日期 lift 到首页表格) ✅(2026-06-04, PM 拍板选 VLM 自动判). 问题: 圆通通知一份拆成连续多图 (image5=进博会派费首页有抬头+网点表格无执行时间 / image6=续页有执行时间无表格), 逐图独立抽两页都不完整 (首页缺日期 / 续页是碎片); 实测 image4/5/6 全 A4 同尺寸故不能靠尺寸判. 产出: _vision.md 加
page_role(start/continuation)+page_number让 VLM 读图时顺便判 (实地真值验过 VLM 正确输出); billcost 侧pageMeta从 RawExtraction 解 (不动共享 parser);extractEmbeddedImageNotices重构 Phase1 抽全部→Phase2groupPages保守分组 (只在明确续页信号才合, 判错退回分开显示绝不错合)→Phase3mergeGroup续页日期 lift 到首页 + concat 有意义 detail 行 (丢全零 artifact);embedded_image按组 emit (name=image5.png+image6.png + pages 数组). 验证对真值: 单测 (parsePageMeta/isContinuation/groupPages/mergeGroup/detailHasSurcharge) + e2e live 真 ytosample (6 图->5 通知, image5+6 合并出带表格+日期完整 bundle) + server 全量 -race 绿. 已部署 m2max. deferred: 人工覆盖 UI (手动拆/合) 待 UI 替换; row 级跨页续表 (mergeGroup concat 已天然支持但 page2 无表头续行 VLM 抽取质量未实测, ytosample 此例是 section 级断点). - [/] 🟢 L726: 引擎能力完整暴露 -- 三档模型知识 (ADR-0018) (P2, 2026-06-06 PM 拍 "平台必须完整暴露引擎能力"). 已交付 + LIVE labtest + 落 main (f04d246): 静态目录 (能力面板) + 现场发现 (openai LiveDiscovery /v1/models, fmlx 自托管) + 实测能力 (capability-probe 抽成 core/pkg/capability + ProbeModel, cheap-tier 同步 + 源戳矩阵) + 两表持久化 + 3 端点 + 前端 Section B. 测试: openai LiveDiscovery httptest (happy/fail-loud/静态默认) + manager discover/probe (happy/persist/fail-loud) + store round-trip/cascade; 修预存 keyless postgres bug. deferred follow-up (登记): (1) 深度 probe 异步 -- 跑四贵探针 (caching 阶梯/max_output 128k/tool_count 二分/reasoning_passback) 真多分钟, 需 async job 子系统 (现 dispatch SSE/job 全 stub, ProbeResult discriminated union 已为转 async 留缝, 不破前端契约); (2) probe 矩阵 vision/pdf/batch 从 ModelInfo 合并 -- 现这些 documented 字段取自 capability 包自有 documented 表, 无该 model 条目时显 untested, 与 tier-1 静态目录的 SupportsVision 不一致 (诚实但不一致), 应让 buildModelCapabilities 也吃 provider.Models() 的 Supports ; (3) discover 回灌引擎 ModelRegistry -- 现仅 UI 读面, 不喂引擎路由/budget (ADR-0008 C2 registerQuoteDispatchModels 模式); (4) 未保存实例 test-before-save probe (POST /config/probe, ProviderFactory accessor 已就位) ; (5) HTTP handler 层直测* -- 现 manager + openai 分支已测, 3 个 handler (handleDiscoverInstance/ProbeInstance/GetInstanceCapabilities) 是薄壳但无 httptest 直测, 补 discover happy + nil-provider 502.
-
[ ] ⚪ L698: quote-dispatch 可观测性 metric (P3, 2026-05-03 登记自 L696 follow-up). 背景: round 数 / verdict 分布 (ok/retry+hint/retry+new_prompt/await/exhaust 命中比例) / await 触发率 / cost-per-dispatch / wall-clock latency 等指标当前仅 stderr log. 产出: prometheus exporter 暴露
quote_dispatch_*metrics + Grafana dashboard. 跟 L695 同位 (SSE 带宽) 一并接 OpenTelemetry. 不阻塞: 当前手动 stderr 阅读够用. -
[/] 🟡 L699: P2 UI phase 1 装配 (stage 1 完成) (P1, 2026-05-03 启动 / stage 1 全 5 步 2026-05-04 完成). stage 1 已完成: ① ADR-0009 P2 frontend 架构选型 (commit
5255cbb+af94b0a论证 frame 重写) ② 6 endpoint 类别 11 stub 路由 (commit82cf365+1053e4e14 test +f152e33swag 重生) ③ frontend/ 脚手架 + 5 页面骨架 + 双层 preset (commite09f559) ④ docker-compose + Caddyfile + release.yml frontend image build 接入 (commit8b86d9c). 范围已 ship:/login//flow/runtime/settings5 页面拉真 stub 端点验证 frontend↔backend 通路, 双层 preset 切换演示, SSE event 流 stub 接通. 真实装路径 (stage 2-4): ADR-0010 节点 declarative metadata 软约束 + flow.json 编译期产 engine.Config + DB 接 + 真鉴权 + React Flow canvas + 决策树 + 头像剧院 + Claude Design 协作流接入 (sprint 1 prototype) + L697 SSE 桥接. 阻塞: P2 设计文档 19 处 PM 批注 (业务流程编排页 + 设置面板 + 节点 namespace 等) + Anthropic Claude Design 入职 (3-7 天). 下次 cut tag (v0.5.0+ alpha) release.yml 自动 build flyto-agent-frontend image push registry → deploy job 拉新 image + Caddy reload → hub.flytoex.net 根路径活到 React SPA 工作台. 跨 commit 出口契约: 5255cbb→af94b0a→82cf365→1053e4e→f152e33→e09f559→8b86d9c 七连击 stage 1 全部交付.
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 顺序:
248253eC1core/pkg/counterfactual/Deliverable schema (五步反向思维标准化数据结构, MetadataKey "flyto.counterfactual.deliverable" 跨包 Record.Metadata 落点);0385efdC2core/pkg/skills/reverse_think/MiniMax Anthropic 兼容端点 Go 化 client (替代~/.claude/skills/reverse-think/SKILL.md软性 markdown 在生产 Agent 不可调的 gap);c4b755fC3tools.Metadata.RequiresReverseThinkingopt-in flag (与 RequiresCheckpoint 同源 rule of two);4618b79C4Deliverable.AsReplayEvent→evolve.LogReplayer跨包 adapter (counterfactual import evolve 单向, evolve schema-agnostic 不感知 counterfactual; 关上 PM 担心的 "各行业自写 skill 进化结果停在哪里" 循环);40aae31C5platform/common/safetychain/ReverseThinkingHookreference 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.Hint—Error()附 hint 字段 (2026-04-19) - [x]
transport.APIError.TokenGap—Error()附(token_gap=N)+ 真设计意图达成: compactortruncateAndRetryCompact接WithTokenGapoption, 从 API 精确溢出值一步跳步 (字段 godoc 承诺的"让压缩模块一步跳过 N token 的消息组"兑现), 带TestTruncateAndRetryCompact_TokenGapStride锁定 (2026-04-19) - [x]
engine.Config.Verbose— engine.New 尾verbose_startup+ runLoop 尾verbose_run_loop_startedtrace (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 parseListop=="&" → list.Background=true+ 尾部孤立&wrap 单 child NodeList (Bash 语义:cmd &把 cmd 放后台). (b) extract.go extractContext 加background bool, 两处 NodeList 分支 (extractFromNode + extractAllFromNode) 实现 "left 继承 / right 清掉" 传播:cmd1 & cmd2cmd1 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 新 helperamplifyBackground(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 包新增公共 helperResolveHeredocBody(redir):<<-形式返回 strip 行首 tab 后的 body (匹配 bash 运行时), 普通<<原样返回, nil/非 heredoc 返回 "". bash_security.goIsDangerousCommand+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 []boolparallel 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/QuerySourceFromCtxctx 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_violationobserver 事件携带{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.ComputeCacheBreak 分支发section_cache_breakobserver 事件携带{name, reason}, engine 经buildBundleAndSectionRegistry(observer)透传 observer (NewSectionRegistryWithObserver新构造器保持NewSectionRegistry向后兼容). (2) 默认 bundle 新增首个生产 VolatileSection:default_bundle.go追加mcp_serversvolatile section,engine.buildSystemPromptWithContext从mcpMgr.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". 新公共 helperrunWithRecover(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.New在mcp.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)Evolverstruct 加autoApproveReadOnly bool+New()赋值. (2)Propose()审批逻辑前置 short-circuit:autoApproveReadOnly=true && isReadOnlyEvolution(proposal) → approved=true绕过 approvalFunc. (3)isReadOnlyEvolutionhelper 刻意收窄, 当前仅EvolveNewSkill符合 (只写 markdown, 不执行代码);EvolveNewTool/EvolveOptimize/EvolveSelfAdjust即便标称 read-only 也不 auto-approve, 因其内容会执行代码或改运行时. (4) 新evolution_auto_approvedobserver 事件与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_overriddenWarningEvent). 不加 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+buildMeta写meta[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.go加dangerousEnvVarPrefixesmap (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.Assignments读assign.Name(case-insensitive) 拦截 env-var 前缀注入 (LD_PRELOAD=./evil.so curl ...过去会绕过所有检查因 ExtractCommandName 剥前缀, 现在精确抓).DangerInfo.Reason含具体 env-var 名,Pattern含完整NAME=VALUE前缀供 TUI / 审计渲染. 顺带 dropCommandInfo.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_strideobserver 事件扩dropped_group_indices []intpayload, 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 +parseLLMVerdictnormalise 读 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() []stringsorted).- 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可直接作为Assemblesink 不需 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
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-29 billcost 子包引入 — 10 字段合法 tracked debt + 2 字段 minimax vision baseline 追溯¶
C1 commit 引入 core/pkg/billcost/ 子包 (Band + Output + ReturnFee + Reflect + CalculateShipCost), schema-agnostic 跨行业可重用 (运费 / 保险阶梯 / 任何 "按维度分段" 抽取). scanner 检出 12 条新 dead, 全部按合法 tracked debt 路径登记 (无 drain 计数).
10 条 billcost 字段合法 tracked debt (与 shadowdb 4 / staging 4 / counterfactual 1 同形, "core 内部无 reader, 外部 consumer 在 scanner 视野外"):
外部 API 表面字段, 由 platform/common 在 C2 (LLM 直出 schema) + C3 (反射器自纠循环) wire 跨 module 消费. core 内部 reflector 不读这些字段是设计选择 — Band 旗字段是持久化层路由旗 (不是反射器关心的形状), Violation 字段是 LLM 反馈循环载荷 (反射器只发出, 不消费):
Band.IsDeliveryFee/Band.IsTargetSpecific/Band.TargetSites— 非 WMS 原生扩展旗 + 网点列表, platform 持久化层按旗分发到 ship_cost_cfg 各列ReturnFee.Summary— 退回件 review UI 显示Violation.RuleID/.Partition/.WeightG/.GotCost/.PrevCost/.Detail— C3 LLM 自纠循环把 Violation slice 序列化喂回 LLM, 6 字段都是回喂 prompt 的载荷
2 条 minimax vision 字段 baseline 追溯 (commit 426f623 残留, 本次 C1 重生 baseline 时一并捕捉):
pkg/providers/minimax.VisionRequest.MaxTokens— vision 请求 max_tokens 字段, provider 调用方设定 (与 minimax.Request.MaxTokens 同位但 vision 端点独立 wire 协议)pkg/providers/minimax.VisionResponse.Content— vision 响应 content 字段, provider 解析填. 与 anthropic / openai response 包装类同形 — wire 协议字段刻意 round-trip 完整, 留备未来工具调用扩展 (vision tool_use response 路径)
baseline 220 → 232 (+12 合法 tracked debt), 不计入应 drain 指标. 上述 12 条不列为实现债务.
2026-04-29 C6 follow-up — 2 字段 drain (232 → 230): C6 的 ReflectTool (core/pkg/billcost/tool.go) 把 Violation.RuleID + Violation.Detail wire 进 summarizeViolations (LLM 工具调用返回的人类可读输出), scanner 视野内首个 reader 出现, 自然 drain. 剩余 10 条 (Band 旗 3 + ReturnFee.Summary 1 + Violation 4 + minimax vision 2) 保持 tracked debt 状态.
2026-04-25 L569 反事实子包引入 — 1 字段合法 tracked debt (与 shadowdb / staging 同形)¶
L569 缩水 6 commit 完成时, scanner 检出 1 条新 dead — tools.Metadata.RequiresReverseThinking. 该字段在 platform/common/safetychain/reverse_thinking_hook.go 的 Lookup 回调里被读 (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.md 与 feedback_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.CreatedAtSession.ShadowTable— 外部 consumer 在 SQL 里引物理表名, decorator 与 EnforceSessionFilter 也可读取. core makeCloser 走 trackedSession.shadowTable 闭包不读 sess.ShadowTableSession.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_crossedobserver 事件. trackEvents 尾部 30 行代码抽取为新方法applyTurn(方便测试, 也解耦 emit 逻辑). 引入包级常量costThresholdsUSD = []float64{1,5,10,50,100}和 helpercrossedCostThresholds(last, cost) → []float64(返回 half-open 区间(last, cost]内的升序档位, 支持单轮跨多档). Session 加lastCostThresholdEmitted float64guard 防同档重复 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-switchcase *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.FileCacheEntry6 字段真 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.CacheStats3 字段 (Entries/MaxSize/Evictions)[~]归档: 已在 docs/api-reference.md 列为 pull API (和 SessionStats/DenialStats 同列), godoc 升级双语写清消费形态 "调取 pull, 消费者主动调 FileStateCache.Stats() 读字段, scanner 视野内无内部 reader 是期望状态, 不强加内部 reader 过扫描器". 不减 baseline.tools/builtin.FileStateCacheEntry5 字段[~]归档: 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/End→RedirectionInfo.HeredocBodyStart/End→DangerInfo.HeredocBodyStart/End→checkpoint_suggestedobserver 事件 payload (heredoc_body_start/heredoc_body_endkey, 零值归因非 heredoc 场景时跳过). Reason clause 同时带 "source bytes N-M" 文本让人类可读 log 也承载位置. 回归 test 一主题一 sub-claim:TestPreprocessHeredocs_BodyStartEndAddressesOriginalSource4 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.8Background字段同系 (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.Info5 字段 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.godetectGitInfo替换为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.ExecutorDI 层 (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/../.. 越狱). 修复: exportvalidateRef→ValidateRef(GetDiff 内部调用点同步), NewGitSyncAdapter defaulting 块后一次调gitlib.ValidateRef(remote)+gitlib.ValidateRef(branch), bad 值 panic (风格对齐既有 nil Executor panic). 加 2 test:TestNewGitSyncAdapter_RejectsInjectedRemote(--upload-pack=evilremote) +TestNewGitSyncAdapter_RejectsPathTraversalBranch(feat/../etcbranch). 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).mergePayloadhelper 处理 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 模式真隔离 (BashToolpwd落在 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 ASTbash.Node根本无 FD 字段,Operator已持 "2>&1" canonical 字符串, 消费者 (pkg/permission bash_security) 都读 Operator 不需要分解 FD 号. wire 路径需先补 parser + 再补消费者两层 speculative, 不走. advisor 复核通过. baseline 353 → 352. - [x]
Metadata.Destructive/ReadOnlygodoc 诚实化 (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)ToolCallResultgodoc 重写 — 说明这 2 字段是 engine → Event/OperationEntry/Bridge → UI/audit 一条信任边界, 3 处 StoredPath 共享同一条安全约束; (5)bridge event_serializer.gotool_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.Descriptionwire 进 task-notification XML (2026-04-20 晚续 session): 按 4 问 + 反向思考 审判. godoc "任务描述(用于追踪)" 意图两路消费 — (A) 调用方读[]WorkerResultSDK 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) stringpure function 严重优先分发 (blocking → critical → warning → ""), engine.go:3944 warning emit 用 switch-on-code 替换原 else-if, 新增context_window_blockedcode 带 "上下文窗口已占满, 下轮 pre-compact 将被强制触发" 消息 (可观测信号, 非引擎行为承诺). godoc 重写 IsAtBlockingLimit: 从"无法继续对话" → "100% 占满, 仅 observability signal, 真正兜底是下轮 maybeCompact / forceCompact, 严格蕴含 Error 和 Warning 阈值 true". 加 2 test:TestCalculateWarningState_BlockingImpliesCriticalAndWarning锁 implication chain (blocking → Error + Warning 同 true),TestPickWarningCode_SeverityOrder5 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.MinConfidencegodoc "最低置信度要求" 暗示引擎 gate 但零 gate site = 造假, 且 LLM 经 prompt 自报置信度是既有能力不需要模型新特性. 三层 wire: (1)capability.go新增 constConfidenceInputField = "_flyto_confidence"+ helperCheckConfidenceGate(cap, input)(MinConfidence=0 原样放行, >0 时解析/拒绝/剥除); MinConfidence godoc 双语 Wire 段明述 buildToolDefs 提示 + orchestrator gate 两层消费路径. (2)orchestrator.executeSingle在tool.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+DryRunResult3 (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).
- B1 合法 pull API 归档 (9 字段):
-
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.TemperatureC 档已独立归档,FeedbackRecord5 字段上 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 消费路径 (commit22478e6). 未来 session 不再重审, 和engine.CacheStats/FileStateCacheEntry/PlanApprovalEvent/CheckpointSuggestedEvent等[~]归档同列. - A2 C 方案 evolve→engine adapter 缺口 (6 字段, 独立主题, 等业务场景触发):
RuntimeToolMetadata4 (ConcurrencySafe/ReadOnly/IsEvolved/Version) +ToolResult2 (Output/IsError). 整条 "Agent 自造工具 → Engine 工具注册表 → 模型可见" 能力在 core 内从未接.engine_integration.go:13原注释示范for _, t := range evolver.EngineTools()是造假 — Evolver 类型无 EngineTools() 方法, 无 engine.Tools().Register() 调用点. commit22478e6改顶注释陈述现状: 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.FileCacheEntry6 字段真 drain,engine.CacheStats3 字段[~]pull API 归档,tools/builtin.FileStateCacheEntry5 字段[~]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.Description2026-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),
CommandInfo5 +HeredocInfo4 (2026-04-20 前半 session) drain,RedirectionInfo.SourceFD(2026-04-20 本 session) 经 internal/ 包例外删除 (0 消费者 + parser AST 无 FD 支持 +Operator已持 canonical 字符串 "2>&1", 冗余). - git (
git.Info5): 本 session 已 drain +Run原语落地 (详见 L695 下internal/syslib/git.Info条目). - tools 族: 全部处理完毕 — 2026-04-20
Metadata.Destructive/ReadOnlygodoc 诚实化 +ToolCallResult.Truncated/StoredPathdrain, 2026-04-21Result.Datavision wire drain +MinConfidencesafety gate 真 wire + 余 11 字段分档归档 (详见上方 "tools 族深审 (2026-04-21)" 条目). - cache (
ToolStabilityReport4): 工具稳定性报告未 surface. - permission 次要 (
Response2 +DenyRule1 +LearningStats1 +SedCheckResult1 +SuggestedRule1). - builtin 次要 (
ImageResult2[~]归档 +FileEditResultData2 +GrepResult2 +SkillEntryDesc2 +SedEditInfo1).ImageResult.MediaType/Base642026-04-21 vision wire drain,Width/Height2 字段同日[~]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/querySDK 类型, 即便 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. 选项:
- 启动 D 档 drain — 按 D-A → D-B → D-C 顺序一主题一 commit, 节奏同 alpha.8 (sub-claim checklist + 一 sub-claim 一 test). alpha.9+ 周期.
- 锁 386 切 TODO.md P1 消费层 7 条 (SQL 只读校验器 / DB Dry-run / ML 验证器 / CAS / 熔断 / Staging / 影子表) — 消费层是端到端产品价值点, 真债务是内部洁净度, 优先级由 user 判.
- 局部 drain D-A 档 3-5 高价值主题后切 P1 — 兼顾 (SessionStats/PlanApprovalEvent/DenialStats/ClassifyResult 这类 user-facing 信号先落地, 其余留档).
pkg/evolve.LLMCallOpts.Temperature (C 档剩 1 条) 设计决策 (扩 flyto.Request 跨 provider) 与本 sweep 正交, 保留 open 不强制打勾.
代码风格 / Lint¶
Hard Contract 系列 follow-up (2026-05-01, 9 commit e6e715a..316f819)¶
跨业界调研 (Claude Code / Cursor / Aider / Cline / OpenInterpreter) 出 13 条 hard contract gap, A 级 2 条已落地 (Read-before-Edit + Bash 危险路径), B/C/D 级 6 条登记为 P3 follow-up 等真消费者驱动. 详见 core/docs/hard-contracts.md § 已识别的 follow-up.
- [ ] L900 user-audit approval 通道 (
ToolResult.RequireApproval) — Bash 危险路径 / Read-before-Edit 当前 v1 hard reject, 类比 Claude Code 走 user-in-the-loop UI 审批 (一次授权可允) 设计差异. 等 PM 决策驱动. ⚪ P3 - [ ] L901 Bash 完整 POSIX shell AST — bash_dangerous_path.go v1 用 regex + Fields 锚定语句边界, 抓不到
eval "rm /etc"/sh -c "rm ..."/rm $DANGEROUS_VAR包装. cc tree-sitter 风格升级留 v2, 待真用户报告或 LLM 主动这么干. ⚪ P3 - [ ] L902 Tool result size cap — 单条 ToolResult 超 16KB 截断, 防 Glob/Grep 大目录 50MB 字符串塞 LLM context. 业界标准 (Anthropic SDK
max_tokensper tool_use block). 实证频率低, 实测见 1 次再做. ⚪ P3 - [ ] L903 子进程 spawn 深度预算 — Skill A → Skill B → Skill C 嵌套, 加深度限 (max 5 层) 防指数级进程树资源耗尽. 现无 Skill 互调用真实使用, 等真消费者. ⚪ P3
- [ ] L904 跨轮 LLM 死循环检测 — engine 已有 6.4.1 thinking 段死循环检测 (commit 8a0c18f) 抓单轮内. 跨轮 (LLM 在多轮重复同一失败操作如 r11 mistral nemo) 留 hook 层自实装 (safetychain 参考), 不进引擎强制. ⚪ P3
- [ ] L905 thinking_loop_detected guard v2 — v1 用尾部窗口 1KB unique rune 比例阈值 0.02 抓 r11/r15 实际形态. 边缘 case (前 3KB 死循环 + 尾部 1KB 健康收尾) 漏抓. 升级方案: 滑动窗口或 multi-window vote. 实证驱动. ⚪ P3
SOCKS5 token 管理 follow-up (2026-05-13, RFC 1929 鉴权链路上线)¶
flyto-proxy v0.4+ relay 加 RFC 1929 user/pass + UUID v4 token list 鉴权, 2026-05-12 上线 hk-133 prod + m2max staging (FLYTO_SOCKS_TOKENS env 逗号分隔, 各消费者各自配 WMS_SOCKS_TOKEN). 当前 token 是 .env 静态, 加/撤一个消费者需 ssh hk-133 改 .env + restart relay (~5s 拨号中断窗口).
- [ ] L906 SOCKS5 token 管理 admin web UI — admin 通道新加端点 list token (user name + UUID + created_at + last_used) + create/revoke 一个 token + revoke audit 记录. 配套 relay 实现 hot reload (SIGHUP 或周期重读 socks_tokens, 不重启进程). 长期支撑多消费者上线 (b/g 蓝绿切流量 / 第三方接入 / 撤权回归测试自动化). ⚪ P3
ADR-0008 v2.3 follow-up (2026-05-15, handler 解硬编码 + Gemma 4 ADR-0007 capability + m2max staging 跨 model 实证)¶
3 commit (8efe18b + 7704264 + 69a2d60) 把 quote-dispatch handler 解硬编码 deepseek 走 ADR-0007 capability fallback, m2max staging 切 m5max oMLX Gemma 4 (sub gemma4-e4b + main gemma4-moe-26b-a4b-q6), r2 实证 12m25s rounds=2 verdict=await 在深圳单带费首重重量行命中 — 跟 r31 v3 deepseek-v4 同一行同 pattern. 详 ADR-0008 v2.3 + CHANGELOG Unreleased (v0.5-dev) 顶部段.
-
[x] 🟢 L907-A dispatch endpoint chunked NDJSON streaming response ✅(2026-05-15 ADR-0008 v2.4, 1 commit). 解 CF tunnel 100s "origin 没发 byte" 撤. handler 预检过后切 streaming mode (Content-Type application/x-ndjson + X-Accel-Buffering no + WriteHeader 200 + flush started 行), 设 OnEvent callback per engine event flush 一行 NDJSON 让 CF idle 计时器一直见字节, dispatch.Run block 跑完后按结果 flush 终结行 (type=final / type=await_unavailable / type=error). HTTP status 全 200, client 通过终结行 type 字段判别. QuoteDispatchResponse / QuoteDispatchAwaitResponse 保留导出做 NDJSON 终结行 schema mirror 给 SDK 消费者. 测试 lock-in (TestQuoteDispatch_ConfigWired_StreamingPathReached) 验 200 + application/x-ndjson + started + sub_model echo + error 终结行. 详 ADR-0008 v2.4.
-
[x] 🟢 L907-B HumanInputProvider 真接通 + POST /answer 端点 ✅(2026-05-15 ADR-0008 v2.4, 1 commit). dispatch handler 进 streaming 时 uuid.NewString() 生成 session_id echo 回 started 行, server struct 加 quoteAwaitSessions sync.Map (session_id → buffered cap=1 chan string), closure-based HumanInputProvider 在 Ask fire 时 emit
{"type":"await_human","session_id":...,"question":...}行后 select 在 answerCh <-/ctx.Done() block. 新 POST /api/v1/billcost/dispatch/answer 端点 cfg short-circuit 503 / JSON decode 400 / session_id 空 400 / not found 404 / 槽已占 409 / 200 accepted 全 case 覆盖. 5 新测试 -race 绿 (NoCfg_503 / BadJSON_400 / MissingSessionID_400 / SessionNotFound_404 / Accepted_200). 完整 e2e 流程: dispatch streaming → await_human 行带 session_id → client POST /answer → channel 拿到答 → dispatch 继续 → final/await/error. 详 ADR-0008 v2.4 L907-B 段. -
[ ] L908 Gemma 4 vs DeepSeek v4 跨 model 横向对比 r 序列 ⚪(P3, 2026-05-15 自 ADR-0008 v2.3 r2 实证后续). 背景: r2 Gemma 4 (gemma4-moe-26b-a4b-q6 main + gemma4-e4b sub) 跑 ytosample.xlsx 12m25s rounds=2 走到 await_human_input, 业务真因跟 r31 v3 deepseek-v4-pro + v4-flash 命中同一行 (深圳单带费首重重量 ambiguity). cost / quality / latency / round 数 4 维度详细对比未横向跑. 产出: 同 xlsx 同主子组合替换 model 跑 N round, 量化 deepseek-v4 vs Gemma 4 在 (latency / token cost / final_json 字段对真值的准确率 / round 数收敛) 4 维度差异. 驱动: PM 决定 m2max staging 长期主用哪个 (Gemma 4 自托管 0$/token vs deepseek 直连 $0.x/M). 不阻塞: 当前 Gemma 4 已跑通业务路径.
ADR-0008 v2.6 follow-up (2026-05-27, phase 0 sheet 识别 + db-backed session)¶
PM 2026-05-27 m2max staging /billcost-test 测试现场戳穿 "迁移时弄错了" + "也不能光存在内存吧" — v2.6 协议引入 phase 0 + 持久化 session 范式级修法. 详 ADR-0008 v2.6 + CHANGELOG Unreleased (v0.5-dev) 顶部段.
-
[x] 🟢 L909-A phase 0 sheet identification 协议落地 ✅(2026-05-27 ADR-0008 v2.6, 1 commit). 新包
quotedispatchstore(SessionStore interface + InMemoryStore + PostgresStore +quote_dispatch_sessions表 DDL 接 platformMigrations).quotedispatch/加sheet_overview.go(DumpSheetsOverview) +phase0.go(IdentifySheet + Phase0Result).cmd/quote-engine-probe/prompts/phase0_sheet_identify.md新 prompt. handler 改造跑 phase 0 → SetPhase0Result → emit phase_identify_result → await_human 三态 (confirm / override:/ abort) → SetHumanAnswer → phase 1 (现有 dispatch.Run) → SetPhase1Result → emit final. 新 GET /api/v1/billcost/dispatch/{id} 拉 session 现状. cmd/common 启动期调 MarkRunningInterrupted 恢复. deploy/billcost-test/index.html加 phase_start / phase_identify_result / aborted 三 event handler + phase 0 三按钮 UI. 15 单元测试 -race 绿 (sheet_overview 4 + phase0 6 + inmemory_test 5). 详 ADR-0008 v2.6. -
[ ] ⚪ L909-B 长连接断后 phase 1 resume (P2, 2026-05-27 登记自 L909-A follow-up). 背景: 当前 phase 0 await_human 后 client 长连接断了 (network blip / 浏览器关 / common 重启), POST /answer 来时内存 channel 不在, handler 返 409. 但 db state 已干净 (phase_0_result 落库). 产出: 新 endpoint
POST /api/v1/billcost/dispatch/{id}/resume从 db 拿 xlsx_blob + selected_sheet 异步起 phase 1 worker, 写 phase_1_result + done. 或者改 POST /answer 在 channel 丢时自动走 resume 路径. 不阻塞: 当前 happy path (浏览器 tab 不关) 完整工作. -
[ ] ⚪ L909-C parent_session_id 跨 session 复用 (P2, 2026-05-27 登记自 L909-A follow-up). 背景: phase 0 选好的 sheet 跟 phase 1 抽出的 JSON 是后续业务规则反射 / 账单 / 对账的输入. 加下游时希望 client 传 parent_session_id 直接拿 phase 1 结果走下游, 不重跑 phase 0. 产出: dispatch endpoint 接 optional
parent_session_id字段, 从 db 拿 phase_0_result + phase_1_result 跳过这两步直接进下游. 协议字段 ADR-0008 v2.6 已预想. 阻塞: 下游 (业务规则 / 账单 / 对账) 协议自身. 不阻塞: 当前只到报价抽取. -
[x] ~~⚪ L909-D parser/excel.go 解硬编码 (P3, 2026-05-27 登记自 L909-A follow-up)~~ 作废 (2026-06-04): bill-recon 整产品退役 (CHANGELOG 2026-06-04),
parser/excel.go作为零调用死代码已随之删除 (Excel 解析路径只服务 bill-recon, dispatch 链路从不调). 硬编码问题自然消失, 无下游消费者. -
[ ] ⚪ L909-E PostgresStore 集成测试 (P3, 2026-05-27 登记自 L909-A follow-up). 背景: v2.6 InMemoryStore 单测覆盖完整 lifecycle, PostgresStore 测试跳过. 跟 internal/server/sessionstore postgres_test.go 一样需要 dockertest 起 ephemeral pg, 工程量大. 产出: postgres_test.go 跑 dockertest 起 pg, 覆盖 Create / Get / SetPhase0Result / SetHumanAnswer / SetPhase1Result / SetError / MarkRunningInterrupted + SaveRound (v2.6.1) + JSONB roundtrip + BYTEA blob roundtrip. 不阻塞: SessionStore interface 在 InMemory + Postgres 实现间字面对齐, InMemory 单测覆盖绝大部分行为问题.
-
[x] 🟢 L909-F round-level 持久化 ✅(2026-05-27 ADR-0008 v2.6.1, 1 commit). v2.6 db-backed session 只存 phase 0 / phase 1 final, 每轮中间 (sub 输出 / main verdict 含 retry+new_prompt 重写 / 反射器 PASS-Fail 序列) 全丢 — m2max staging 4 轮 18m29s 收敛后没法事后审计 "main 是否塞目标值给 sub 作弊" / "反射器在某轮真跑了吗". 新表
quote_dispatch_rounds(DDL append platformMigrations) FK 到 quote_dispatch_sessions ON DELETE CASCADE, 一行一 (session, round, source), source 取 sub / main / reflector. sub 行填 sub_prompt_used (本轮完整渲染后 system prompt, 作弊审计铁证) + text + tokens / cost. main 行填 text + main_verdict JSONB + tokens / cost. reflector 行填 reflector_pass + reflector_data (block_count / max_blocks / turn / validator_name); 单 sub turn 反射器可 fire 多次 (block -> pass) → 多 reflector 行. quotedispatch.Result 加RoundTraces []RoundTrace字段, dispatch.Run 每轮 append (中途 abort 也 append 部分 trace 让失败 turn 有审计行); 包装 OnEvent 捕获 ResponseValidatedEvent. SessionStore 加SaveRound, handler 调新persistRoundTraceswalk + 写库 (Run 任何结果都写 — converged / max-rounds / error). 6 测试 -race 绿 (quotedispatch 3 + quotedispatchstore 1 + internal/server 2). 详 ADR-0008 v2.6.1 + CHANGELOGUnreleased (v0.5-dev)顶部段. -
[x] 🟢 L909-G 结构化多问 await (选择题 UI) ✅(2026-05-28 ADR-0008 v3.5, 1 commit). v3.4 await 是单字符串
Verdict.HumanQuestion+ inner loop 每答一个重 eval 一次, N ambiguity = N 倍 main token + N 次打断 PM. 改成 AskUserQuestion 式选择题: main 一次列出本轮所有 ambiguity (各带候选 options), 操作员一次答完, main 一次重 eval. 协议改全消费者一起改:agentprompt.HumanQuestion{Question, Options}新类型 +Verdict.HumanQuestions []HumanQuestion+ ParseVerdict 多问校验 + BuildMainAwaitAnswerMessage 多组配对;verdictSchemahuman_questions 数组;HumanInputProvider.Ask([]HumanQuestion) ([]string, error)slice 契约 (答数=问数按位对应) 4 实现; handler channelchan []string+ answer 端点Answers []string+ 按 channel 类型分派 (phase 0 chan string 不动, 最小爆炸半径); UIAwaitQuestionsPanel卡片; main_agent.md wire-contract 同步. 测试: agentprompt + quotedispatch (TestRun_AwaitMultiQuestion) + handler (TestQuoteDispatchAnswer_MultiQuestion) 全模块 -race 绿. 真验证待 staging 实跑 (需 deploy + prompt). 详 ADR-0008 v3.5 + CHANGELOGUnreleased. -
[ ] ⚪ L909-H billcost dispatch 小债清理 (P3, 2026-05-28 登记). 三条: (1)
index.htmlsettings modal hint (line ~738) 仍写 "prompt 经 multipart sub_prompt/main_prompt 传, 留空用 server 默认" — 已被 A (去 per-request prompt override) 推翻, prompt 现在只从 server 落盘读, 文案过期需改; (2)agentprompt.BuildAwaitMessage(单问 sub-routed) 自 v3.4 起无生产 caller (await 路由到 main 不到 sub), dead code 待删 (动它要改 3 个测试, 与 v3.5 主题无关故 defer); (3)extractJSON(agentprompt + postprocess + dispatch sub 清理三处) 改括号配对取第一个完整 JSON 对象, 客户端容错 gemma 重复输出 (登记自 A 的 CHANGELOG gemma 假收敛诊断段). 不阻塞: 都是清理 / 容错增强, 不影响 happy path. -
[ ] ⚪ L909-I 主表首重重量是否 await (PM 产品决策) (P3, 2026-05-28 登记). 主表 3kg+ "面单首重(0kg)" 这类: sub 现填
base_weight=3000(段起点默认) 而非 0 (忠于原表 (0kg) 写法), main 认符合约定不 await 它. 5kg 件现算 4.7 元 vs (0kg)->0 的 6.5 元. 是否让 main 对主表首重重量也 await 拉人工确认 = PM 产品决策, PM 自己调 prompt 的判断内容, 不替他动. wire 层 (多问 await) 已就绪, 决策后 PM 在 main_agent.md 加 cross-check 规则即可. -
[ ] 🟢 L910 模型 A 共享引擎端点的多租户文件隔离 (P2, 2026-06-18 登记自并发审计追问). 背景:
/agent/run+/sessions+/plans三个端点复用cmd/common启动期engine.New的单一长生命引擎 (s.Attach), 注册完整默认工具集 (Bash + 文件读写编辑) 且全在同一个工作目录下跑, 共享一份 fileHistory / fileCache. 并发安全已审计干净 (race-free, 见 memoryproject_concurrent_run_race_audit+ ADR-0019), 但文件系统级不按租户隔离 — 多租户带文件工具的负载路由到这三个端点会互相读写文件. 当前不是活跃风险: 客户报价业务走的是模型 B (quote-dispatch每请求独立引擎 +tools.None(), 完全隔离, 见 api-reference.md "Run 端点选型与引擎隔离模型" 节). 产出 (多租户流量进模型 A 端点前必须收口): 给模型 A 端点接每请求沙盒 + 独立工作目录 — 引擎已支持 (Config.Executor注入 sandbox backend, 见 memoryproject_sandbox_local_vs_cloud;tools.WithWorkdirper-request 覆盖 cwd), 缺的是cmd/common把它们按租户接上 (当前是本地 executor + 单一目录). 不阻塞: 通用控制台 / 单租户 / 无文件副作用用法不受影响.
统计¶
最后更新: 2026-05-15 (grep 精确计数 ^[[:space:]]*- \[x\] / ^[[:space:]]*- \[ \], 包括嵌套子条目, 文件内口径可随时 grep -c 核验)
| 状态 | 数量 |
|---|---|
| ✅ 已完成 (文件内保留) | 55 项 |
| 🔴 P0 未完成 | 0 项 |
| 🟡 P1 未完成 | 2 项 (ML 验证 / 熔断, 全 platform 消费层; L407 文档自动化 + L693 SessionStore 2026-04-26 完成) |
| 🟢/⚪ P2/P3 未完成 | 11 项 (含 2026-05-15 加 L908 P3 Gemma 4 vs DeepSeek 横向对比; L907-A 流式 NDJSON + L907-B HumanInputProvider 真接通 已完成 ADR-0008 v2.4) |
| 总未完成 | 13 项 |
| 总计 | 68 项 (文件内) |
关键观察: 无 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-05-01 ADR-0007 follow-up: DeepSeek 官方接入 + TD-20 实证 + Mix engine 物流胜利 (5 commit): ADR-0007 Capability tracking 决策的实证 + 第 5 个 direct provider 接入. PM 反向论证否决 "openai provider + BaseURL 借壳" 工程捷径, 拍板 deepseek 走独立子包 (与 ADR-0007 § 2.1 direct provider 列表一致). TD-13 + TD-20 落地实施 + 实证, 物流业务 r 系列首次见 deepseek-v4-flash 收敛 (r22-r25 全失败 → r26+ round 2 收敛 7m37s). Commit 顺序: C1
79a5be2wire/openai.go DeepSeek 顶级 prompt_cache_hit_tokens 字段 fallback (Usage struct 加 PromptCacheHitTokens / PromptCacheMissTokens + buildUsageEvent 嵌套优先 fallback; 2 测试) + C201b2022deepseek provider 子包 (双模式 ModeOpenAI 默认 / ModeAnthropic 备选; 2 V4 model 静态填 ADR-0007 三字段 ProviderKind=direct + ToolNameRegex=^[a-zA-Z0-9_-]+$+ ReasoningPassbackMode=string r24 真因 + 文档双确认; provider-level capability fallback 让消费者忘记 RegisterModels 也接通 reasoning passback; 13 unit test 全绿 -race) + C3a6f0c1dcapability-probe 接入 deepseek + caching ladder 路径 (cachingProvider 走 100/300/600/1200 ladder 触发 deepseek auto-cache; 7/7 capability ✓) + C402cf0b3TD-20 三 prober 实测 (probeToolNameRegex 单点 dotted name + probeReasoningPassback 2 round-trip + ProviderKind 静态标; 5 target 注册点全填 providerKind; 实证完美命中 deepseek-v4-flash 服务端字面回 regex^[a-zA-Z0-9_-]+$跟 ModelInfo 静态值一致 + passback "string" 报 r24 真因 "must be passed back" 字面; OpenRouter 经 Azure 路由 claude 4.6 系列实证 regex^[a-zA-Z0-9_-]{1,128}$实际比 anthropic provider 静态填的 1-64 更宽, TD-26 follow-up; ADR-0007 § 2.2 bifurcate direct vs aggregator 决策得到强力实证) + C5ff08515quote-engine-probe 接入 deepseek + 物流 r26+ 实证胜利 (主+子可独立选 minimax/openrouter/deepseek; 跑参 main=minimax-M2.7-highspeed sub=deepseek-v4-flash 官方直连 → round 2 收敛 7m37s; 跨厂商主+子架构验证通过 + ADR-0007 C4 wire 层 reasoning_content passback 在生产场景真生效, 子 agent ~6m thinking + 多轮 tool calling 没被 deepseek 服务端 400 拒). ADR-0007 § 2.2 bifurcate 实证矩阵: direct deepseek-v4-flash (regex=strict + passback=string) vs aggregator OpenRouter→deepseek/v4-flash (regex=permissive + passback=none) — 同一 model id 因后端路由不同 capability 完全相反. 物流 r 系列对照: r21 minimax-only baseline ✅ / r22-r25 OR-deepseek 全失败 / r26+ 官方直连 round 2 收敛 (ADR-0005 + ADR-0006 + ADR-0007 累积修复全栈胜利). Tracked debt: TD-13 + TD-20 drained / TD-22 partial (probe 数据已落, RegisterModels 接通留 follow-up) / 新 TD-26 anthropic 1-64 实际太严调研真实上限 / TD-27 OpenRouter→Azure 路径 (claude 4.6 系列) capability 跟 Anthropic 直连可能不一致 / TD-28 anthropic/minimax 也跑 TD-20 prober. 测试 -race 全绿: wire +2 + deepseek 13 + capability-probe 既有不破坏. -
2026-05-01 ADR-0007 Capability tracking 接入纪律 + Bug W tool_call_id dedup hotfix (6 commit): 物流业务命门 r22-r25 序列暴露"修一层揭露下一层"wire 协议 gap. PM 反思真因不是 5 个独立 bug 是引擎对每个 (provider × model) 能力没真测过全走兜底假设. 3-agent 并行 review reconcile 走方案 B' (3 字段 schema 扩 + opt-in flag 无 strict + bifurcate direct/aggregator + OpenRouter live metadata 消费扩展). Commit 顺序: C1
09b6bc1Bug W wire 层 transport-level dedup hotfix (单 message 内同 tool_use_id 重复 silent skip, 跟 final_text_duplicate_blocks 同源, 切出 ADR-0007 范围) + C2dac31d6ModelInfo 加 ToolNameRegex / ReasoningPassbackMode / ProviderKind 3 字段 (零值=未知=零回归) + C3f77b2c34 direct provider modelInfo 静态填值 (anthropic regex^[a-zA-Z0-9_-]{1,64}$+ openai/minimax/gemini^[a-zA-Z0-9_-]+$+ 全 ProviderKind=direct) + C417f7ba4wire 层 capability-aware (openaiMsg 加 ReasoningContent omitempty / StreamRequest 加 2 字段 / flytoMessagesToOpenAI mode=="string" 时 inject reasoning_content / wire.ValidateToolNames pre-flight 校验返 ErrModelToolUnsupported typed error / openrouter Provider 接通) + C516423acOpenRouter live metadata 消费扩展 (architecture.input_modalities → SupportsVision / top_provider.max_completion_tokens 优先 root / pricing.input_cache_read → SupportsCaching / ProviderKind=aggregator 默认 / ToolNameRegex^[a-zA-Z0-9_-]+$默认) + C6 本 commit ADR-0007 (8 节体例对齐 ADR-0001/2/3/5/6) + 文档同步. 业界对照: SDK 走 string + server-validates (Anthropic/OpenAI Python); 聚合层 (LiteLLM/Aider) 走 PR-maintained model db 30+ 维度. Flyto 拉到 LiteLLM 同档. ADR-0007 § 2.3 红线: capability code 仅 wire 层信号化决定行为, 引擎层禁用 capability code 驱动 auto-fallback (与 ADR-0005 + ADR-0006 § 3 红线同). 与 ADR-0006 互补: ADR-0006 事后归类 (provider 4xx 暴露真因) + ADR-0007 事前预防 (wire pre-flight reject + capability-aware inject) 不互斥. § 物流业务隐性约束: v0.5 周期物流 sub agent 锁定 minimax (r21 0 violations 收敛 ✅) ADR-0007 期间不切其他 model 探索. Tracked debt: TD-19 engine 重复 emit 真因调研 / TD-20 capability-probe 实测扩 3 项 / TD-21 wire details_array mode 实装 / TD-22 OpenRouter per-model ReasoningPassbackMode 实测填 / TD-23 input_modalities 全 modality 字段 / TD-24 消费者 RegisterModels 接通 / TD-25 lmstudio/ollama capability 推断. 测试 -race 全绿: wire 全套 + 4 provider 全套. r26 实证待跑 (物流业务保持 minimax sub 不变). -
2026-05-01 ADR-0006 typed ErrorCode + wire→engine fail-loud cause 链 (6 commit): 物流报价表抽取业务命门 r22 (OpenRouter + deepseek-v4-flash) 3s 内
internal_error中文兜底吞掉真因. 3-agent 并行 review reconcile 实证 task spec 假说 ("wire reasoning_details JSON tag 错") 全错, 真因是 (1) 业务调用方 bug: 工具名billcost.reflect含.违反 OpenAI function-calling regex^[a-zA-Z0-9_-]+$(OpenRouter 转 SiliconFlow HTTP 400 reject) + (2) 独立的引擎 fail-loud bug: HTTP 非 200 路径不读 body / parseNonSSEError 不解 OpenRouter 嵌套 metadata.raw / EngineError.Error() 不暴露 Detail / ClassifyAPIError 字符串 fallback 默认 ErrInternal 折叠形态. PM 反思措辞甩锅 — 两件事性质独立都要修. Commit 顺序: C1f140eb1业务 fix tool 名billcost.reflect→billcost_reflect(CLEVER 注释入档 OpenAI regex 限制) + C2e009350wire OpenRouter parseNonSSEError 解嵌套 metadata.raw (SiliconFlow{code,message}/ OpenAI passthrough 形态; 解一层不递归保 surface 小; 3 测试) + C3960445cEngineError.Error() 拼 Detail (Go 经典 wrapping 形态字符串路径; 2 测试) + C4152903d加 5 typed ErrorCode 词汇表 (ErrProviderHTTPStatus / NonSSE / MidStreamErr / WireUnmarshal / ModelToolUnsupported; ADR-0006 § 3 红线 godoc 入档只分类不行为) + C5b3a083cwire→engine 透传 cause 链 + ErrorEvent.Detail (flyto.EngineError sibling type 镜像 + wire/openai.go HTTP 非 200 修真出血点 + wire/gemini.go rule of two + ClassifyAPIErrorTyped errors.As 优先 + WrapError flyto 分支 Detail 复用 + 3 retry caller 改用 Typed; 5 测试) + C6 本 commit ADR-0006 (core/docs/adr/0006-error-classification-typed-errors.md8 节体例对齐 ADR-0001/2/3/5) + TODO/CHANGELOG/CLAUDE.md 同步. 业界对照: Anthropic Python SDK / OpenAI Python SDK / Vercel AI SDK / LangChain 全 typed error + cause 链, Flyto 早期字符串 pattern match 是反模式. ADR-0006 § 3 红线: typed code 仅供分类引擎层禁用 auto-fallback, 与 ADR-0005 引擎中性化精神连续. r24 实证完美生效 (改名后 + ADR-0006 fail-loud 通路):[error provider_http_status] API 调用在 1 次尝试后失败: openai_compat: provider error (via openrouter→SiliconFlow): [20015] The reasoning_content in the thinking mode must be passed back to the API— typed code 替代 internal_error / Detail 拼接 / 嵌套 metadata.raw 解出 / 真因暴露完美. r24 真因是 deepseek-v4-flash 协议要求 reasoning_content 在下一轮请求 passback (新发现的 wire bug, 不在 Bug U 范围, 留 TODOwire OpenAI-compat reasoning_content roundtrippingfollow-up). Tracked debt: TD-11 sibling EngineError 合并 (v1.0 评估), TD-12 anthropic api.APIError + wire typed error 合一, TD-13 capability-probe tool name regex 启动期校验, TD-14 lmstudio/ollama/openrouter 等 provider 同款迁移. 测试 -race 全绿*: errors_test.go +5 + openai_test.go +3 + 既有全过. -
2026-04-27/28 ADR-0004 bill-recon 价格模型对齐 WMS ShipCostCfg (4 commit, v0.5.0-alpha.11 cut): alpha.10 review (commit
44b277d修原图显示 + vlm 失败标红 + 双提交 409) 时 PM 拉飞驼内部 WMS 数据字典 (git.flytoex.net/AWS_Team/CloudWM/02_Design/CWM云仓数据库设计说明书.xlsxSheetCWMACCTShipCostCfgMasterL1 +ShipCostCfgL21,CostType=0=StandardCost / 1=AdjustedCost), 指出 alpha.6 12 字段parser.Adjustment(EffectiveDate / Carrier / Regions / AdjustmentType / Details / Summary / TimeDimension) 跟 WMS 5 关键 gap (free-form 文本 vs 结构化BaseWeight + BaseAmount + IncrementWeight + IncrementPrice/Regions []stringvs 一行一省ProvinceName + CityName1:N / 无StandardCostSysNo关联 base 标准成本 /Carrier="圆通"名字 vsShipTypeId / ShipTypeMSN物流 ID / 无LimitTop/Bottom重量段 +VWRate体积重比 +Priority优先级). PM 拍板"马上大修, 完整实现", 3 个决策点拍: ① 派费 =BaseAmount涨, WMS schema 够用不加PerPackagePrice新字段 (PM 指正我 reverse thinking 走偏); ②StandardCostSysNo=0占位等 C7 接 WMS 反查时 resolve; ③ 一图一 master 简单, 用户合并 future. 4 commit chain (合并实施 — schema/parser/store/llm/workflow/web 紧耦合无法分拆 build 通):199fd11C1 docs (ADR-0004 + CHANGELOG + TODO) →045ec70C2 大切 (schema migrationprice_adjustmentsdrop, 新ship_cost_cfg_master + ship_cost_cfg镜像 WMS + parserAdjustment→AdjustmentMaster + AdjustmentDetail + AdjustmentBundle全字段 json tag snake_case 顺手关 alpha.6 marshal mismatch bug + storeSaveShipCostCfg + ListShipCostCfg + CountShipCostCfgMaster+ LLMExtractShipCostCfg+ WMS 字段 prompt +parseShipCostCfgContent+ workflowAdjustmentExtractor新签名 + webbundleConfirmPayload新 wire shape, 1173+/656− 行) →2ce9076C3 review UI 卡片重做 (master 4 字段 + 3 折叠高级 + N 行 detail 子表 9 列 + 加行删行 + style.css grid 布局) →b314a2eC4 bill detail 接ListShipCostCfg(server.go 加adjustmentBundleDTO+ 扩handleGetBill返{ bill, adjustments[], warning? }, app.js 加toggleBillDetailinline 展开 +renderBillDetailmeta + 每 bundle image + master<dl>+ detail<table>9 列, 老 bill 显示占位, 吃掉 alpha.10 中断的 detail-image follow-up). 不落 WMS db — bill-recon 自己造 schema 镜像 WMS, C7 接通后再评估降级 (ADR-0004 § 4.3 + § 7 触发条件登记). cut v0.5.0-alpha.11 (2026-04-28), build-push 3分02秒 + deploy 20秒. PM 浏览器侧待测 alpha.11 review + bill detail 全链路. - 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 顺序:
eec48feC1 SessionStore 接口 (Create/Get/Delete 三方法) + InMemoryStore drop-in 替换 server.sessions map + 4 handler 改造 + TOCTOU race 折叠 (319 行 sessionstore + 改 server.go 182 行);beaff60C2 Postgres 后端 +internal/db/共享池 +--postgres-dsnflag + docker-compose pg + healthcheck + persistent volume + release.yml POSTGRES_PASSWORD secret + testcontainers-go 真 pg 测试 (~510 行 + main.go 53 行 + docker-compose 48 行);96e893aC3 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 顺序:
89c38d3C0 顺手修部署 path bug/v1/* → /api/v1/*(Caddyfilehandle /api/v1/*不剥前缀致 server.go 注册/v1/*经 hub.flytoex.net 全 404, 上一会话 commit 5 没真实经 Caddy 实测, 触发 memoryfeedback_validate_network_path_before_deploy);26bc732C1 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} 首版;bce7670C2 cmd/common --swagger flag + Swagger UI endpoint (httpSwagger v2 包内嵌, side-effect import docs, authMiddleware/rateLimit allow-list 加 /swagger/);22b6388C3 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 产物);a9b04abC4 docs/CONSUMERS.md 顶层 wrapper 133 行 (端口拓扑/env/flag/OIDC auth 流程图含 allow-list/业务 REST 一次性+多轮会话 curl 例子/观测 gRPC SafetyChain 指引/进一步阅读链);bf5af1dC5 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 顺序:
01f08e7C1 server.go 拆 buildHandler/Serve/ListenAndServe-wrapper 让出 signal handling;8189d05C2 Verifier 替代 BearerToken 走 auth.HTTPMiddleware (raw shared-secret + ConstantTimeCompare 删除, dev 模式 Verifier=nil 与 admin 一致);694bd07C3 server.New 接受外部 engine 不再内嵌 anthropic provider 写死, 加 Attach + HandlePermission 两 API;e1db327C4 cmd/common 加--rest-addrflag, 装配 anthropic provider + engine + s.Attach + 第三 listener wire (signal handler 三路协调 grpc.GracefulStop / httpSrv.Shutdown / restCancel, errCh capacity 升 3);c35e761C5 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" 形态, 按 memoryfeedback_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/sqlitetest-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); memoryproject_version_roadmap.mdv0.1 发版事实更新.
ADR-0017 运行时引擎配置 follow-up (2026-06-06)¶
后端已落地 + 测试绿 (engineconfigstore 双实现 + crypto + engineconfig Manager/ConfigurableProvider + 4 端点 + cmd/common 装配; /run provider/key/model 真生效). 剩: - [ ] 🟡 dispatch loop snapshot 默认下沉: quotedispatch sub/main/audit/vision 当前默认仍读启动期 env (QUOTE__MODEL / providerRegistry built once). 改为从 engineconfig snapshot 读默认 (form 覆盖逻辑不动, 优先级 form > snapshot > 503), 让模型路由对 dispatch 也真生效. 见 quotedispatch_handler.go:2138-2245 + engine_factory.go 的 providerRegistry 构造. - [ ] 🟢 settings.tsx 接真端点: API密钥 + 模型路由 + 审计模块从 mock 接 GET/PUT /api/v1/config/engine + PUT/DELETE /config/secrets/{name}, 按 tier (real/persist_only) 三档渲染. - [ ] 🟢 swagger 重生: server_config.go 的 4 个端点有 @swag 注解, 待 swag CLI 重生 docs/swagger.json. - [ ] 🟢 key rotation 迁移工具*: engine_secrets.key_version 列已留, master key 轮换需 re-encrypt 迁移 (ADR-0017 §6).
引擎整体 review (2026-06-15, 8-lane 多 agent + 对抗复核)¶
PM 让整体 review 引擎 (~11 万行非测试 Go). 结论: 核心扎实 (主循环 / 8 provider / 错误模型 / 生命周期都真 wired + 防御性工程到位), 无 critical. 但有 3 条主路径硬伤 + 一条 ~1.5 万行 "脚手架带" (建了测了未接运行时). 下列按价值排, 盯报价流程.
- [x] #1 normalize 不在发送路径跑 (high, 已修 2026-06-15):
NormalizeMessagesForAPIgodoc 写 "发送前清理" 但全仓零调用, 唯一 normalize 在 session restore. 长对话中途 compaction/注入产出的非法序列 (孤立 tool_result 等) 照样发出去 provider 400. 修复: engine.go runLoop 在 maybeCompact 后、BuildAndStream 前用DefaultNormalizePipelineWithObserver(e.observer, e.strictMode)规范化一份副本喂 send (raw history 不动, 每轮一次). regression testTestRunLoop_NormalizesOutboundMessages证明能抓 bug (短路即 FAIL). 全 engine -race 绿. - [x] #2 StreamGuard 只包 Anthropic-compat 路径 (high, 已修 2026-06-15): 断流看门狗 + 截断检测只在 transport(api).Client 路径. OpenAI-compat (wire/openai.go) + gemini Stream 无. 而真报价抽取跑的就是 OpenAI-compat (LAN gemma4) -- 最该保护的路径裸奔. stream_guard.go godoc 早写明 "对所有 provider 通用" 但没兑现. 修复: StreamGuard 从 anthropic 专有
internal/transport(package api) 移到中性包internal/streamguard(只依赖 flyto+stdlib, api 和 wire 都 import 无环), 两个 wire 家族的 Stream 各包一次 -> 6 个 openai-compat provider + gemini 全自动获保护 (范式级, 非 7 处补丁). 第 0 步实测 gemma4 末尾真发 usage chunk -> 截断检测不误报. regressionTestOpenAICompatClient_Stream_GuardedEmptyResponse证明能抓 gap (裸流即行为 FAIL). build ./... + streamguard/wire/transport/8provider/engine 全 -race 绿. - [ ] 🟡 #3 两个打架的 token 估算器 (high, 2026-06-15 深挖后改判: 需实测不能盲改): 压缩触发 (compact.go:1477 ShouldCompact = EstimateTokens > threshold) 用本地文本估算; 警告 (token_budget.go:348 CalculateWarningState) 用 provider UsageEvent 的真实用量. 但这是团队故意的拆分, 非纯 bug: 压缩触发必须在第 1 轮就工作 (那时无任何 UsageEvent), estimate 是唯一普遍可得的信号; 警告给 UI 用真实值. compact.go:194-213 注释自陈 estimate 跟真值差 "2-3 倍是另一层问题", 已把 CJK 系数按 r31 实测 1.5->0.6 缓解, contextWindowProvider 标 "幌子但保留作兜底". 风险: 改 maybeCompact 用真实 token 是改在工作的压缩逻辑 (r31 收敛靠它) -- 估算偏低->改真值更早压缩->丢上下文->抽取变差; 偏高->更晚->overflow 400. 校准后偏哪个方向/多少, 无人实测. 推荐路径 (非盲改): (1) 先插桩, 在真实长对话 (报价 dispatch) 上记录 maybeCompact 处 EstimateTokens vs 上一轮真实 totalInputTokens 的差 (方向+量级); (2) 据实测决定是否把 ShouldCompact 改成 max(estimate, 上一轮真实) 或类似, 让触发不晚于真值 (防 overflow) 同时不显著提前 (防丢上下文); (3) 顺手清 TokenBudgetManager 死 hybrid 估算器 + contextWindowProvider 幌子. engine.go 已有 totalInputTokens (4903) 可喂进 maybeCompact (6305), 接线不难, 难在定方向 -- 故 blocked-on 实测.
- [ ] 🟢 provider 一致性 (medium): reasoning_effort 对 OpenAI o-series 被 openai-compat wire 丢 (只 DeepSeek 映射); ToolNameRegex/ReasoningPassbackMode 6 个 openai-compat provider 只 2 个 plumb 到 wire (openrouter/deepseek). ~~header 感知重试 (Retry-After) 只 Anthropic 路径~~ -> ✅ 已修 (2026-07-17): openai-compat wire Stream 接 retryer + apierror.DefaultClassifier (含 Retry-After 解析), 全家族获 pre-stream HTTP 重试, 见下方缺口 A 条 + CHANGELOG.
- [x] ✅ openai-compat pre-stream HTTP 重试 (缺口 A, 2026-07-17, prod deepseek 裸奔修复): openai-compat 家族 (含 prod 默认 deepseek) 的 5xx/429/529 此前任何一层都不重试 (wire 无 retryer + engine isRetryableError 无 5xx 分支且大小写不匹配 openai wire 小写错误串). 修 (模仿 anthropic, 范式级): 拆 HTTP 分类 (DefaultClassifier+APIError+连接错误诊断) 到中立 apierror 包解循环依赖 (transport import wire), openai wire Stream 用 retryer.Do 包裹 pre-stream 镜像 anthropic transport CreateMessageStream; 5xx/429/529/连接错误经 apierror.DefaultClassifier 分类 + 通用 composite policy 退避重试, 4xx 不重试, 重试耗尽 toEngineError 转回 flyto.EngineError 保 ErrProviderHTTPStatus. 承重测试数真实重试 (503x2->成功/4xx 不重试/耗尽/200-非-SSE 不重试). 追加 HTTP 标准精化 (fmlx 契约咬合): 507 改不可重试 (修反向 bug: 此前落 >= 500 被误重试; fmlx 507=永久装不下) + 5xx 解析 Retry-After (RFC 9110 标准, 尊重 fmlx 的 503 + Retry-After, 惠及 anthropic 路径). 全 core -race 绿. 详见 CHANGELOG.
- [x] ✅ 缺口 B: mid-stream 死字段 ev.Retryable 已接通 (2026-07-17): (a) 原始 fmlx prefill-refused 显存 mid-stream #1 已随 fmlx 根因修消解 (fmlx 内存满改 pre-stream 返 503 + Retry-After + 缺口 A 联合, 不再走 mid-stream). (b) 死字段接通: openai wire consumeSSE 在 mid-stream 429/529 (openai.go:862) 和 scan error/EOF (openai.go:970) 设 ev.Retryable=true, runLoop case flyto.ErrorEvent (engine.go:5288) 此前只认 stream_empty/truncated/idle_timeout 从不读. 现加 ev.Retryable && !hasAnyContentBlock && midStreamRetries
post-loop context-aware 退避重试 (与 partial-stream 同构), clean turn 完成后 reset 计数器 (per-turn 独立预算, 防长对话偶发 blip 累积误判). 幂等守卫 !hasAnyContentBlock: 只在还没吐 token 时重试 (429/529 流刚开始天然满足; scan error 若已吐内容守卫拦下走硬错). 两 gate 验证 (advisor): StreamGuard.Watch 原样透传 ev.Retryable 非 drop (wire TestOpenAICompatClient_MidStreamRetryableSurfaces 实证); openai.go:970 是瞬态 scan IO 错误. 测试数真实重试 (engine mid_stream_retry_test.go: 无内容->重试成功 / 已吐内容->守卫拦下硬错 / 跨轮 reset 两轮各重试). 退避读不了 Retry-After (SSE error chunk 无 HTTP header, 与 pre-stream 不对称, 注释入档). L722 (MiniMax mid-stream EOF) 未合并: 那是已吐内容的 EOF, 需 wire 缓冲/重放 (非平凡), !hasAnyContentBlock 守卫天然排除, L722 仍独立开着. 全 engine -race 绿. 审核补修 (2026-07-18)*: StreamGuard 合成错误 (Retryable=true) 在 partial 预算耗尽后漏进 mid-stream 分支致两预算叠加 (2+2, 持续空流 5 调用非 3), mid-stream 分支现排除 guardSynthetic 三码; 回归测试修复前 FAIL 于 5 次实证. - [ ] 🟢 第一层: deepseek 无损切 anthropic mode 核实 (prod 更优路径): deepseek 官方推荐 anthropic api, 其 ModeAnthropic 走 transport client 有现成完整重试. 切它 = prod 主力最快上稳路 (不必等 openai 重试). 但需先核实无损: (a) reasoning_content passback (r24 真因) 修复只在 openai wire 层, anthropic mode 走 anthropic 协议原生 thinking passback -- 需真测多轮 thinking 请求确认不重蹈 r24 (不能凭代码推断); (b) flow 用没用到 anthropic mode 会 ignore 的 budget_tokens/cache_control/top_k/image/mcp; (c) 静默 model fallback 坑 (送错 id -> v4-flash). 核实通过再切 (配置层 cfg.Mode 可切不改代码). minimax 不是 candidate: 其 anthropic 端点实测不稳 / agent 场景表现较差 (provider 注释), 留 openai + 缺口 A 兜底.
- [ ] 🟢 runLoop 1605 行单函数拆分 (design): engine.go:4093-5698. 天然切缝 = stream-consume / tool-dispatch / retry-classify. engine.go 7448 行本身是合理核心 (内聚 + 双语 godoc), 但这个函数超界.
- [ ] ⚪ "脚手架带" 登记 exit criterion (tracked debt): 下列建了+测了+扫描器过但运行时零非测试消费者 -- 部分属设计意图先行合法债, 但缺 named 消费者 + exit criterion, 读起来像活的. 逐个标 "等谁消费":
- 权限引擎 (~9700 行 pkg/permission): ~~主 agent 工具执行不过
e.perms.Check(主循环零调用), 只 Team Worker 过闸~~ -> ✅ 已接通主循环 (2026-06-16), 见下方 campaign "权限闸" 条 (permGateEnabled守卫 + runLoop 权限闸 pass + 真模型 🟢). 本地 CLI 不沙盒是故意的 (对齐 Claude Code/Cursor, 见 memory project_sandbox_local_vs_cloud), 现 "本地默认放行 (无 handler 直通) / 云端真拦 (有 handler)" 已兑现. 另 (未做): 无 builtin 工具 set RequiresCheckpoint, 默认 Bash 跑任意命令无引擎级确认 -- 权限闸接通后高危 Bash 走 Check (ModeDefault 下 Ask handler), 但 RequiresCheckpoint 这条独立强制确认通道仍空.- 2026-06-15 真模型 harness 实证 (cmd/runtime-probe toolgate): 上面是 code-review 推断, 现已用真模型坐实. 构造 engine (PermissionMode=Default + deny-all PermissionHandler + 1 个自定义危险工具), gemma4-e2b 与 e4b 真打两次, 结果一致: 工具 Execute 真跑, handler.Handle 零调用, 无 PermissionRequestEvent. blast radius = 主循环里 builtin 与自定义工具皆不过闸 (builtin 只在 Metadata 声明 PermissionClass, 主循环不读它调 Check). 误导点: dogfood
tui/cmd/flyto设了PermissionHandler但被主循环静默丢弃 = 死配置. 笔记不一致 (待产品定调本地拦不拦): sandbox memory 说本地靠 "ApprovalFunc 把关高危" (=该拦) vs 本条说 "本地不沙盒故意"; session.go:431 注释称 "runLoop 在需要权限时调 WaitForPermission" 但该调用在非测试代码零存在 (注释承诺代码没做).
- 2026-06-15 真模型 harness 实证 (cmd/runtime-probe toolgate): 上面是 code-review 推断, 现已用真模型坐实. 构造 engine (PermissionMode=Default + deny-all PermissionHandler + 1 个自定义危险工具), gemma4-e2b 与 e4b 真打两次, 结果一致: 工具 Execute 真跑, handler.Handle 零调用, 无 PermissionRequestEvent. blast radius = 主循环里 builtin 与自定义工具皆不过闸 (builtin 只在 Metadata 声明 PermissionClass, 主循环不读它调 Check). 误导点: dogfood
- evolve 子系统 (4756 行): 零生产消费者. 符合 v1.x 路线 (memory project_evolve_rl_stance), 故意提前落地非 bug, 但该 bound.
- daemon + bridge + websocket (SSE/WS 最后一公里, ~3k 行): 建全测全无 server 实例化. 自闭合死岛.
- staging / shadowdb / reflector: 全仓零非测试消费者 (已知, 等平台 SQL 后端). 已有 tracked debt 登记 (上文 2026-04-24/25 段).
真模型逐功能验证 campaign (2026-06-15, PM 驱动)¶
PM 质疑 "跑通报价 = 引擎没问题": 报价只用引擎 ~16/38 功能, 且一堆功能 silent 坏 (不中断), 报价照样绿 = 假安心. 逐个功能真模型 (本地 fmlx gemma4 free + MiniMax) 单独打开看输出对真值, 非看崩没崩. harness: cmd/runtime-probe (引擎运行时行为) + 复用 cmd/schema-probe / cmd/capability-probe (provider 能力). 盲区图原始: tmp .../wjdm7wl76.output.
- [x] 结构化输出 (报价依赖): schema-probe 真打 gemma4-e2b, 10 个 JSON Schema 特性 (enum/anyOf/$ref/深嵌套/数值范围 ...) API 全接受 + 工具触发 + 值逐条对真. ✓ 真生效. 顺验 #2 StreamGuard 在真路径没破结构化.
- [x] 缓存 + reasoning 回传 (报价依赖): capability-probe 真探. MiniMax-M2.7-highspeed 第二次同请求 cache_read=1118 真命中 ✓; reasoning_passback_mode=none (2 往返真测, 不回传被接受) -> r24 那类卡死在 MiniMax 上不发生. (注: PM 已弃 2.7 用 3.0, MiniMax 非今后主流; 验到的是引擎那两条路本身能用, 与具体模型解耦.)
- [x] 权限闸 (产品要用, 报价不碰): 🔴 缺口 -> ✅ 已接通 (2026-06-16, PM 拍 Q1=接通). runtime-probe toolgate 真打 gemma4-e2b/e4b 两次, deny-all + Default 全没拦, 工具照跑 handler 零调用. 修复 (范式级, 镜像 SubAgent 闸): Engine 加
permGateEnabled(构造期算 =配了 handler || 显式非 bypass mode; 无 handler + 空 mode = 本地默认直通零开销, 有 handler = 云端真拦) + runLoop 在 hook 闸后 / checkpoint 闸前新增权限闸 pass (发 PermissionRequestEvent 观测 ->e.perms.Check-> 非 Allow 剔除并返 IsError tool_result -> Allow 带 UpdatedInput 重写输入, 镜像 SubAgent -> 全拒同 hook/checkpoint 全拒路径). builtin + 自定义工具共用 registry 汇入 ExecuteBatch, 一处 inline 闸全覆盖. 判据取舍: 仅判 handler 存在会让 "Default mode 无 handler" 静默直通 (同类漏洞), 故额外覆盖显式 enforcing mode fail-closed; grep 坐实 core 无 test 在主引擎设 PermissionMode -> 零回归. tui 零改动 (已设 handler / 无 mode -> 闸自动开, 死配置复活). platform/common 自动激活云端拦截. 验证:permission_gate_wire_test.go(deny 拦工具 + handler 真咨询 / 无 handler 直通负对照 / UpdatedInput 重写) + 真模型 harness 复跑 🔴->🟢 (gemma4-e2b: 工具执行 false / handler 咨询 true / PermReqEvent true). 全 engine -race 绿. 详见上方 "脚手架带 / 权限引擎" 条. - [x] 工具执行回路 (产品要用, 报价不碰): 🟢 机械通. 引擎真执行注册工具 + 结果喂回. (gemma4 多轮工具循环末尾吐
<eos>乱码 = 本地 fmlx 模型/模板毛病, 非引擎; 单记一笔, 疑与 #2 同族.) - [x] 记忆抽取 turn 边界静默子 agent (产品要用, 报价不碰): 🔴 silent 失败 -> ✅ 端到端打通 🟢 (2026-06-16, 三 commit 1a/1c/1b). 三层缺陷链, 逐层真模型诊断剥出 (feedback_diagnose_from_specific_session_data / feedback_verify_error_source_before_theory, 险些误判为模型限制):
- item 1a 静默机制 (
e7f4b18): runMemoryExtractionfor range events {}吞含 ErrorEvent 整条流 +defer Event("complete", nil)无条件报喜 + cursor 无条件推进. 修: drain 捕获 ErrorEvent->extractErr + 数 Write/Edit->wroteCount + tool_calls 计数; complete payload 诚实化 + 失败发memory_extraction_failed; cursor 仅成功推进; List err 不再静默; 兜底加锁读 sa.Error 抓 panic. regressionmemory_extraction_silentbug_test.go. 诊断价值: 修后 harness 无 failed 事件 -> 排除"引擎吞错". - item 1c 真根因 -- 子 agent 漏发 Tools (
807f511, 重大):SubAgent.runLoop的 flyto.Request 漏 Tools 字段 (注释吹传 allToolDefs 但赋值不存在) -> 所有子 agent 发模型零工具 -> 引不出工具调用. 修:flytoReq.Tools = apiToolDefsToFlyto(sa.allToolDefs). regressionsubagent_tools_wire_test.go(请求探测, 撤修即 FAIL). 这才是 0 文件主因. - item 1b 精简系统提示 (本 commit): 抽取子 agent 没传 SharedSystemPromptBytes -> fallback 22KB 父编程 bundle (嫌疑①坐实) + BuildPrompt 没带记忆目录绝对路径. 修:
buildMemoryExtractionSystemPrompt(memDir)经 SharedSystemPromptBytes 注入 (引擎拥有通用 frame, scenario 抽什么仍归 extractor). 替代 (扩 MemoryExtractor.SystemPrompt) YAGNI 暂缓. - 🟢 坐实: harness
--check memory+ gemma4-moe-26b-a4b-q4 (4B active MoE) -> tool_calls:1 wrote:1,production-deploy-host.md落盘含 "HK-133 ... 45.145.229.197" factFound=true. (dense-12b 路径打错被拒 / e2b 2b 把规则当记忆 = 本地小模型能力, 非引擎; 4B MoE 起 🟢.)
- item 1a 静默机制 (
- [x] Wave-1 全量扫描 (workflow 13 功能 + 完整性 critic, 2026-06-15): 引擎包 (非 provider/wire) 13 功能逐个代码层扫. 全 fix sketch 在 workflow 输出
tmp .../wv3bwybhg.output. 分桶 (= 下个会话逐个修的清单):
桶 A -- 真静默 bug (修正确性, 无需产品决策):
- [x] 🔴 压缩接受乱码摘要 (HIGH, wired+silent, 已修 2026-06-15): compactFull 只查 summary=="" (实际在 pkg/context/compact.go, 套 buildFallbackSummary), 非空但语义错/幻觉的摘要原样接受、永久替换真历史, 零校验零报错. StrictMode.CheckCompactFailure 是死的 (maybeCompact 从不 Check). 已修 (范式级): 新增引擎层 validateCompactSummary (engine.go) 内在质量闸 -- 长度 floor 20 / 乱码比 0.30 / 退化重复比 0.05 (复用 detectThinkingLoop 的 unique-rune-ratio 思路), 仅 >200 字符跑重复检查. 故意不做 sketch 里的"必保留 token 抽样核对" (锚点重叠): 易误报会误杀合法激进摘要 -> 破坏 r31 收敛 (TODO #3 已警告压缩路径 load-bearing), 闸只抓明显损坏输出. maybeCompact + forceCompact 两路成功后过闸 (sketch 只提 maybeCompact, forceCompact 有同款盲接, 一并修) -> 不过则 wire StrictMode.CheckCompactFailure (eval panic / 生产记 observer) + 回落已有 micro fallback. 顺手抽 emitMicroFallback helper 消除 4 处近似回落块. regression compact_summary_validation_test.go (表测 + 集成 坏摘要->Kind=micro 垃圾不落地 + strict panic + strict 合法不误报). 全 engine -race 绿 + core build 过.
- [x] thinking 死循环检测有死区 (MEDIUM, silent, 已修 2026-06-16): wire 层 reasoningBuf 仅 FinishReason!=nil 才 flush (openai/gemini; anthropic block-based 仅 content_block_stop), 异常断流 looped thinking 被丢 -> 引擎 detectThinkingLoop 拿不到, r15 loop-to-timeout case 整个绕过 (ErrorEvent 硬错 return / post-loop 空流盲重试都绕过守卫). 已修 (engine-only, 偏离 sketch 的 3-wire-patch): 三家断流前都已增量发 ThinkingDeltaEvent, 引擎累积 thinkingDeltaBuf 即可拿到 looping thinking (provider 无关, wire 不动). 抽 detectAndCorrectThinkingLoop helper (warning + 注入纠偏 + caller 决定重试计数), 正常路径 (5548) + ErrorEvent 硬错路径 + post-loop 空流分支共用; 检测到 loop -> 纠偏 + 重试 (计入 turn, MaxTurns 兜底). 已知边界 (注释): "先出文本再 loop" (hasAnyContentBlock=true) v1 不覆盖. 检测算法 (数花样 unique-rune-ratio) 不动 -- "找重复节拍"抓松散循环的升级 PM 拍 follow-up 再议 (跟 item 3 同族, 当前是 char-based 数花样, 对紧凑循环准、对车轱辘式松散循环漏). regression thinking_loop_abnormal_exit_test.go (ErrorEvent/空流两 shape 自纠 + 健康 thinking 负对照). 全 engine -race 绿.
- [x] 重复块检测漏近似 (MEDIUM, silent, 已修 2026-06-16): detectDuplicateTextBlocks 用 TrimSpace 精确相等 map key, 近似/whitespace 差异/JSON 重序列化/跨 thinking-text 通道重复全漏. 已修 (保留精确 fast-path + 叠加模糊层): 归一化 normalizeForDupCompare 用 strings.Fields 压所有空白成单空格 (抓"只差排版"重复); 精确 fast-path 在归一化形式 map exact-match; 模糊层仅对 ≥64 rune 块两两算字符 8-gram Jaccard (≥0.85 且长度比 ≥0.70). 偏离 sketch 的判断: 不碰跨通道 thinking↔text 重复 (模型先想再答内容本相近会误伤, 且 thinking 不进 final text, 归 detectThinkingLoop 管); JSON 重排只 PARTIAL (靠共享 shingle, 不建 schema 假设的规范化器). regression duplicate_block_fuzzy_test.go (空白变体/长近似必抓 + 长不同/短近似不误报 + normalize/jaccard 单测), 现存 5 exact 测试不回归. 全 engine -race 绿. follow-up (B, PM 拍缓): thinking 死循环检测从 char-based 数花样升级"找重复节拍"抓松散循环 -- 跟本 item 同族 (近似重复), 可复用 normalize/shingle 基建.
桶 B -- 假成功诚实化 (低风险快修, 杀 #1 病根):
- [x] ✅ 子 agent 死执行器假成功 (HIGH, dead_code, 已接通 2026-06-16 PM Q2=接通): AgentTool 注册进默认 registry 但 executor 没人接 (SetupAgentExecutor 零调用), Execute 回落 fallbackResult 返 IsError:false "Sub-agent execution is not available" -> 模型当成功继续, 子 agent 工作凭空消失. 修 (真 wire, 非只诚实化): (1) New 顶层建共享 taskStore 穿进 buildToolRegistry/registerBuiltinTools (Task 工具与 Agent 执行器共用一 store -> 后台任务进 TaskList); (2) New 装配后调 SetupAgentExecutor -> Agent 工具真 fork+跑子 agent (Agent 工具未注册时 no-op 安全, cfg.Executor 保证非 nil); (3) fallbackResult IsError false->true (诚实化防误配). 叠加 item 1c Tools 修复 -> spawn 的子 agent 真发工具 -> 端到端可用. test agent_executor_wire_test.go (撤 wire 即 FAIL) + agent_test.go (fallback IsError:true). 全 engine+builtin Agent/Task -race 绿.
- [x] ✅ Skill 工具死执行器假成功 + SetupSkillTool 零调用 (dead_code + 假成功, 已接通 2026-06-16 PM 拍接通; 同时关掉桶 C "Skills 文件加载"): SkillTool 注册进默认 registry 但 SetupSkillTool (唯一绑 executor + 加载文件 skill 处) 零调用 -> executor 恒 nil -> Execute 回落 fallback 返 "executor not configured" 且 IsError 未设 (假成功, 模型当 skill 跑过而工作流静默蒸发) + 文件 skill 从不加载 (registry 永空). 修 (真 wire, 镜像 AgentTool): (1) skill.go nil-executor fallback IsError->true (诚实化, 病根 #1); (2) engine.New skillRegistry setter 块后接线, 刻意拆两件事: 抽 bindSkillExecutor(eng) (tool-gated, Skill 未注册时 no-op, 永远绑) + 宿主 FS 目录扫描由新 cfg.DisableSkillFilesystemScan 门控 (默认 false=扫, SaaS 多租户设 true 防读宿主 skill 文件, 同 DisableDream/DisableCalibrator 跨租户 FS 隐患). 被否替代* (注释存): 整个 SetupSkillTool 一刀切门控 (像 DisableDream) -> 会连 executor 绑定一起丢, 对注册 Skill+代码 skill 的 SaaS 消费方重引入假成功; 拆开后工具任何配置下都诚实, 只宿主 FS 读被门控. test skill_executor_wire_test.go (撤 wire 即 FAIL 实测确认 + DisableSkillFilesystemScan=true 仍绑 executor 锁拆分设计) + skill_test.go TestSkillTool_Execute_NoExecutor 翻 IsError:true. 全 engine + builtin Skill -race 绿 (除本机 TMPDIR symlink baseline flaky TestFileEdit_SymlinkPassthrough, 与本改无关).
桶 C -- 死代码 (产品要用前必须接, 需产品决策: 接通 vs 诚实标 dormant+exit criterion):
- [x] ✅ Team 接通 = 模型可调并行小队工具 (dead_code, 已接通 2026-06-16 PM 拍接通 + 两要求): 5 个协调工具 (send_message + shared_task_) 注册了但 NewTeam/RunWorkers 零调用 -> 永久 "not in a Team" 空转, 并行 worker 机器全建好却没有创建入口, 模型无从消费 (诊断纠偏: 先误判 "待场景别造假消费者", PM 指正 = 没暴露入口当然没人消费, 同 Agent/Skill dead-code). 修 (新模型可调工具, 镜像 Agent 执行器): builtin TeamTool + TeamExecutor 接口 (依赖倒置) + nil-executor IsError:true; engine teamExecutor.RunTeam 当前引擎为 Leader 直构 Team struct (绕开 NewTeam 对活引擎变异) + 新 router (worker<->worker send_message + 可选内存任务板); SetupTeamTool 接线 (tool-gated no-op). 架构修正: worker 权限不走冒泡 (同步工具路径 Leader 阻塞在 Execute 无法应答冒泡 -> 死锁), 改父引擎 pe.perms 同步施加 (仅 permGateEnabled, 否则 nil 对齐本地不沙盒); runWorker 重构接 checkerFor, RunWorkers 传冒泡 (不变) + 新增 exported RunWorkersSync (同步扇出不冒泡不注入通知). PM 两要求: 模型自主=默认注册; 可临时屏蔽两级=权限闸 deny "Team" (运行时) + cfg.DisableTeamTool (静态不注册); 消费者可调=NewTeam/RunWorkers/RunWorkersSync 全 exported 不动. 防递归: worker 拿不到 Team 工具 (runWorker 删 allowedMap["Team"]). test team_executor_wire_test.go (撤 wire 实测 FAIL provider streamed 0 + DisableTeamTool 工具缺席). 全 engine -race 绿 (含 team_test.go RunWorkers 回归) + core build 过.
- follow-up: 模型驱动的嵌套小队 (worker 再开 Team) 当前禁掉防无界爆炸; 要放开需 spawn 深度预算 (参 L903 skill spawn 深度), 等真需求. 消费者 Go API (NewTeam) 仍可刻意嵌套.
- [x] ✅ Plan 模式接通 + 修底层 enforcement bug (dead_code + 潜伏 bug, 已完整修通 2026-06-16 PM 拍 A): 不是"建好没暴露"而是"接了也跑不起来" -- PlanModeManager 零调用 + 三工具从不注册 (模型进不去) + ModePlan 在 CheckToolPermission 一刀切拒绝所有工具含只读 (与 EnterPlanMode "用 Glob/Grep/Read 探索+写计划" 指令矛盾, 进去就卡死) + 主循环闸只在 permGateEnabled 时跑 (本地默认 ModePlan 不强制). 修 (完整修通): (a) checker.go 新增 PermClassPlanWrite, readonly+planwrite 提到 plan 判定前放行, plan 模式只拒改写/Bash/web/generic; (b) 新 WritePlan 工具写 PlanStore (对齐防注入原设计) + EnterPlanMode/ExitPlanMode 补 Metadata(PermClassPlanWrite) 使其 plan 模式放行; (c) 闸条件加 e.planModeActive() 使 plan 模式 enforcement 独立于 permGate; (d) engine.New 建 PlanModeManager 绑 eng.perms + 默认 MemoryPlanStore/NoopApproval (消费者经 cfg.PlanStore/PlanApprovalPolicy 注入) + 三工具经 registerBuiltinTools 注册 (受 cfg.Tools 过滤 + cfg.Toolset 中性化排除) + setupPlanModeTools 延迟注入 manager (镜像 SetupAgentExecutor, nil-manager Execute 诚实失败); (e) cfg.DisablePlanMode 静态屏蔽 + 权限闸运行时屏蔽 + Engine.PlanModeManager() accessor. test plan_mode_wire_test.go (端到端 Enter->Write->Exit + runLoop 改写拒/只读放行 + DisablePlanMode 缺席; 撤闸条件实测 danger_run 执行 FAIL, 撤 checker 放行实测 read_safe 不执行 FAIL) + checker_test.go 扩断言. 全 permission+engine -race 绿 + core build 过.
- [x] ✅ Plan 队列 + 进度接通 = 步进执行器 (PlanProgress 缺的消费者) + REST 端点 + 真跑修 run-model 注入 gap (dead_code, 已接通 2026-06-17 PM 拍 "全链+异步队列" + "永远别用没消费者搪塞" + 真跑闭环): EnablePlanQueue 全仓无 true, NewPlanProgress/AttachProgress 零非测试调用者 -- PlanQueue (UDS 异步队列) + PlanProgress (5 态步骤状态机 + Kahn 拓扑序 ReadySteps + 失败连带跳过) 机器全建好, 缺的是中间那个消费者: 按 PlanStep 步进驱动 PlanProgress 的执行器整仓不存在 (不是"建好没接线", 是"缺中间件"). 修 (造缺失消费者, 让审批产出步骤->异步队列->步进执行+进度合成一条流程): (1) core plan_executor.go 新 runPlanStepsWith (独立 Engine 可 stub 单测, runStep 注入) 按 Snapshot().ReadySteps() Kahn 拓扑序逐步驱动 PlanProgress (StartStep/FinishStep/SkipDependents), 每步一次 e.Run; runOnePlanStep 单步; normalizePlanStepIDs 防空/重复 ID 折叠 map. (2) 升级队列进度回调全 5 态 PlanExecFunc onStepDone(stepID,err) (仅 done/failed 两态) -> onStep(stepID, StepExecStatus, errMsg) -- 否则轮询看不到 running 中间态也表达不了 skipped (接了等于没接通 PlanProgress); executePlan 回调 + engine.go initPlanQueue execFunc 改用执行器 (原合并 prompt 方案留注释). (3) platform plan_handler.go 4 REST 端点 (submit/status/list/cancel 直连新 Engine.PlanQueue() accessor, 避 typed-nil) + registerRoutes (queue 关时 503 非 404) + cmd/common 默认开 EnablePlanQueue (FLYTO_DISABLE_PLAN_QUEUE 关) + PlanQueueDir 默认 /tmp/flyto-plans. (4) 真跑暴露并修真 gap: 队列 e.Run 用引擎冻结 Config.Model, 与 ADR-0017 settings 热换的 provider 失同步 (staging RunProvider 已切 deepseek 而队列发 gemma4 -> "supported: deepseek-v4-pro/flash, but you passed gemma4"); handleAgentRun 靠 WithModel(snapshot.RunModel) 同步 model 与 provider, 队列没有. 修 = core 加 Config.DefaultRunModelFunc resolver 注入点 + runOnePlanStep WithModel(resolver()), platform 接 engineCfgMgr.Current().RunModel() 使排队 Run 与 /run handler 同源 backend. 真跑闭环 (feedback_verify_against_truth, 对照落盘 step_statuses 非 LLM 自评): staging REST 真提交 3 步带依赖计划 (calc-2/3 dep calc-1) -> 轮询实测全程 pending->running->done (running 中间态可见, 旧合并 prompt 看不到) + 拓扑序遵守 (calc-1 done 才 calc-2/3 running) + 10.8s 全 done; 修 gap 前同样提交实测 calc-1 1.9s failed + calc-2/3 skipped (失败连带跳过也真跑验证). test plan_executor_test.go (拓扑序/失败跳过/进度投影含 running/stuck/取消/ID 规范化, stub 单步不跑 LLM) + plan_handler_test.go (503/往返/404/400). 全 engine + platform server -race 绿 + linux/arm64 交叉编译 + staging force-recreate 部署. dormant 判断结案: 真跑反推出真消费者 (步进执行器), 非纸面 dormant -- 真跑暴露 model 注入 gap 证明它在真链路确实需被正确接通才能用. follow-up: ~~审批弧自动喂队列~~ ✅ 已做 (2026-06-17 PM 拍做 + 直觉纠偏"这是引擎能力非平台补丁": 异步 opt-in WithPlanAutoQueue() RunOption, ExitPlanMode 审批通过经 tools.Result.Data 信号 runLoop 提交队列 + emit PlanQueuedEvent + goto sessionEnd 让模型停手避免双重执行, tool_result 先 append 保会话历史完整; platform auto_queue_plan 开关 + plan_queued SSE case; 承重单测证模型不跑第二遍 + staging 真跑头尾, 见 CHANGELOG); ~~步进执行器并行 ready steps~~ ✅ 已做 (2026-06-17 PM 拍剩下新对话做, 4 follow-up 全清): 先 -race spike 验证 gating 经验问题 -- 同一 Engine 并发 Run 不安全 (主 runLoop SetMessageID 共享文件工具 data race), 但 fork sub-agent runLoop 不调 SetMessageID 故并发 worker race-free (spike plan_parallel_spike_test.go N=8 真共享 FileWriteTool 并发写 -race 50+ 次绿). 修 = wave-based opt-in 默认关: runPlanStepsParallelWith 拓扑驱动器每轮取整个 ready 集合 (wave) 经 runPlanWave fork 隔离 sub-agent 并发跑 (WaitGroup barrier 镜像 Team.RunWorkersSync), 用共享 session 历史快照 seed (仍串联前面 wave 上下文) + barrier 后确定顺序 applyTurn merge 回去 (与 #1 组合非互斥); per-plan opt-in 全消费者贯通 (PlanSubmitOptions/QueuedPlan/REST parallel); 文件冲突 godoc 契约. advisor 纠偏 "Team 生产跑并发 worker 不是安全证据" (脚本 provider 测试 race 没被 -race 抓过) + 判别 spike 当发版闸. 全 engine + platform server -race 绿 (25x -cpu=8 加压). ~~新 follow-up (预存非本次引入)~~ ✅ 已做 (2026-06-17 PM 直接修, 不留债): 主 runLoop 经 e.tools.All() 对共享 FileEdit/FileWrite 实例调 SetMessageID 写 t.messageID, 工具 Execute 读同字段; 一个 engine 跑并发 Run (平台单 engine 跨 /agent/run + 队列, 或 #2 并行 worker) 该写与另一 Run 的读重叠 = data race + 破坏快照打键. 修 = per-call context 透传 (非加锁打补丁, 加锁只是"安全地错"仍 clobber): tools 包加 WithMessageID/MessageIDFromContext (私有 key 镜像 eventEmitterKey, engine 设/builtin 读无环); runLoop 移除 SetMessageID 写改 toolCtx 注入; FileEdit/FileWrite Execute 优先读 ctx fallback 字段 (SetMessageID 留向后兼容). 判别测试 TestConcurrentRun_FileWrites_NoMessageIDRace (N=8 并发 Run 各写不同文件) stash 修复 PRE-fix 实测 DATA RACE on SetMessageID + FAIL, POST-fix 绿 (25x -cpu=8); 正确性 gate TestEngineRollback_RestoresFileEndToEnd 证值经 ctx 仍流动 key 快照. 见 CHANGELOG; ~~跨步会话上下文串联~~ ✅ 已做 (2026-06-17 PM 拍剩下新对话做: runPlanSteps 每 plan 建 throwaway newSession(planID, e) 不入 sessionState map 防泄漏, runOnePlanStep 改 Engine-independent 自由函数 e.Run->sess.Send 串联历史, run-model 每步经 resolver 解析保 ADR-0017 热换; 单测 dump 真透传 messages 断言第 2 步含第 1 步 prompt+reply 非 LLM 自评, 见 CHANGELOG); ~~QueuedPlan per-step 错误字段~~ ✅ 已做 (2026-06-17 PM 拍先做最廉价: QueuedPlan.StepErrors map[string]string + executePlan onStep failed 态持久化 errMsg / running 态 delete 清 stale 防 RecoverPending 重跑残留 + handler 经 omitempty 自动透出; 单测 + handler fixture 测 + staging 真跑 timeout_secs:2 杠杆造出 per-step failed 实证 step_errors 落盘透出, 见 CHANGELOG).
- [x] ✅ 反事实/reverse-think 接通 = ExitPlanMode 触发自审唱反调 (dead_code, 已接通 2026-06-16 PM 拍接入 + 真跑闭环): reference hook (ReverseThinkingHook) + reverse_think.Client + counterfactual.Deliverable 全建好但完全悬空 -- 从没实例化, platform 请求路径零注册, 且全仓零工具标 RequiresReverseThinking (接了也不触发). altitude: ADR-0001 否决引擎级强制, 走平台 hook 层 (PM 拍), 0 引擎核心改动. 修 (三 commit): (1) 0bcbd65 core ExitPlanMode.Metadata 标 RequiresReverseThinking (决策确认时刻=反事实检查点, 低频不撞吞吐, 与审批闸互补; 不标 Bash/FileWrite 高频; SQLCASTool 排除 -- 通用 /api/v1/agent/run 路径默认 builtin 集不挂它); (2) c1d9600 core counterfactual verdict 协议修复 (真跑照出: 模型对第三态返回 depends_on_<具体维度> 而非字面 depends_on_X; Validate 本就 schema-agnostic 接受, 真 gap = 没分类 API; 加 Verdict.Kind 前缀收敛族 + DependsDimension 提维度 + prompt 说清占位符; 不碰 Validate 宽容); (3) 2ab60a1 platform 装配层接通 (cmd/common 构造 Client+Hook 注册 session 级 hooks.Manager, key 缺失优雅关; server.go reverseHooks 字段 + AttachReverseThinking + handleAgentRun WithSessionHooks; Sink 按 Kind 分类 log 给 Kind/DependsDimension 提供生产 reader; engine 无公开引擎级 hook 注册口, WithSessionHooks 唯一不改 core 公开路径). 真跑闭环 (feedback_verify_against_truth, 不用 fake -- PM 指示): reverse_thinking_live_test.go (build tag live + key 双闸默认 CI 不烧 token) 真 MiniMax 真跑, 真 engine registry 查 ExitPlanMode flag + Manager.Execute(PreToolUse) 驱动 + Sink 捕获 + Kind 非 Unknown 真值断言; 实跑两轮均 depends_on_<维度> (engine_requirements / consistency_requirements) Kind 正确归类, 反向论证实打实挑 WithSessionHooks 方案刺 (per-request 开销 / 跨 session 不一致 / 全局 enforcement 无法表达). 全 counterfactual+reverse_think+engine+server+safetychain -race 绿. follow-up (非本次)*: base docker-compose.yml 注入 MINIMAX_TOKEN_PLAN_KEY (prod; m2max staging override 已有); Sink 接 staging.Record.Metadata + evolve.LogReplayer (通用路径无 Record, quote-dispatch 路径才有); 行业自定义 PromptBuilder 注入领域上下文.
- [x] ~~Skills 文件加载 (SetupSkillTool 零调用 -> registry 永空)~~ -> ✅ 已接通 (2026-06-16), 见上方桶 B Skill 条 (engine.New 接 SetupSkillTool, 宿主 FS 扫描默认开 / cfg.DisableSkillFilesystemScan 关).
桶 D -- 故意 dormant (设计如此, 非 bug, 至多补文档/exit criterion): - MCP / 校准器 / hardcoded checkpoint / builtin agent defs: ADR-0005 中性化, engine_factory.go 强制 Disable=true + tools.None(). 非 bug. - Dream: 报价流程 DisableDream:true (ADR-0005 Bug N 多租户 FS 隔离); 开时失败 fail-open 进 NoopObserver (TUI 该接真 Observer). - 记忆相关注入: base 注入真跑 (TextScorer 默认, 纠盲区图错: 走 RoleUser 非 system, LiteralSystemPrompt 不绕过它); Freshness/Scorer/TypeRegistry/Sync 旋钮 nil-by-default 零消费者; TypeRegistry.FormatForPrompt 零调用者. - ResponseReflector 引擎闸: ADR-0008 v3 搬 dispatch loop, 引擎 nil 是对的. 残留风险=dispatch reflector 误 APPROVE 无独立真值核对. - Hooks: 正确 opt-in. Reminders: 默认开* (纠盲区图"默认关"的错).
跨功能病根 (critic 归纳, 修病根 > 逐个补洞): 1. 假成功 fallback (IsError:false on no-op): AgentTool / Skill 工具 2. Setup 函数零调用 -> 静默休眠: SetupAgentExecutor / SetupSkillTool / SetMessageID / AttachProgress 3. 异步 fire-and-forget 错误只进 observer (常 NoopObserver = 黑洞): memory-extraction / Dream / plan-queue 4. 背压下 select+default 静默丢消息: 父 forward (engine.go:5276) / inbox 5. 模型输出无校验直信: 压缩摘要 / 结构化输出 / 去重 6. 安全兜底死的: StrictMode 存了从不 Check (除 tool-result pairing)
critic 揪出的引擎级补扫项 (wave-1 未深挖, 修阶段先确认):
- [x] file_history 回滚死键 (修真静默 bug, wire 层, 2026-06-16): SetMessageID 零非测试调用者 -> runLoop 不传每轮 id -> 快照键恒 "" 而 OperationLog 键 turn-N -> Engine.Rollback 物理恢复静默跳过. 修: 抽 turnMessageID 共享 helper (两键空间同源) + 新 tools.MessageIDAware 接口 (镜像 SocketAware, ADR-0005), runLoop turnCount++ 后遍历 e.tools.All() 断言 SetMessageID(turnMessageID) (不硬编工具名) + 修 Rollback godoc "倒序" 假声明改按路径排序确定性恢复. regression file_history_rollback_wire_test.go 用 CanRollback 证键=turn-1 (wire 层, 不触发下条 clobber). 全 engine -race 绿.
- 缺口登记 (exit criterion): 全仓无生产代码触发 Engine.Rollback. "撤销在哪触发" (界面 undo / 校验或熔断失败自动回滚) 是产品功能决策, 待 PM 定; 文件历史每轮已正确备份 (键=turnMessageID), 接 SetMessageID 而暂无触发点是刻意 "存储层正确/消费者待定", 非掩盖缺口的 formal-wire (见 engine.go Engine.Rollback godoc).
- [x] 🆕 operationLog Write/Edit undo 占位串 clobber (修阶段新发现 + 已修 2026-06-16): 引擎执行工具走 orchestrator.ExecuteBatch -> 为 Reversible 工具 (Write/Edit) 调 GenerateUndo 填 result.UndoInfo. 但 Write/Edit 的 GenerateUndo 把 undo content 写成字面占位串 [restored from file history backup] (本意 "FileHistory + OperationLog 协同", 但 ExecuteUndo 只是裸 tool.Execute, 协同从未实现). Engine.Rollback 第 1 步 file_history 还原真内容后, 第 2 步 operationLog.RollbackMessage -> ExecuteUndo -> 重跑 Write(占位串) -> 把文件覆盖成垃圾. 即便死键修好端到端 Rollback 仍坏 (比没还原更坏). 修: Write/Edit GenerateUndo 改返 nil (file 操作撤销是物理, 归 file_history 第 1 步, 不进 Saga 重执行第 2 步; 工具仍 Reversible:true 经 file_history 可撤销; 撤销可见性经 FileHistoryView.CanRollback 暴露). regression TestEngineRollback_RestoresFileEndToEnd (engine 级真 run loop -> Rollback -> 断言原字节, 两缺陷任一在则 FAIL) + 两个 GenerateUndo 单测改断言 nil. 全 engine + tools/builtin -race 绿. (与上条同属 "撤销文件编辑" 一条坏路径的两个根因.)
- [x] 出站规范化误删 (核查结论: 非 bug, 注释标清, 2026-06-16): commit 1c7b914 的 9-normalizer pipeline 在 runLoop 每轮跑出站消息. 4-agent 调查 + 亲核坐实 EmptyMessageFilter (判定式块类型感知, default 分支保留任何非文本块, 只持空字符串 text 块算空, TestEmptyMessageFilter_KeepsToolBlocks 锁) 与 OrphanThinkingFilter (只删 100% thinking 的 assistant; load-bearing thinking 总与其 tool_use 同处一条消息 engine.go:5059 -> isThinkingOnly false -> 保留) 都删不到 load-bearing 消息. 处置: 给两 filter 各加不变量注释防回归, 零逻辑改动. 全 engine -race 绿.
- Config.Fallback 无 failover setter / MaxBudgetUSD 无 enforce / StrictMode 无 setter.
- Plugin host 磁盘发现死: Host.LoadAll 无 engine 调用者 -> 丢盘上 plugin 静默无效.
延后 (PM 拍: provider/wire 层另算战场, 本轮不扫): reasoning 回传硬编码表错一格静默 400 / openai 路径 EnableSystemCaching 没开缓存白烧 / 结构化输出后端假接受真不强制 / 工具调用空 map fallback 静默错执行. critic 标为最大未扫漏洞, 留下一战场.
发布跑道清账计划 (PM 2026-07-12 拍 "越晚弄发布越麻烦", 按契约破坏面倒序排)¶
原则: 动公开契约的先做 -- 消费者 (flytoCall 起步, 后续更多 vertical) 每多接一家, 改契约的代价翻倍. 纯增量能力 (新 flow / 新工具) 不破坏契约, 可以晚; 改语义的 (鉴权 / 租户作用域 / 命名模型) 必须抢在消费者固化之前.
- [x] ✅ R1 鉴权 (OIDC 双轨) 代码落地 (2026-07-12, ADR-0022):
--auth-modeflag (legacy/soft/enforced) +auth.SoftHTTPMiddleware(带 token 必须验签过 401 不降级, 不带回落t_default+ deprecation 日志记 method/path/远端). 切强制只改 flag. - [x] ✅ R1 运维面: IdP (Keycloak) 上线 + prod 切 soft (2026-07-13, PM 拍 "现在赶紧做"): Keycloak 26.3.5 上 HK-133, 路径式
https://hub.flytoex.net/idp(Caddy /idp/ 路由, 零新 DNS/证书), DB=共享 postgres 的flyto_keycloak库. realmflyto+ clientflytocall(client_credentials, mapper: tenant_id=t_default 硬编码 + aud=flyto-platform). common 已带--oidc-issuer+--auth-mode=soft重建. prod 三轨真调验证: 无 token 200+deprecation 日志 / 坏 token 401 / 真 token 200 且审计行 subject=服务账号 sub. 凭据在 hk133/opt/flyto/secrets/flytocall-oidc.env(600), KC admin 在idp.env. 剩余*: 通知 flytoCall 限期接入 -> 切--auth-mode=enforced(只改 flag); 未来第二租户时 flytocall 的 tenant claim 从 t_default 换独立租户 + 实例迁移. - [x] ✅ R2 租户作用域 provider 解析 (2026-07-12, ADR-0022): Manager per-tenant snapshot (
SnapshotFor惰性加载 + copy-on-write,Current()= t_default 兼容视图) + 全方法租户参数化;ConfigSnapshot.ResolveInstance落地 "名精确 > 类型单条 > 多条 400 点名"; flow/转写/config 面全按请求租户解析. t_default 现有条目行为逐字节不变. 多租户隔离 + 解析语义测试全绿. - [x] ✅ R3 flow 自助创建 (2026-07-16, ADR-0020 v4, PM 拍提前): flows 表 + flowstore + 写面 (存草稿/发布闸=flow.Prepare/删除) + GUI 编辑器 (/flow 页, 输出 schema 即反射器) + 执行解析链 (内建优先->租户已发布). 生命周期/防覆盖/租户隔离测试全绿, prod 已部署.
- [x] ✅ R4 工具按名注册接线 (INF-3) (2026-07-16, ADR-0024): 复用 core
tools.Registry作平台侧运行时注册表 (FlowConfig.Tools),flow.Definition.Tools(含 sub agent) 解析成tools.Allowlist进 spec; 未知名 400, 发布闸同规则;GET /api/v1/flows/tools目录端点 + GUI 选择器. cmd/common 只登记只读安全 builtin (Read/Grep/Glob). 剩余: 业务工具 (查运单 / kb.search RAG) 按 flytoCall 需求逐个过审登记. - [ ] 🟢 R5 declared 档 model spec: 见下方独立条目 (m5max 256K 声明值). 消费面是 token 预算/UI 模型卡, 不动外部契约, 可后置.
- [x] ✅ R6 flow 执行审计持久化 (INF-4) (2026-07-13, ADR-0023, PM 提前拍):
flow_dispatches一行一执行 (tenant+subject / flow+provider_instance+model / input+raw_output / status+output+error / 计时) +dispatchstore(postgres+inmem) + handler 四出口 fail-open 写入 +GET /api/v1/dispatches列表 /{id}详情 /{id}/eventsSSE 重放全转正 (租户隔离, 跨租户 404). 剩余:idempotency_key真去重 (已落库, 加唯一索引+窗口即可, 语义增强非破坏, 可后置). - [ ] 🟢 R7 staging (m2max) binary 同步 + prod 过渡 override 清理: 下次 tag deploy 时 release.yml 已带 master key export, hub 上只剩主钥的临时 override 文件可删.
交付节奏建议: R1+R2 一个包 (改语义, 一次性通知消费者); R3+R4 一个包 (增能力); R5-R7 见缝插. 每包: ADR -> 实现 -> 全量测试 -> fastpush -> 真调验证.
具名 flow / 厚接入 (flytoCall 消费者, ADR-0020, 2026-07-12)¶
模型 B 泛化成数据驱动具名 flow. P0 (INF-1 按名执行 + INF-2 REST 结构化输出) 已落地 (enginefactory + responseguard.JSONSchemaValidator + flow 包 + POST /api/v1/flows/{name}/run + callcenter.wrapup).
- [x] ✅ INF-1 具名 flow 注册 + 按名执行 (2026-07-12):
flow.Definition/Registry/BuiltinRegistry+ 通用执行器POST /api/v1/flows/{name}/run(返 JSON 非 SSE, 每请求模型 B 引擎 + 完全租户隔离). - [x] ✅ INF-2 REST 层结构化输出 (2026-07-12): 三层防御 (provider json_schema / in-loop
JSONSchemaValidator反射器 / REST 最终校验).responseguard接到真消费者. - [ ] quotedispatch 收敛到 enginefactory (P2, tracked debt):
BuildMainEngine/BuildSubEngine与enginefactory.BuildStructuredEngine暂有两份 model-B flag 集. 收敛前必须先补 Config 等价性 guard -- quotedispatch 测试用fakeEngineRunner短路真引擎构造, 不覆盖engine.New的 flag 集, 直接收敛无测试兜底 (风险在 billcost 收入路径). 下次要动 quotedispatch 的 flag 时借机收敛. - [x] ✅ INF-3 工具收窄 (2026-07-16, ADR-0024): 注册表 + 按名解析 + 收窄进引擎已落地 (见 R4 条目). 剩余拆出: 知识库 RAG (全仓 embedding/vector/RAG 零命中, net-new) + 查运单/建工单等业务工具, 按消费者需求逐个登记. 消费点
callcenter.kb.search/opening_line. - [ ] 🟡 flytocall.lead_tag 质量迭代 (2026-07-17 登记): 652 全量 70.5% vs 90% 目标; 抓手 = undecidable 滥用 (28) / V-I 边界 (31) / U-I 边界 (24), 修法走记忆面 POST 纠正条目 (不重发布), 先 rule 子集 104 条验证增益再全量. GUI Playwright E2E (模拟用户建 flow) 亦未收尾.
- [x] ✅ Team flow 编排 (主 + 并行 sub agent) (2026-07-16, ADR-0024, PM 强令):
flow.Definition.SubAgents-- 每 sub 独立隔离引擎 (自己的 output_schema 反射器 + 工具收窄 + 可选 model), 并行扇出后主 agent 以{{.sub_outputs.<name>}}汇总; failover 对每次引擎运行各自生效; 扇出上限 8; GUI 编辑器带团队区. 消费场景: flytoCall 质检 (评分/违规/摘要并行). verdict 驱动多轮交互形态仍在 quotedispatch, 有消费者要再数据化 (agentprompt 协议已抽包). - [x] ✅ INF-4 flow 执行审计 (2026-07-13, ADR-0023):
flow_dispatches真持久化 + trace_id/flow_version/身份全关联 + 读面转正. 残余:idempotency_key去重 (见 R6 条目). - [ ] DB 背书自助注册 Registry (P2+):
flow.Registry接口是接缝. 需先定注册治理 (谁能注册 / prompt 审核) -- 与 §4.1 "prompt 归 CCM" 张力, 非 P0. - [x] ✅ 对真 provider 端到端跑通 wrapup (2026-07-12):
flows_live_test.go(//go:build live) 对真 Anthropic 走真defaultFlowEngineRun路径, 输出对照真值验证 (tier/tags 从活口径选取未自造, 见 ADR-0020 §5); staging + prod 真调均 verdict=ok. - [x] ✅ ADR-0020 v2 provider 来源纪律 (2026-07-12): 去掉
FlowConfig.DefaultProvider兜底与 RoleRun 路由优先; 请求必须provider_instance点名消费者自己的 ADR-0017 命名实例, model 走 请求 > 定义 > 400, 缺失一律 fail loud (503/400). 隔离按配置条目不按 key 值. - [x] ✅ prod 启用运行时引擎配置 + flytoCall 自有实例 (2026-07-12, PM 拍后执行): ①
FLYTO_SECRET_MASTER_KEY机器生成进 Gitea Actions secret + release.yml export, prod common 重建后运行时配置 enabled (store=postgres); ②flytocall-deepseek实例已建 (key 值与对账相同, 配置条目独立), prod 真调 flow verdict=ok; ③ 原 deepseek 默认引擎 override 的职能搬进配置层 (5 角色路由全指deepseek实例 +deepseek-chat), hub 上 override 瘦身成只带主钥的过渡 stub (release.yml 已带 export, 下次 tag deploy 后 stub 可删, 备份在 hub/root/override.bak.*). - [ ] 自托管模型的声明式 spec (declared 档) (P2, 2026-07-12 登记): m5max Gemma4 上下文 256K 是运营方声明值,
/v1/models目录不带 / probe 探不出 / admin API 另一套鉴权 -- 引擎侧该模型 ContextWindow=0 只能吃默认预算. ADR-0018 documented/probed 两档之外要一个 "operator declared" 档 (per-instance 声明 model spec, 落 engineconfigstore, discover/capabilities 端点合并带出), 别硬编码进 provider. 消费点: token 预算 / 压缩触发 / UI 模型卡. - [ ] prod REST 管理面无鉴权 (follow-up): common 无
--oidc-issuer->/api/v1/config/*(含 provider key 写入) 无 token 可调, 依赖 Caddy/网络边界. 多租户/对外前必须上 OIDC (ADR-0017 §1.5 单租户现实下暂可). - [ ] 转写端点异步模式 (提交+轮询) (P2, 2026-07-14 登记): 长音频转写响应静默数分钟, 消费者到平台链路的 NAT/中间盒按空闲切连接 (实测 ~4min). 同步端点保留, 增 async=true -> 返 task_id + GET /audio/transcriptions/{task_id} 轮询 (与 DashScope 同形, dispatchstore 有持久化接缝). 触发条件: flytoCall 生产环境报连接被切.
- [ ] 后续 flow 逐个上 (数据, 非 handler): tag.suggest / nba / profile.extract / pipeline.infer / lead.quality_score / funnel.insight 等. 各自一份
Definition+ prompt, 与 flytoCall 对齐契约后加. ASR 依赖类 (qa_score / violation / call.summary) 的上游已就绪: M5Max 语音转写已是引擎一等能力 (ADR-0021:flyto.TranscriptionProvider+ 探活 + 平台代理POST /api/v1/audio/transcriptions; prod 实例m5max已建), 待定的是转写文本进 flow 的编排 (谁调 ASR / 转写结果落哪 / flow input 契约).
ADR-0025 消费者工作室 + 会话/记忆 (2026-07-20, PM 拍地基->记忆->i18n->画布)¶
只做 flytoCall, 不碰物流对账 (ADR-0025 §1.4). 攒着整体一起部署, 不零敲上 prod.
- [x] ✅ P0 路由级 failover + /agent/run fail-loud (2026-07-20): RoleRoute fallback + 健康探活 failover + provider_instance fail-loud (见 CHANGELOG + ADR §2.5).
- [x] ✅ P1 第一刀: /agent/run per-customer 会话/记忆 (2026-07-20):
customer_id-> per-customer 引擎缓存 (ScopeRoot + FileSessionProvider,DisableDream:true) +ledger.jsonl底账 + 最近窗口注入. 可观测 "记住" 来自窗口 (第二通即生效, 不依赖 Dream). 接线点纠正: 生产分析走 /agent/run 共享s.engine非 enginefactory. 记忆落 FS + postgres 索引/灾备. 全 -race 绿 + 端到端集成测试证环路. 未部署 prod. - [x] ✅ P1 第二刀: Dream 服务端多 scope 调度 (2026-07-21 done): 引擎 4 Config 位 (
DisableDreamAutoFire/门槛/DreamTranscriptDir/DreamPromptBuilder) +CheckAndRunSync同步钩子 (信号量真 cap) + platform 中央 sweep (cap=2, active 钉防回收) + scopeObserver/dream_turn 观测 + 窄巩固 prompt (底账内嵌单步 Write, gemma4 真跑逼出的范式修法). 真模型端到端: 三通 -> sweep -> customer_profile.md 全事实可溯源零幻觉 -> 第四通共存良构. 微信客服线可直接复用同一套 (调度器/cap/PromptBuilder 全通用). 见 CHANGELOG. - [x] ✅ P1 部署前闸: 真模型验证 (2026-07-21 done): 本机直连 m5max/gemma4 三通实测 + 负对照, 窗口注入铁证 (品类信息仅存历史, 模型答对; 无历史客户答不出) + verdict 全程良构 + 顺手修 "首次接触" 占位 gap (见 CHANGELOG). 部署时仍须确认:
--customer-scopes-dir指持久化 + 有备份的卷. - [x] ✅ P1 底账/窗口硬化三项 (2026-07-21 done):
readLedgerTail反向 seek 读 (O(尾部) 与底账大小无关, 跨块/坏行/超长单行测试锁死); per-customer 引擎闲置 TTL 回收 (默认 30min, 与容量 LRU 互补, dreamSweep 不再刷 lastUsed -- borrow 是唯一写者); scope 档案 FS -> postgres 定期镜像灾备 (scopemirror实现 core memory.SyncAdapter 接缝, sha256 变更检测, Push 随调度 tick / Pull 在 buildEngine 缓存未命中时自动回复新卷, 本地永远赢). 见 CHANGELOG. - [ ] P1 底账/窗口硬化余项 (登记): 多实例部署前 scope FS 本地盘 + 粘性路由约束 (ADR-0011 §6, 现单实例 OK); 输入截断 600 rune 偏狠 (结论 900 更重要, 保留合理) -- 真数据来了再调质量旋钮.
- [x] ✅ P1 i18n 层 (2026-07-21 done): 语言轴 (第四正交轴, 中文默认) + 自写 t()/目录 (基础 + 一页一片段) + 存量 642 key 全量提取 (4 并行批次) + 后端错误 code 本地化映射 + i18n 闸挂 npm lint (parity + 禁内联中文). 见 CHANGELOG. 后续: 后端 ErrorEvent 新 code 加入目录是常态维护; 营销页英文文案待 PM 过目定稿.
- [x] ✅ P2 画布工作室 (核心) (2026-07-21 done): React Flow 默认视图 (主 + <=8 sub 两真节点, 坐标不存, round-trip 零漂移, 复用 buildDefinition) + 三层抽象兑现 (技术旋钮收折叠高级区) + 工具授权清单 + 画布 i18n. 表单保留为高级/回退. 见 CHANGELOG. 浏览器人工走查待 PM 打开 /flow 页.
- [x] ✅ P2 提示词版本/回滚 (2026-07-21 done):
flow_revisionsappend-only 快照表 (Put 同事务落行 / Publish 翻状态 / Delete 连带删) + StoreListRevisions/GetRevision(cap 50) + REST revisions/rollback (回滚 = 旧定义重新 Put 成新草稿, 历史永不改写, 仍过发布闸) + 前端版本历史抽屉 (一键回滚). 对照 diff 刻意后置, 有真需求再上. 见 CHANGELOG. - [x] ✅ P2 进化开关 (2026-07-21 done):
flow.Definition.Evolve可选 bool (缺省关, 向后兼容, 随修订历史版本化) + verdict=ok 确定性先例沉淀进 flowmemstore scope "evolve" (去重 + cap 20 轮转 + fail-open, 与人工纠正分 scope) + {{.memory}} 双 scope 渲染闭环 + 画布/表单业务语言开关. 真模型闭环验证 (gemma4: 第 2 次执行从先例答出仅存于第 1 次输出的事实 + 负对照干净) 固化为 live tag 测试. 不做 RL/LLM 提炼 (v1.x). 见 CHANGELOG. - [ ] P2 两层租户下放 (登记 2026-07-21, 待 PM 决断): "可见范围配置" 的读者 (flytoCall 白牌/受限视图前端) 不在本仓库, 本轮不造无读者的 dead config (dead-field 纪律). flytoCall 提出接白牌需求时再做: per-tenant 能力可见性数据模型 + API + 受限渲染约定.
消费者 UX 专题 follow-up (2026-07-18, PM 专题)¶
三修已落地 (等待黑洞 / 平台掐话 / 报错裸奔, 详见 CHANGELOG Unreleased "消费者用户体验" 段).
- [ ] i18n 层 (P3, PM 拍不急): 面向用户文案 (WarningEvent.Message / EngineError.Suggestion) 硬编码中文, Detail 是英文 provider 原串. 行业 platform 出海/多语客户前补. 正路: Code 是稳定契约, 消费方按 Code 查表映射本地化文案, 引擎不内置翻译框架.
- [ ] flows 流式模式接入 dispatches 回放 (P2): GET /dispatches/{id}/events 目前从落库行合成 (started/input/result), 不含运行期 warning/progress. 若运维要事后看 "这单当时重试了几次", 需把流式帧旁路落库 (dispatchstore 有接缝), 有真需求再上.
- [ ] 前端 POST-SSE 解析器合一 (P2, 自审 2026-07-18 登记): agentStream.ts 与 flowStream.ts 各有一份 fetch+ReadableStream SSE 解析循环, 已开始分叉 (flowStream 有终态守卫/Content-Type skew 防护, agentStream 无; 两者 multi-line data 拼接都偏离 SSE spec 的 \n join). 抽共享 parsePostSSE(body, onFrame) 进 lib/sse.ts, 两客户端只留各自 event switch. 同时补 agentStream 的断流检测 (run 页现在断流会静默停转).
- [ ] flowSSE 顾问帧背压隔离 (P2, 自审 2026-07-18 登记): emit 在 mutex 内同步写 socket, 客户端 TCP 停滞时 team 扇出各 sub 的事件 drain 会串行卡在 mutex 上. warning/progress 类顾问帧改小缓冲 channel + 单写 goroutine 满则丢弃, result/error 终态保持阻塞发送. 触发条件: 真实操作员反馈试跑面板卡死或 RunTimeout 异常增多再上.