# Flyto Agent - 细节补齐清单 > **归档**: 已完成的 815 项见 [TODO_DONE.md](./TODO_DONE.md). 本文件只列当前未完成的 11 项, 加速日常读取. > 每个细节都是"专业"和"玩具"的差距.逐一补齐,不留历史债. ## 优先级说明 - 🔴 P0:安全/准确性 - 不修就是漏洞 - 🟡 P1:功能完整性 - 不修就是残缺 - 🟢 P2:体验/性能 - 不修就是粗糙 - ⚪ P3:扩展性 - 不修就是僵硬 --- ## 模块 0:可观测性基础设施 ✅ ### 0.1 EngineObserver 接口体系 ✅ ### 0.2 默认实现 ✅ ### 0.3 StrictMode 严格模式 ✅ ### 0.4 Engine 接入 ✅ --- ## 模块 1:Bash 工具 ✅ 已完成 ### 1.1 AST 解析替代字符串分割 ✅ ### 1.2 环境变量前缀跳过 ✅ ### 1.3 引号感知命令分割 ✅ ### 1.4 进程管理 ✅ ### 1.5 输出截断策略 ✅ ### 1.6 后台运行 (run_in_background) ✅ ### 1.7 命令分类 ✅ --- ## 模块 2:FileEdit 工具 ✅ 已完成 ### 2.1 Curly Quote 保留 ✅(早期方案精妙设计) ### 2.2 Desanitization ✅ ### 2.3 两步验证设计 ✅ ### 2.4 文件缓存集成 ✅ ### 补完 ✅ --- ## 模块 3:FileRead 工具 ✅ 已完成 ### 3.1 图片处理 ✅ ### 3.2 PDF 支持 ✅ ### 3.3 Jupyter Notebook ✅ ### 3.4 设备文件阻止 ✅ ### 3.5 路径安全 ✅ --- ## 模块 4:Glob & Grep ✅ 已完成(升华重构) ### 4.1 双引擎策略 ✅(升华设计) ### 4.2 多行匹配 ✅ ### 4.3 类型过滤 ✅ ### 4.4 分页 + 排序 ✅(升华设计) ### 4.5 安全加固 ✅(G8-D review) --- ## 模块 5:权限系统 🔴 ### 5.1 自动模式分类器 ✅ ### 5.2 Compound 命令完整处理 ✅ ### 5.3 Sed 脚本验证 ✅ ### 5.4 重定向目标分析 ✅ ### 5.5 权限决策缓存 → 规则应用管道 + 去重 ✅ --- ## 模块 6:上下文压缩 ✅ ### 6.1 压缩后恢复精细度 ✅ ### 6.2 消息分组 ✅ ### 6.3 压缩前图像移除 ✅ ### 6.4 多策略压缩 ✅ ### 6.5 三层压缩降级 + 断路器改进 ✅ --- ## 模块 7:查询循环 ✅ ### 7.1 消息规范化管道 ✅(升华重构) ### 7.2 stop_reason 完整处理 ✅ ### 7.3 Tool Result 配对修复 + 可观测性基础设施 ✅ ### 7.4 Token 预算精细度 ✅ ### 7.5 查询链追踪 ✅ ### 7.6 查询循环防御性 ✅ --- ## 模块 8:API 客户端 ✅ ### 8.1 错误分类精细化 ✅ ### 8.2 重试策略完善 ✅ ### 8.3 API 预连接 ✅ ### 8.4 SSE 边界情况 ✅ --- ## 模块 9:Hook 系统 ✅ ### 9.1 Hook 输出影响行为 ✅ ### 9.2 pre/post-sampling hook ✅ ### 9.3 插件级 Hook 注册 ✅ --- ## 模块 10:Memory 系统 ✅ ### 10.1 路径遍历防护 ✅ ### 10.2 团队记忆同步 ✅ ### 10.3 自动提取代理 ✅ ### 10.4 新鲜度警告 ✅ --- ## 模块 11:MCP 客户端 ✅ 已完成 ### 11.0 工具名格式统一 ✅ ### 11.1 Transport 接口 + 多传输支持 ✅ ### 11.2 Client 重构 ✅ ### 11.3 Schema 转换完整性 ✅ ### 11.4 资源预取和缓存 ✅ ### 11.5 Elicitation 处理 ✅ ### 11.6 MCP 防御性 ✅ --- ## 模块 12:Plugin 系统 ✅(核心完成) ### 12.1 依赖解析 ✅ - [N/A] 语义版本约束(^1.0.0, ~1.2.3)- 不做.插件模型是叠加式(无代码级耦合), 版本约束需要 registry + 多版本共存,生态尚不存在.官方插件全部无相互依赖印证此判断. minEngineVersion 待引擎 API 稳定后再考虑. ### 12.2 LoadResult + 结构化错误 ✅ ### 12.3 Claude Code 格式兼容 ✅ ### 12.4 SDK 内置注册 ✅ ### 12.5 Plugin 完整性校验 ✅ (原 DXT/MCPB Bundle, 2026-04-15 反转重命名) ### 12.6 插件配置 Schema ✅ ### 12.7 Plugin 声明式 tool 注册 ✅ (2026-04-15 新增) - **驱动**: 产品经理 2026-04-15 对话纠正: 原话"注册 tool 本来就是现成的功能", 指出 commit 58c3ab5 的"plugin 只能通过 MCP server 间接暴露 tool"是不完整 framing. tools.Registry.Register 本就是公开接口, plugin loader 只需补一层 manifest → pluginShellTool → Register 的数据翻译即可. 本 commit 填补了这个功能缺口. ### 12.8 Plugin MCP server engine 集成 ✅ (2026-04-15 新增) - **驱动**: 上次对话 073da52 LSP 审计发现 engine 层 pluginHost 和 toolsRegistry 的 MCP wiring gap: Plugin.Hooks 有 syncPluginHooks, Plugin.Tools 有 syncPluginTools, 但 Plugin.MCPServers 无对称 sync 方法. internal/mcp 已有完整 5647 行 Manager+Client+Transport, 本任务是纯 wiring 不是造轮子. --- ## 模块 13:Agent 子进程 ✅ ### 13.1 预定义代理类型 🟢 ### 13.2 工具过滤精细度 🟢 ### 13.3 Prompt Cache 共享 ✅ ### 13.4 工具权限精细控制 ✅ --- ## 模块 14:Skill 系统 ✅ ### 14.1 SkillDef + SkillRegistry ✅ ### 14.2 Frontmatter 完整支持 ✅ ### 14.3 文件发现 ✅ ### 14.4 Inline/Fork 执行 ✅ ### 14.5 SkillTool ✅ ### 14.P1 ✅ --- ## 模块 15:系统提示词 ✅ ### 15.1 PromptBundle + BundleRegistry ✅ ### 15.2 缓存边界优化 ✅ ### 15.3 默认 Bundle(claude+programming)✅ ### 15.4 SDK 扩展 API ✅ ### 15.7 中文 Bundle ✅ ### 15.6 P1 补充 ✅ ### 15.5 测试 ✅(47+ 测试) --- ## 模块 16:AutoDream(记忆巩固)✅ > KAIROS 系统核心.与 C 方案自进化直接关联 - Dream 整理记忆,自进化基于记忆改进. ### 16.1 Dream 引擎 ✅ ### 16.2 Dream 四阶段提示 ✅ ### 16.3 DreamTask ✅ ### 16.4 Stop Hook 集成 ✅ ### 16.5 SubAgent 扩展 ✅ --- ## 模块 17:UltraPlan(高级计划模式)✅ ### 17.1 计划生成(P0)✅ ### 17.2 计划步骤(P1)✅ > **注**:17.3 远程计划移至模块 20,仅适用于本地进程(CLI/SDK本地部署)提交计划到守护进程执行. --- ## 模块 18:Coordinator Mode(多 Agent 协调)✅ ### 18.1 协调器角色 ✅ ### 18.2 任务通知 ✅ ### 18.3 Scratchpad ✅ ### 18.4 Worker 生命周期 ✅ --- ## 模块 19:Bridge Mode(远程桥接)✅ > 实现位置:platform/pkg/bridge/(平台层,非引擎层) > 架构决策:v1/v2 是 Anthropic 私有云协议,不复刻;改为通用 BridgeTransport 接口. ### 19.1 核心接口 ✅ ### 19.2 消息去重与批量上传 ✅ ### 19.3 SSE Transport ✅ --- ## 模块 20:Daemon Mode(守护进程)✅ > 实现位置:platform/pkg/daemon/(平台层,非引擎层) > 架构决策:Go goroutine pool 替代 Node.js 子进程;Idle Timeout 替代固定 24h. ### 20.1 会话生命周期管理 ✅ ### 20.2 健康检测 ✅ ### 20.3 远程计划(来自17.3)🟢 ✅ > 适用于本地进程(CLI 或 SDK 本地部署)提交计划到守护进程执行. > HTTP API 不需要此功能--服务端本身就是执行方,客户端调用即是"远程". --- ## Platform HTTP API Server ✅ > 实现位置:`platform/pkg/server/server.go`(平台层,在引擎层之上) > 对应:PLATFORM_TODO.md §1.1 HTTP API Server P0 全部完成 --- ## 模块 21:UDS Inbox(进程间通信)🟢 ### 21.1 内存 Inbox(同进程通信)✅ ### 21.2 消息协议 🟢 --- ## 模块 22:精妙细节(从早期方案深度分析中发现)✅ > 这些是产品级和玩具级的分水岭. ### 22.1 API 交互细节 ✅ ### 22.2 缓存和性能细节 ✅ ### 22.3 安全细节 ✅ ### 22.4 消息处理细节 ✅ --- ## 模块 23:SQL 工具链 ✅(2026-04-23) > 面向 staging / 影子表的三件套. AI 写业务 DB 走 "Agent → staging → ML 审批 → WMS API 写生产" 三步流程 (memory `project_db_ai_relationship.md`) 中的 staging 一跳. TODO.md 之前把这三条归在 "消费层待实现不属于引擎层" 是 2026-04-08 立项时的保守判断; 实际业务逻辑 schema-agnostic / driver-agnostic, 通过 StagingDB newtype + *sql.DB DI 让客户端注入 driver, 引擎层仅 import `database/sql` 标准库, 零生产第三方依赖, 所有客户复用. ### 23.1 SQL 只读校验器 ✅ (commit `79670c7`) - 纯字符串解析, 零 DB 依赖 - 规则: 非 SELECT/WITH/EXPLAIN 拒 / 多语句拒 / LIMIT 可注入或校验 / 表名白名单 - quote-aware 扫描 (字符串 / identifier quote 内的 `--` 和 `/*` 不被误识) - schema-qualified 表名 (`public.orders`) 支持 ### 23.2 SQL CAS 乐观锁 ✅ (commit `bf31278`) - `StagingDB` newtype + `NewSQLCASTool(db, maxRetries)` DI (构造处强制显式声明 staging 作用域) - maxRetries 默认 0 (fail-fast; AI Agent 看 version 冲突应重 plan 非 silent retry) - version 非 int 运行时 reject (避免 timestamp / CDC 复制场景 silent 失效) - identifier `[a-zA-Z_]\w*` 白名单拒 quoted, 所有 value 走 `?` 参数化防注入 - `modernc.org/sqlite` test-only dep (core 第一条非图像处理第三方依赖, 仅 `_test.go` import) ### 23.3 SQL Dry-run 三路 ✅ (commit `a935604`) - 方案 E (before + after 都 SELECT), UPDATE/DELETE/INSERT 按 operation 刻意不对称 - LLM 传 `preview_predicate` (工具不 parse SQL), 一致性检查标 `mismatch` / `after_predicate_mismatch` 信号 - 100 行 truncate 显式提示 (非 silent sampling 的 approval theatre) - `DryRunResult` 3 字段首次 write point (SQLDryRunTool 填), 外部 UI / audit 反序列化消费 (pull API 归档状态保持不变) --- ## 基础设施层(服务端能力)🔴 > 早期方案是客户端,背后有 Anthropic 服务端.我们是客户端+服务端,必须自建这些基础设施. > 原则:涉及代码面越广的越先做,否则后面改动太大. ### INF-1 可观测性(EngineObserver)✅(与模块 0 相同,详见顶部) ### INF-2 文件历史/回滚 + ToolCapability 协议 ✅ ### INF-3 优雅关闭 ✅ ### INF-4 会话活动追踪 ✅ ### INF-7 引擎竞态修复 ✅(2026-04-07) ### INF-5 安全审计 ✅ ### INF-6 版本兼容 ✅ ### INF-7 数据安全(文件/DB/API 三维度)📄 文档完成 > 文档:`docs/data-safety.md`(已完成,含凭据安全章节) > 代码:消费层实现,引擎层接口已就绪 #### 引擎层--框架质量修复(代码审查) #### 引擎层--凭据安全 #### 引擎层--SDK 编排能力 - [x] 🟡 **L952b → L407: platform 消费层文档自动化三件套** ✅(2026-04-26, commit C0-C6: `89c38d3` C0 path bug fix `/v1/* → /api/v1/*` (Caddyfile + ADR-0002 + server.go 8 mux 路由 + server_test.go ~25 处 + main.go 注释); `26bc732` C1 server.go 8 handler swag 注解 + 3 named response type (HealthResponse/StatusResponse/ListToolsResponse) 替代 ad-hoc map + swag.go seed file + docs/{swagger.json 22.5K 12 schema, swagger.yaml 12.3K, docs.go 23.1K Go embed} 首版; `bce7670` C2 cmd/common --swagger flag + Swagger UI endpoint (httpSwagger v2 + side-effect import docs, authMiddleware/rateLimit allow-list 加 /swagger/); `22b6388` C3 core/Makefile 4 docs target (docs-swag/docs-grpc/docs-consumers/docs-all) + tool install pin (swag@v1.16.6 + protoc-gen-doc@v1.5.1) + grpc-api.md 首版 278 行 + .gitea/release.yml docs drift gate (apt-get protoc + make docs-install + docs-swag/grpc + git diff --exit-code); `a9b04ab` C4 docs/CONSUMERS.md 顶层 wrapper 133 行 (端口拓扑/env/flag/OIDC auth 流程图/业务 REST 一次性+多轮会话 curl 例子/观测 gRPC SafetyChain 指引/进一步阅读链); `bf5af1d` C5 Caddyfile handle /swagger/* → common:8080 + docker-compose --swagger flag (HK-133 lab 默认开, 生产另份 compose 关); 本 commit C6 TODO/CHANGELOG/CLAUDE.md 同步). **三件套全产**: 业务 REST → docs/swagger.{json,yaml,docs.go} (12 schema, swag init 产物); 观测 gRPC → docs/grpc-api.md (HealthService + SafetyChainService 字段表); 顶层 docs/CONSUMERS.md (133 行 wrapper, 链 swagger.json + grpc-api.md 不重复). **CI drift gate** 在 release.yml dead-field-ratchet 后插, push tag 时 install protoc + swag + protoc-gen-doc 跑 docs-swag/docs-grpc + git diff --exit-code, 偏差 fail tag 构建 (业界对照: Stripe / Anthropic / OpenAI 都对 OpenAPI spec 跑同等闸). **意外+顺手修 (C0)**: 上一会话 commit 5 (`c35e761`) Caddyfile `handle /api/v1/*` 不剥前缀, server.go mux 注册 `/v1/*` 不匹配, 经 hub.flytoex.net 全 404, L407 之前先打通真实部署链 (memory `feedback_validate_network_path_before_deploy` 警告再次成立). **smoke**: ANTHROPIC_API_KEY=fake go run ./cmd/common --rest-addr=:18080 --swagger → /api/v1/health 200 + /swagger/index.html 200 + /swagger/doc.json 200 返回 commit 1 嵌入 spec; -race 全绿. **不做** (rule of two): SDK 自动生成 (Stainless 模式, 等 5+ 语言客户端); .proto 字段注释完善 (health.proto 部分字段 description 列空, protoc-gen-doc 自动反映, 后续工作). - [ ] 🟢 **L952c: 场景化编排 Go 使用教程** (P3, 2026-04-17 拆自 L952). **产出**: `core/examples/orchestration/{ssh_deploy,db_migration,system_config}/main.go` 三个可跑示例 + `core/docs/orchestration_scenarios.md` 指向它们. 演示 Checkpoint / Reversible / DryRun / SecretStore 组合用法. **成本**: 500-1500 行 Go. **不紧急**: 当前无明确第三方集成需求, 投机未来回报率低. 等有实际集成方催再补不迟. #### 引擎层(已就绪,无需再实现) #### CLI TUI 消费层(ccm/tui/ - P0+P1 完成, 新功能冻结 2026-04-15) #### agent-engine CLI 修复(2026-04-08 发现) #### server / transport 低优先问题(2026-04-08 Review 发现) - ~~`internal/server/` 已删除,迁移至 platform/ 从零开始~~ #### 消费层集成测试(2026-04-08 立项) #### 消费层待实现(不属于引擎层,由平台/消费者完成) - [ ] 🟡 ML 验证器接入(diff 序列化 → ML 推理 → 通过/拒绝) — core 接口就绪 (Validator / LLMValidator / CompositeValidator / AlwaysApprove + NewValidatedTool nil fail-fast, 见 L699+). platform 接外部 ML backend 实现 + 装配即可启用. - [ ] 🟡 熔断器(连续 N 次 ML 拒绝 → 暂停 AI 写权限 + 告警) — core 三态 breaker + VerdictSink 桥接就绪 (commit `3342425`). platform 选作用域 (全熔/只熔写/每工具一熔) 并 wire ValidatedTool sink 即可启用. - [x] 🟡 Staging 表管理(决策包级 pending_tech/rejected_tech/pending_ml/rejected_ml/approved/executed/failed 7 状态机 + 混合控制: staging 主动 ValidateTech/Biz + 外部推 MarkExecuted/MarkFailed + 可插拔 DependencyGuard + InMemoryStore 参考实现) ✅(2026-04-24, commit 1/2/3: d9992d5/2c38e46/本 commit). 复用 `validator.Validator` 为两层 slot, `reflector.EvaluatorAsValidator` 适配 Evaluator 接入. pull-only query API. SQL-backed Store 由平台层实现 (contract 见 `core/pkg/staging/store.go`). - [x] 🟡 影子表管理(多轮推理场景:session 级镜像表 + 会话结束清理) ✅(2026-04-25, commit 1/2/3: 2c84209/e140952/本 commit). 方案 C 列标记隔离 (`pkg/shadowdb/`): 物理 shadow 表加 session_id VARCHAR(64) NOT NULL 列, 按 session_id filter 做跨 session 隔离. 规避 PG TEMP TABLE 在 pooled *sql.DB 下 temp 表蒸发 + driver 分裂问题. 零 DDL 纯 INSERT/UPDATE/DELETE/SELECT + ? 参数, PG/MySQL/SQLite 通吃. Opener 接口 + InMemoryOpener 参考实现 + pull-only Reap 孤儿 GC (core 不起 goroutine) + EnforceSessionFilter 三层防御中层 (quote-aware string/comment 剥离). 与 staging 边界: staging 决策级 pre-commit, shadowdb session 级推理中 scratchpad; shadow -> staging -> production 单向, shadow 永不 merge. - [x] 🟡 **L692: platform/common 业务 REST/SSE 通道激活** ✅(2026-04-26, commit 1-6: `01f08e7`/`8189d05`/`694bd07`/`e1db327`/`c35e761`/本 commit). **3-agent review reconcile** (调研 11 LLM API + grpc-gateway / 质疑 6 道硬题 / 设计 4 备选), PM 拍板方案 A 修正版: 激活 server.go 1263 行已写好的业务 REST/SSE + grpc-gateway 砍掉 (admin/server.go 已实现观测面 REST handler) + ADR-0002 立 "REST 业务 / gRPC 观测" bifurcation (与 9 天前 memory `feedback_architecture_principle_over_rule_of_two.md` 的 "gRPC 优 HTTP" 是分层不是反悔). **commit 顺序**: `01f08e7` C1 server.go 拆 Serve+wrapper 让出 signal handling; `8189d05` C2 Verifier 替代 BearerToken 走 auth.HTTPMiddleware (raw shared-secret + ConstantTimeCompare 删除, OIDC 与 admin 一致); `694bd07` C3 Attach + HandlePermission 拆 anthropic provider 写死, server 不再读 ANTHROPIC_API_KEY 不再 hard-code 模型; `e1db327` C4 cmd/common 加 `--rest-addr` flag 装配 anthropic provider + engine + s.Attach + 第三 listener wire (signal handler 三路协调: grpc.GracefulStop / httpSrv.Shutdown / restCancel); `c35e761` C5 docker-compose expose 8080 + Caddyfile `/api/v1/*` (flush_interval -1 + response_header_timeout 0 SSE 透传, ANTHROPIC_API_KEY `${...:?...}` 必填); 本 commit C6 ADR-0002 + TODO/CHANGELOG/CLAUDE 同步. **Tool 级安全链装饰刻意不在 cmd/common 装** — 一刀切 AlwaysApprove + DefaultExtractor 会把所有 Tool 锁同一组合或 fan out 到不匹配 Tool 语义的 wrap. common 保持纯 transport, verdictStore 接线就绪, 由行业驱动代码 (logistics 等) 在 Tool 注册时装饰并写数据 (ADR-0002 § Decision 第 3 条记录这个分工). **业界对照**: 11 LLM API (Anthropic / OpenAI / Bedrock / Cohere / Mistral / Replicate / LM Studio / Ollama / LangServe / Together / Fireworks) 全单通道 REST/SSE; Vertex AI 是 gRPC-first 例外, 但 GCP transcoding 模式不仿照. ADR-0002 引用 11 框架矩阵 + grpc-gateway / gRPC-Web 现状 + ConnectRPC 替代论证. **测试**: server_test.go 既有 546 行用 httptest 直接打 handler 无须改, 4 个 raw bearer TestAuth_* 删除 (auth.HTTPMiddleware 自身覆盖在 internal/auth 包测试), 新加 `TestServer_Serve_RespectsCtx` ctx 取消干净返回. -race 全绿. cmd/common binary 集成验证 `--help` 显示 `--rest-addr`. 实测 (docker exec curl + SSE chunk timing) 留 push tag 前按 memory `feedback_validate_network_path_before_deploy` 走. - [ ] 🟢 AuditSink 数据库实现(写入 PostgreSQL,按 session_id 查询,支持批量回滚) - [ ] 🟢 WMS 波次建立参考实现(状态机新增"任务已建待确认"状态) - [x] 🟡 **L693: 业务 REST 多副本 SessionStore interface** ✅(2026-04-26, commit 1-4: `eec48fe`/`beaff60`/`96e893a`/本 commit). **3-agent review reconcile** (调研 LangGraph/Vercel/Temporal/Express/Django 业界 prior art / 质疑 6 道击中 3 道 / 设计 3 alternatives 选 typed Alt 2), PM 拍板"整个 platform 都 Postgres". **真相**: 多副本真阻塞不是 sessions map, 是三层进程内 pin (server.permCh + Session.pendingPermissions + engine.sessionState), 后两层在 core 引擎层平台层不能解, **必须 LB sticky routing**; SessionStore 价值降级为"replica 重启不丢元数据 + 滚动部署 drain + Postgres audit". **commit 顺序**: `eec48fe` C1 SessionStore 接口 (Create/Get/Delete 三方法, 不要 Touch/List 投机) + InMemoryStore drop-in 替换 server.sessions map + 4 handler 改造 + TOCTOU race 折叠 (`319 行 + 改 server.go 182 行`); `beaff60` C2 Postgres 后端 + `internal/db/` 共享池 (中央 schema 权威 platformMigrations + Migrate 启动期 idempotent) + `--postgres-dsn` flag + docker-compose pg service + healthcheck + persistent volume + release.yml POSTGRES_PASSWORD secret + testcontainers-go 真 pg 测试 (`~510 行 + 改 main.go 53 行 + docker-compose 48 行`); `96e893a` C3 ADR-0003 (387 行, 8 节, 三层 pin 物理事实分析 + sticky routing phase 1 ip_hash + phase 2 X-Session-ID 升级路径 + cache miss 503 fallback) + Caddyfile 注释 (单副本 vs 多副本部署区别); 本 commit C4 TODO + CHANGELOG + CLAUDE.md 同步. **关键决策**: drop Redis 档 (元数据 payload 太薄, 共享 staging pg 池更经济); drop staging Postgres 后端 (PM 接受 YAGNI 跳过, 等 staging 真有消费者再做, ADR-0003 § 5.5 登记触发条件); engine.SnapshotStore 接线 cache miss 自动恢复历史留 follow-up. **不引正式 migration 工具** (1 张表, plain CREATE IF NOT EXISTS 自检足够; 等 ≥ 5 张 + schema 稳定再引). **测试**: 6 InMemoryStore 测试 + 5 testcontainers Postgres 测试 + 2 db pool 测试, 全模块 -race 全绿; dead-field-scanner baseline 220 不变 (scanner 只扫 core/). **PM 部署侧必做** (v0.4 release 前): Gitea secrets 配 POSTGRES_PASSWORD (跟 ANTHROPIC_API_KEY 同位). - [ ] ⚪ **L694: gRPC + REST cross-transport request-id / trace 串通** (P3, 2026-04-26 登记自 L692 ADR-0002 tracked debt). **背景**: server.go 自己生成 request-id (server.go:331 getRequestID), gRPC 侧没有. 用户从 logistics C# 发 gRPC ListVerdicts 想看 "我的请求触发了什么 verdict" — request-id 串不起来. **产出**: OpenTelemetry trace span 跨 transport 注入 (HTTP header + gRPC metadata), Tempo / Jaeger 一类观测后端落. **成本**: 引入 OpenTelemetry SDK + collector / exporter wire, 中等. **不阻塞 v0.3**: 当前单租户 dogfood 不需要, 上量后才有 ROI. - [ ] ⚪ **L695: SSE 1000 单/s 带宽监控** (P3, 2026-04-26 登记自 L692 质疑 agent Q1.3). **背景**: SSE framing (`event:...\ndata:...\n\n` 文本) 比 gRPC binary protobuf 多 5-10x overhead. 业界共识 (LLM API 全 SSE) 接受这个代价, 但 logistics 1000 单/s × 平均 20 events/单 = 20K events/s 真上量后跨 region 流量成本要量化. **产出**: prometheus exporter 暴露 `sse_bytes_per_second` / `sse_events_per_second` metrics, Grafana dashboard 跟踪. **不优化**: 监控只是为了量化, 真到瓶颈再优化 (压缩 / batch / fallback gRPC streaming, 都是 v1.0+ 课题). - [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-dir` flag) + 1 server handler 文件 + 测试 30+ 单测含 fakeEngineRunner test seam. swag 注解走 server.go 同款体例, `make -C core docs-swag` 重生 swagger 三件套. 详 CHANGELOG `Unreleased (v0.5-dev)` 段. - [ ] ⚪ **L697: HumanInput SSE 通道实装** (P2, 2026-05-03 登记自 L696 follow-up). **背景**: `quotedispatch.HumanInputProvider` 接口已立, server 现接 `UnavailableProvider` stub → 触发 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.go `ALTER ADD COLUMN` + PostgresStore/InMemoryStore wire + persistRoundTraces + `review-dispatch.py` `A:` 行. 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` 复用 core `minimax.ExtractVision`, 忠实转 markdown prompt) + handler `Run` 后提取->VLM->独立 `embedded_images` final 字段 + 流式事件 (best-effort 单图失败不致命) + cmd/common `MINIMAX_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` (additive `ExtractShipCostCfgWithPrompt`, 对账零影响) + billcost 单独 `_vision.md` prompt + 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.md` prompt 编辑本轮已做 (跟 _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 = `...`; 本地 Gemma omlx = `<|channel>......`; 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 状态机剥 `` / `<|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-request `main_max_tokens` UI 可设, 预填 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` 无条件含失败图) + 抽取成功 stamp `VisionAt` (修 §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.ChinaProvinces` godoc 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 + `quotedispatchstore` `SaveAdjustments`/`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 字面签名是有意取舍). P3 `GET /analysis` base+overlay 分层按 source table 不 flatten + 前端 AnalysisView 三态 coverage + `CHINA_PROVINCES_34` view-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 抽全部→Phase2 `groupPages` 保守分组 (只在明确续页信号才合, 判错退回分开显示绝不错合)→Phase3 `mergeGroup` 续页日期 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 路由 (commit `82cf365` + `1053e4e` 14 test + `f152e33` swag 重生) ③ frontend/ 脚手架 + 5 页面骨架 + 双层 preset (commit `e09f559`) ④ docker-compose + Caddyfile + release.yml frontend image build 接入 (commit `8b86d9c`). **范围已 ship**: `/login` `/` `/flow` `/runtime` `/settings` 5 页面拉真 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 顺序**: `248253e` C1 `core/pkg/counterfactual/` Deliverable schema (五步反向思维标准化数据结构, MetadataKey "flyto.counterfactual.deliverable" 跨包 Record.Metadata 落点); `0385efd` C2 `core/pkg/skills/reverse_think/` MiniMax Anthropic 兼容端点 Go 化 client (替代 `~/.claude/skills/reverse-think/SKILL.md` 软性 markdown 在生产 Agent 不可调的 gap); `c4b755f` C3 `tools.Metadata.RequiresReverseThinking` opt-in flag (与 RequiresCheckpoint 同源 rule of two); `4618b79` C4 `Deliverable.AsReplayEvent` → `evolve.LogReplayer` 跨包 adapter (counterfactual import evolve 单向, evolve schema-agnostic 不感知 counterfactual; 关上 PM 担心的 "各行业自写 skill 进化结果停在哪里" 循环); `40aae31` C5 `platform/common/safetychain/ReverseThinkingHook` reference hook (走 hooks.PreToolUse 不 wrap Tool, 与 Assemble 平行, fail-open advisory LLM 错不阻断业务); 本 commit C6 文档 + ADR-0001 同步. **方案 A 完整版否决理由 (写入 ADR-0001)**: 与 hooks/permission/validator/staging/RequiresCheckpoint 5 套现有 gate 概念重叠 + 业界 8 框架 (Claude Agent SDK / OpenAI Assistants / Vertex ADK / LangGraph / AutoGen / CrewAI / Semantic Kernel / Cline) 全部走 prompt-level + tool desc + 开发者自填策略零先例 engine-level 强制 reasoning gate + 范畴错位 (dev review 协议错配 runtime gate) + 吞吐数学不成立 (logistics 1000 单/s × 即使 10% 触发 → MiniMax token plan call 数被打爆). **3-agent 并行 review 流程**: 调研 agent (8 框架矩阵 + 决策前 gate 形态调查 + 业界共识 reasoning) / 质疑 agent (5 道硬质疑含 RequiresCheckpoint 已实装 anchor 修正) / 设计 agent (4 备选 + 边界图 + 拍板假设矩阵). PM 拍板 Option 2-Plus (在原 Option 1 不做 + Option 2 缩水间, 加一件 evolve 接线解决 "进化结果停在哪" 担忧). **总量**: ~2,131 行 (含双语 godoc + 测试), 6 commit. **测试**: counterfactual 13 + reverse_think 12 + tools 1 新 + safetychain 9 = 35 测试 -race 全绿. core + platform/common 双 module build 干净. - [ ] 🟢 **微信 ClawBot 接入 (platform 层)** (P3 低优, 2026-04-16 登记, 不属于 core 引擎层). **背景**: 腾讯 2026-03-22 开放 WeChat ClawBot 插件 + iLink Bot API, 中国大陆唯一合法的个人微信第三方 Bot 通道. Hermes Agent v0.9.0 (2026-04-13) 已接入. **架构定位**: **属于消费层 (platform/) 不进 core/**, 遵守宪法原则 9 "引擎核心不假设特定场景" — 微信是腾讯产品 × 中国市场 × ClawBot 商业条款, 三重非中立. **代码位置**: `platform/internal/messaging/weixin/ilink_bot.go` (新建), 实现 `core/pkg/bridge/transport.BridgeTransport` 接口. **技术栈**: HTTP/JSON long-poll 35s, AES-128-ECB, user-bound token (扫码登录), 不需要公网 IP. **商务门槛≈0**: 不需要 Tencent 开发者证, user-bound 不是 platform-bound. **部署模式**: 国内直连 ilinkai.weixin.qq.com / 海外 BYOT (Bring Your Own Token) + 域名隔离规避深圳南山司法管辖. **战略价值**: 14 亿用户入口, 中国 to C 市场 unblocker. **优先级 P3**: 不属于引擎层, 平台层做; 排在 platform 层其他工作之后. **详**: `core/docs/agent_teams_competitive.md` §4.6. (P3, **属于 platform/ 层不属于 core/**) --- ### G5 Review 发现(pkg/tools/builtin/) > 来源:2026-04-09 G5 Review #### 已修复 #### 待修复 **安全 / 可靠性** **Bug** **历史债** --- ### G6 Review 发现(pkg/providers/ - 7 个 provider,20 个文件) > 来源:2026-04-09 G6 Review #### 已修复 #### 待修复 **安全** **一致性 / 历史债** --- ### G7 Review 发现(internal/ - bash/mcp/cache/logger/server/diff/tokenizer/git) > 来源:2026-04-08 G7 Review #### 已修复 #### 待修复 **安全 P0** **安全 P1** **正确性 P2** **P3** --- ### G9-G12 架构审查修复(2026-04-09) #### G9 系列:引擎层 Anthropic 解耦 #### G10 系列:Prompt/Context/Config 层解耦 #### G11 系列:Permission/Query/Wire 层 #### G12 系列:工具类包 #### G8 P2 补充修复 #### Phase 2 架构清理(2026-04-09) --- ## 实现债务 (dead-field scanner 追踪) 2026-04-19 新增, 同日 A 档 4 条 wire 完毕. `core/tools/dead-field-scan` 扫到 26 条已声明但扫描模块内从未被读的 exported field, MiniMax M2.7 triage 过, spot-check 4/4 通过 (见 commit 6c01d15 的 `scan-baseline.json`). 这些字段**不是"可删垃圾"**, 是"设计意图先行、实现未连"的 feature gap. 类比 engine.Config.MCPServers (57a06c6) 和 Config.Plugins (07fe345) 的修复思路: wire, 不是 delete. **A 档 wire 同日完成 (2026-04-19)**: 两轮 commit 达成真 wire, 不是"让 scanner 满意"的形式实现. **第一轮 (commit d2c647c) — 满足 scanner read site**: - 3 APIError 字段经扩 `Error()` 方法展示 Hint / TokenGap / Body (256B preview). `Hint` 到位 (日志即用户看诊断); `Body` 半到位 (preview 在日志, 完整 body 可 SelectorExpr 直访); `TokenGap` 仅 print 数字. - `Config.Verbose` 经 engine.New 尾 `verbose_startup` 事件, 只一条启动标记. **第二轮 (commit 本轮) — 真设计意图兑现** (用户反向挑战"scanner 满足 ≠ wire 完成"后补): - `TokenGap` 接 compactor: `agentctx.CompactTiered(..., WithTokenGap(n))` + 内部 `truncateAndRetryCompact` 精确跳步分支 (累积最旧组直到 >= tokenGap 一次丢掉) + `TestTruncateAndRetryCompact_TokenGapStride` + `_ZeroSkipsStride` 两条 regression guard. engine runLoop 从 `*api.APIError` 解 TokenGap 透传给 `forceCompact`. 字段 godoc 承诺的 "压缩模块一步跳过 N token" 从声明兑现到行为. - `Verbose` 加第二处 trace: runLoop 入口 `verbose_run_loop_started` 快照含 model / message_count / tool_count / prompt_len / max_turns / is_skill_prompt. 默认 observer 零行为影响 (新 event 名, 不 gate 现有 event), verbose 模式有完整 trace 起点. baseline 438 → 434 自第一轮起锁定, 第二轮不变动 baseline (两个字段已被读过, scanner 视角已 alive) 但实质意图层面从"形式"升到"真"wire. 原 A 档第 5 条 `LLMCallOpts.Temperature` 经 verify 发现是 scanner 间接假阳性 (test 验 forward, 公开 API, 字段真的 wire, 但 FlytoLLMClient adapter 刻意忽略), 降档到 C 档重评估. ratchet 规则 (见 `make dead-field-scan-ratchet`): baseline 只能减不能增. 新加 dead field → CI fail. 修完一条 → baseline shrink, 提示 commit. 下面 26 条是当前 debt list, 每 wire 一条就从 baseline.json 少一行, 对应条目从本 section 勾掉. ### A 档 (v0.1 要做, 小 wire) — 4 条 (全部已 wire) - [x] `transport.APIError.Body` — 扩 `Error()` 把 body 前 256 字节带入日志行 (2026-04-19) - [x] `transport.APIError.Hint` — `Error()` 附 hint 字段 (2026-04-19) - [x] `transport.APIError.TokenGap` — `Error()` 附 `(token_gap=N)` + **真设计意图达成**: compactor `truncateAndRetryCompact` 接 `WithTokenGap` option, 从 API 精确溢出值一步跳步 (字段 godoc 承诺的"让压缩模块一步跳过 N token 的消息组"兑现), 带 `TestTruncateAndRetryCompact_TokenGapStride` 锁定 (2026-04-19) - [x] `engine.Config.Verbose` — engine.New 尾 `verbose_startup` + runLoop 尾 `verbose_run_loop_started` trace (model / message_count / tool_count / prompt_len), 非 Verbose 路径零行为影响; "详细日志模式"真意图兑现 (2026-04-19) ### B 档 (v1.0 要做, 特性框架 wire) — 17 条 (Skill 3 条 2026-04-19 从 C 档移入) - [x] `internal/syslib/bash.Node.Background` — bash `&` backgrounded job 语义真 wire (2026-04-20): 比 heredoc 深一级 — parser **从不写入** Background, 字段定义但永远为零值. 4 sub-claim 端到端: (a) parser.go parseList `op=="&" → list.Background=true` + 尾部孤立 `&` wrap 单 child NodeList (Bash 语义: `cmd &` 把 cmd 放后台). (b) extract.go extractContext 加 `background bool`, 两处 NodeList 分支 (extractFromNode + extractAllFromNode) 实现 "left 继承 / right 清掉" 传播: `cmd1 & cmd2` cmd1 bg cmd2 fg; `cmd1 && cmd2 & cmd3` 整个 && 组 bg cmd3 fg. (c) CommandInfo 新增 `Background bool` 字段 (双语 godoc 说明 "escapes foreground control" 风险), extractSimpleCommand 从 ctx.background 填入. (d) bash_security.go AnalyzeDanger 新 helper `amplifyBackground(info, c)` 在所有 danger return 点调用: Reason 附 "backgrounded -- escapes foreground control", Pattern 前缀 "& ". 把 bash.CommandInfo.Background 从 parser 装饰属性接入安全决策面 — 过去 `rm -rf / &` 和 `rm -rf /` 同样报警无区分, 现在 TUI/审计能告诉用户哪个能通过取消会话中止哪个不能. 4 条 regression test 一 sub-claim 一 test (3 bash 包 / 1 permission 包): TestParseList_BackgroundWriteSite (4 子 case 覆盖 `&` 正向和 `&&`/`;`/`|` 反向) / TestExtractCommands_BackgroundPropagation (简单 + nested) / TestExtractCommands_BackgroundFieldPopulated (字段暴露 + 反向无误标) / TestAnalyzeDanger_BackgroundCommand_Annotated (含前台对照). baseline 391 → 390 - [x] `internal/syslib/bash.Node.HeredocBody` + `HeredocQuoted` + `HeredocStripTabs` + `HeredocTag` — heredoc 4 字段真 wire 端到端 (2026-04-20): RedirectionInfo 扩 4 字段 (Tag/StripTabs/Quoted/Body) 为 canonical 门面, extract.go NodeHeredoc 分支填入 + Target 回填自 Tag + IsStatic 重算. extractAllFromNode 对 unquoted heredoc body 递归解析 (Quoted=false 时 Parse body 抓嵌套 `$(rm -rf /)` 类绕过, 仿 Assignments.Value precedent). bash 包新增公共 helper `ResolveHeredocBody(redir)`: `<<-` 形式返回 strip 行首 tab 后的 body (匹配 bash 运行时), 普通 `<<` 原样返回, nil/非 heredoc 返回 "". bash_security.go `IsDangerousCommand` + `AnalyzeDanger` 用 ResolveHeredocBody 拼 checkStr, 对 body 做 dangerous-file 字面检测 (过去 `cat > out < 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) `Evolver` struct 加 `autoApproveReadOnly bool` + `New()` 赋值. (2) `Propose()` 审批逻辑前置 short-circuit: `autoApproveReadOnly=true && isReadOnlyEvolution(proposal) → approved=true` 绕过 approvalFunc. (3) `isReadOnlyEvolution` helper 刻意收窄, 当前**仅** `EvolveNewSkill` 符合 (只写 markdown, 不执行代码); `EvolveNewTool`/`EvolveOptimize`/`EvolveSelfAdjust` 即便标称 read-only 也不 auto-approve, 因其内容会执行代码或改运行时. (4) 新 `evolution_auto_approved` observer 事件与 `evolution_approved` 分离, 让安全审计可按事件名区分自动批准 vs 人工批准路径. (5) godoc 升级完整三段: 范围定义 / 安全人机工程权衡 / 其他进化类型永不放宽. 3 条 regression test (ApprovesSkillWithoutApprovalFunc / DoesNotBypassForNonReadOnlyTypes × 3 子 case / FalseKeepsHumanGate) 锁安全边界 (2026-04-19) - [x] `pkg/engine.Skill.AgentType` + `Skill.FilePath` + `Skill.Version` + `pkg/engine.AgentDefinition.MaxTurns` — Skill wire 四字段端到端 (2026-04-19 两轮, 从 C 档"可能残留"重分类为 B 档真 wire): **第一轮** (形式 wire, 被用户反向挑战"真实现吗"推翻): 三字段加 SetAgentRegistry/SetObserver setter + invokeFork 只填 AllowedSubAgentTypes + Invoke 发 skill_invoked. 用户核实暴露 AgentType godoc "fork 模式下使用的 Agent 类型" 只兑现 1/7 sub-claim, test 绿但锁的是形式 wire, 不是 godoc 承诺. **第二轮** (真 wire, 本条最终状态): 按 sub-claim checklist 补齐 — (a) cfg.AllowedTools = ResolveToolset(def, parentTools) 四层过滤 (parent ∩ def.AllowedTools → Background 收窄 → 去 DisallowedTools ∪ globalDisallowed) (b) DisallowedTools 随 a 生效 (c) cfg.Model fallback 到 def.Model (d) cfg.MaxTurns fallback 到 def.MaxTurns, 都 0 时兜底 10 (e) AllowedSubAgentTypes (原有) (f) skill 声明 AllowedTools 时与 def 取交集防逃逸 (g) 未命中发 skill_fork_unknown_agent_type (原有). SkillRegistry 加第三个字段 parentToolNames func()[]string + SetParentToolNames setter, engine.New 注入 `func() []string { return eng.tools.Names() }`. 三字段 godoc 升级双语完整 Wire 段 (AgentType 段枚举 (a)-(g) 全部). 11 条 regression test 覆盖全部 sub-claim (每条 test 锁一条 sub-claim, 防"一条 test 掩盖另一条缺失"): AppliesResolveToolsetWhenSkillSilent / SkillToolsIntersectWithDef_NoEscape / ModelFallsBackToDef / SkillModelOverridesDef / MaxTurnsFallsBackToDef / BothMaxTurnsZeroDefaultsTo10 / PopulatesAllowedSubAgentTypes / UnknownEmitsDiagnostic / EmptySkipsRegistryAndNoDiagnostic / InMemorySkill (file_path="" 显式保留) / FileSkill (路径+版本). baseline 401 → 397 (4 条实 drop: Skill.AgentType/FilePath/Version + AgentDefinition.MaxTurns side-effect 激活). 工作流教训: "先核实真实现再写 test" (feedback_verify_real_wire_before_test.md) 新增 ### C 档 (重评估, 可能删 / 可能 scanner 假阳性) — 5 条 ✅ 全部消除 (2026-04-25 L683 收尾) - [x] `pkg/evolve.LLMCallOpts.Temperature` — Temperature + TopP cross-provider passthrough 端到端 wire (2026-04-25, L683 子包 3 commit): `flyto.Request.{Temperature,TopP} *float64` 加字段 + `flyto.Float()` helper, 7 provider (anthropic / openai / minimax / gemini / ollama / lmstudio / openrouter) 全部接通 sampling 旋钮 (此前 7 provider 的 Config 与 Stream 一律不传, 是 day-1 产品功能缺失而非简单 wire). 纯 passthrough + 极小特例: 越界一律不在 flyto 层 clamp 由上游 4xx 自然冒泡 (业界共识 — Vercel AI SDK/LangChain/instructor 全 passthrough), 仅 1 个 in-Request 已知冲突预拦 (anthropic + NeedsThinking + Temperature != 1.0 / TopP < 0.95 → silent override 1.0 + `parameter_overridden` WarningEvent). **不**加 OpenAI o-prefix 检测 (server 4xx 已清晰, litellm drop_params 实证维护 burden 不值). 0 是合法 deterministic 值, `*float64` + `omitempty` 干净分离 nil/0. ADR rule of two: TopK/Seed/penalty 走 follow-up TODO 等真消费者驱动. evolve 串通: `LLMCallOpts.TopP` + `WithTopP` + `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` 加 `dangerousEnvVarPrefixes` map (LD_PRELOAD / LD_LIBRARY_PATH / DYLD_INSERT_LIBRARIES / IFS / BASH_ENV / PROMPT_COMMAND / PATH / PYTHONPATH / NODE_OPTIONS / GIT_SSH_COMMAND 等 Linux/macOS 后利用常见向量). `IsDangerousCommand` + `AnalyzeDanger` 遍历 `c.Assignments` 读 `assign.Name` (case-insensitive) 拦截 env-var 前缀注入 (`LD_PRELOAD=./evil.so curl ...` 过去会绕过所有检查因 ExtractCommandName 剥前缀, 现在精确抓). `DangerInfo.Reason` 含具体 env-var 名, `Pattern` 含完整 `NAME=VALUE` 前缀供 TUI / 审计渲染. 顺带 drop `CommandInfo.Assignments` (同字段路径下 selector read). 14 条 regression test (9 dangerous / 3 benign 不过拦 / 1 case-insensitive / 1 AnalyzeDanger 结构化) (2026-04-19) - [x] `pkg/context.BundleKey.ModelFamily` — verify 为 struct-as-map-key 假阳性 (BundleKey 作 `map[BundleKey]PromptBundle` 键 + `key == (BundleKey{})` 值比较, 字段参与 hash/eq 但无 SelectorExpr read). 真 wire 为 `(k BundleKey) String() string` 方法, 返回 `"/"` 用于日志 / 错误消息 / observer 事件 payload — 字段在 String() 内被 SelectorExpr 读, 同时给运维人类可读输出. Table-driven regression test 5 case (default / industry / zero_value / partial_empty_family / partial_empty_scenario) (2026-04-19) - [x] `pkg/context.BundleKey.Scenario` — 同上, 随 BundleKey.String() 一并 wire (2026-04-19) - [x] `pkg/context.MessageGroup.Index` — verify 为 write-only (非 test 代码字段被多处写, 0 处读). 真 wire: `compact_token_gap_stride` observer 事件扩 `dropped_group_indices []int` payload, Compressor 在 stride 分支 slice 重建**之前**快照每个被丢 group 的 Index 数组. 运维 grep 事件能看到*哪几个* API-round group 被削 (如 `[1, 2, 3]`) 而非仅 count. regression test 扩断言: 有 >=1 个 index / 无 0 (preamble 保留不变量) / len(indices)==dropped_groups 同源不变量 (2026-04-19) ### 2026-04-23 validator 子包引入 ✅ 本 session 5 commit 内消除 Commit 2 新增 `core/pkg/validator/` 子包的 RuleValidator 后, scanner 检出 8 个 validator 字段 dead (`DiffInput.SourceTool` + `Verdict.{Approved, Details, PolicyVersion, Reason, Score, Severity, ValidatorName}`). 属 "设计意图先行, 实现未连" — 接口 + 数据类型先落地, 参考实现逐步真 wire: - Commit 1 (`30639fb`) 接口 + 数据类型: 引入 8 字段, 零消费 - Commit 2 (`e6239c2`) RuleValidator: 只 set 不 read, baseline 212 → 220 - Commit 3 (`ca6309e`) LLMValidator: `renderPrompt` 读 SourceTool + `parseLLMVerdict` normalise 读 Severity, drain 2, baseline 220 → 218 - Commit 4 (`8b1d556`) CompositeValidator: `childDetail` 读 Approved / Score / Details / Reason, drain 4, baseline 218 → 214 - Commit 5 (本 commit) ValidatedTool Decorator: `formatBlockedOutput` 读 ValidatorName + PolicyVersion, drain 2, baseline 214 → 212 **Exit criteria 达成**: baseline 回到 212, 8 字段全部真 wire, 无残留 validator 字段 tracked debt. `pkg/validator/` 子包 (Validator 接口 + RuleValidator / LLMValidator / CompositeValidator 3 参考实现) + `pkg/tools/builtin/validated_tool.go` (Decorator + VerdictSink hook) 一套基础设施就绪, 为消费层 P1 L434 ML 验证器接入 (platform 接外部 ML backend) 和 P1 L435 熔断器 (状态机消费 VerdictSink 信号) 提供 core 接口层. 这 2 条 P1 仍在 open 状态, 核心已降; 待 platform 层完成. ### 2026-04-24 nil Validator 严格化 + AlwaysApprove 显式 opt-out (C1 / core 安全链 enforcement) 候选 A (platform 集成 Validator + Breaker 端到端) 启动前, 先把 core 层的"显式审批不能隐式继承"契约落到编译/构造期可见 — 产品拍板: platform 装配框架无默认, industry 不 wire 就是不装. 为避免 "industry 以为开了审批实际忘 wire" 的静默安全假象, core 层追加两件强约束: - `pkg/validator/always_approve.go` 新增 `AlwaysApprove{}`: Validator 接口显式 opt-out 实现, 忽略 diff 永远 Approved=true, ValidatorName="always-approve" 作哨兵标识供审计 dashboard 过滤. - `pkg/tools/builtin.NewValidatedTool` 构造期对 nil Validator / nil extractor panic (而非首次 Execute 时静默 nil-deref). Industry 刻意 opt-out 必须显式传 `validator.AlwaysApprove{}`, code review 能抓到, 日志里 ValidatorName 能看到. 4 新 test (`TestAlwaysApprove_*` × 4 + `TestNewValidatedTool_Nil*_Panics` × 2 + `TestValidatedTool_WithAlwaysApprove_PassesThrough`) 全绿, baseline 212 不变 (AlwaysApprove 空 struct 无字段). 这条是 Agent Teams 3 角色 review 里"质疑"角色的 TOP#2 观察 — 独立于"装配框架做不做"决策, 属于 core 层 enforcement 职责, 无论 platform 怎么做都应落地. 消费层 L434/L435 P1 核心前置已就绪 (core 接口 + Decorator + 熔断器 + 显式 opt-out + 启动期 fail-fast), platform 层装配 / 观测 / gRPC 暴露继续 C2-C4. ### 2026-04-24 platform/common safetychain 装配层 (C2) 产品拍板抽象方向 (行业 platform 自选 LLM/ML backend + 熔断作用域) 后, 在 `platform/common/safetychain/` 公共包落装配框架. 无默认, 显式 opt-in; Go 行业可直接 import, C# logistics 走 gRPC (C4). - **`Assemble(inner, v, extractor, scope, sink) tools.Tool`**: 一行装配 inner Tool + Validator + (可选) breaker scope + (可选) 观察 sink, 返回可注册的 tools.Tool. Validator / extractor 为 nil 时 core 构造期 panic (C1 保证), industry 显式 opt-out 必须传 `validator.AlwaysApprove{}`. - **`BreakerScopePolicy` (func type)**: 决定每个 Tool 要哪个 breaker. 刻意选函数类型不是 interface — 质疑角色认为 3 个无状态实现抽 interface 是 YAGNI, 行业想自定义直接写同形状函数. - **3 个内置工厂**: `NoOpScope()` (不挂 breaker) / `DestructiveOnlyScope(cfg)` (destructive tools 共享 1 breaker, 对齐 "写路径整体保护" 语义) / `PerToolScope(cfg)` (每 tool 独立 lazy 分配). - **`BreakerRegistry`**: 每个工厂返回 `(policy, registry)` 双元组, registry 给 C3 admin 端点消费 (`Snapshot() map[string]circuitbreaker.State` + `Names() []string` sorted). - **fan-out 顺序契约**: Assemble 同时拿到 industry sink 和 breaker sink 时, industry sink 先 (记录样本到 store 不丢), breaker sink 后 (推进状态). C3 观察 store 后消费 verdict 流水, 这个顺序保证审计不缺. 16 test (`TestAssemble_*` × 6 + `TestFanOut_AllCombinations` + scope 系列 × 9) 全绿. core baseline 212 不变 (C2 只在 platform/common 加代码, 不碰 core). 消费层 L434/L435 现在 core + common 装配层就绪, 剩 C3 观测端点 + C4 gRPC 暴露. 端到端"industry 选 backend + Assemble + gRPC 暴露"链路逐步闭合. ### 2026-04-24 platform/common admin 观测端点 (C3) 安全链运维可见性: `VerdictStore` 接口 + `RingStore` bounded 内存实现 + admin HTTP 2 新端点. - **`safetychain.VerdictStore`** 接口: `Record(tool, verdict)` (签名对齐 `builtin.VerdictSink` 可直接作为 `Assemble` sink 不需 wrapper) + `Snapshot() []VerdictRecord` 返拷贝. 实现方并发安全. - **`safetychain.RingStore`**: bounded 内存环形缓冲, O(1) Record 热路径无分配. 容量显式参数无默认 (各行业 Warn 流量差异大), clock 可注入 (复用 `circuitbreaker.Clock`, 测试 deterministic). - **`admin.New(version, verifier, opts ...Option)`**: 改 variadic 向后兼容. 新 `WithSafetyChain(store, registry)` option, 任一 nil 视作不启用 (配对契约阻止接线错漏). - **2 新端点 `GET /admin/safetychain/{verdicts,breakers}`**: 仅 WithSafetyChain opt-in 时挂载, 默认 404. 鉴权和 `/admin/tenant` 同级 (verifier 非 nil 时套 auth middleware, Verdict.Reason 可能带运营敏感信息). `authed()` helper 收敛鉴权策略. - **输出形状**: verdicts → `[]VerdictRecord{Timestamp,ToolName,Verdict}` JSON; breakers → `[{name,state}]` 按 name 排序 JSON (dashboard 轮询 diff 稳定). 空 snapshot 返 `[]` 非 `null` 让下游无需 null check. 11 test 全绿 (5 RingStore + 6 admin 端点, `-race` 通过). core baseline 212 不变. 端到端 consumer 示例 CHANGELOG 里写了. 消费层 L434/L435 现在 core + common (装配 + 观测) 全就绪, 剩 C4 gRPC 暴露给 C# logistics. ### 2026-04-24 platform/common 安全链 gRPC (C4) — 候选 A 端到端闭合 给 C# logistics (以及未来 Go 行业) 一条原生 gRPC 通路读安全链状态, 和 C3 admin HTTP 同源双面暴露. 沿用 HealthService 已经走的 proto + genpb + grpcapi 模式. - **proto**: `platform/common/internal/api/grpc/proto/safetychain.proto`, 2 RPC (`ListVerdicts` / `ListBreakerStates`), csharp_namespace 和 go_package 对齐 health. - **gen**: 一次性 install `protoc-gen-go` + `protoc-gen-go-grpc` 到 `$GOPATH/bin` 后 protoc 生成到 `gen/` (共享 `package genpb`). - **server**: `grpcapi.SafetyChainServer{Store, Registry}` 依赖注入, 与 admin.WithSafetyChain 消费同一对 VerdictStore + BreakerRegistry. - **cmd/common wire**: 默认构造 `RingStore(1024) + NoOpScope registry`, 同时挂 admin HTTP (若 --http-addr 非空) + gRPC. 默认启用让 C# stub 能即时对接. 设计决策: Verdict.Details 刻意省略 (proto3 无 map 等价); Severity / State 用 string 不用 enum (第三方 Validator 前向兼容); nil Store / Registry 返空列表不报错 (观测端点不把"没东西"和"服务挂"混淆). 6 test 全绿含 bufconn 端到端 gRPC round-trip 作为 C# stub 互操作代理证据. core baseline 212 不变. **候选 A 端到端闭合**: 整条 "Agent → staging → Validator → ValidatedTool → CircuitBreaker → WMS API" 安全链 core + common (装配 + HTTP 观测 + gRPC 暴露) 全部就绪. 剩的工作落到 industry platform 侧: 选 LLM / ML backend, 装配 Tool 到 engine 消费层, 把真实 Verdict 流水填进 common 的 `VerdictStore`. L434/L435 P1 核心路径已清晰, 可落 platform 层实施时直接消费. ### 2026-04-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.CreatedAt - `Session.ShadowTable` — 外部 consumer 在 SQL 里引物理表名, decorator 与 EnforceSessionFilter 也可读取. core makeCloser 走 trackedSession.shadowTable 闭包不读 sess.ShadowTable - `Session.DB` — 外部 consumer 经 sess.DB.ExecContext / QueryRowContext 跑 SQL. core 内部经 opener.db 走, 不通过 session 暴露 均为外部 SDK / 平台层 consumer 的 API surface, scanner 视野不覆盖. 无强行加 internal reader 路径 — 任何强加都是 "走捷径过 scanner" (memory `feedback_scanner_pass_vs_intent_done.md`), 设计本身就是 opener 持单源真理 + Session 是 immutable snapshot. **baseline 216 → 220 (+4 合法 tracked debt)**, 与 staging 引入时同模式 (212 → 216 +4 合法). 不计入应 drain 指标. ### 2026-04-24 staging 子包引入 (commit 1) — 后续 3 commit 内消除 staging 子包 (`core/pkg/staging/`) 实现 "Agent 决策 → staging → 审批 → 生产" 链路中 "staging" 一环的状态机与 Record 契约. commit 1 按 "设计意图先行 tracked debt" 模式 (memory `feedback_design_intent_first_tracked_debt.md`) 只落类型 + 状态机, reader 在 commit 2 (Store 接口 + InMemoryStore) 与 commit 3 (Engine 混合控制状态机) 里进来. baseline 212 → 223 (+11 字段). | 字段 | 将在哪个 commit 被 reader 消费 | |---|---| | `Record.ID` | commit 2 ✅ (InMemoryStore.Stage 分配 + Get/List 返回) | | `Record.SessionID` | commit 2 ✅ (Stage 校验, List/ListBySession 过滤) | | `Record.State` | commit 2 ✅ (Update/Mark 方法读源状态 + 写新状态) | | `Record.Diff` | commit 3 (Engine.ValidateTech/ValidateBiz 调 `Validator.Validate(ctx, r.Diff)`) | | `Record.CreatedAt` | commit 2 ✅ (List SortFunc 按 CreatedAt 升序) | | `Record.UpdatedAt` | commit 2 ✅ (ListStuck 过滤 + SetUpdatedAtForTest 写 + 所有 Update/Mark 写) | | `Record.Metadata` | commit 2 (Store 透传存取), commit 3 (DependencyGuard 读 hint) | | `Record.TechVerdict` | commit 3 (Engine.ValidateTech 写后 Engine test 断言 + platform 侧 audit dashboard 读) | | `Record.BizVerdict` | commit 3 (Engine.ValidateBiz 写后 Engine test 断言 + platform 侧 audit dashboard 读) | | `Record.ExecutionError` | commit 3 (Engine test 断言 MarkFailed 后 reason 保留) | | `Record.ExecutionProof` | commit 3 (Engine test 断言 MarkExecuted 后 proof 保留 + 幂等 first-write-wins 验证) | | `Query.SessionID` / `States` / `Since` / `Limit` | commit 2 ✅ (InMemoryStore.List 过滤实现) | **commit 2 后进度 (2026-04-24)**: 9/11 drain, baseline 223 → 218. Commit 2 新增 4 条 dead (`TechVerdict / BizVerdict / ExecutionError / ExecutionProof`), 预期在 commit 3 Engine test 断言写后字段值时收割. Commit 2 后 staging 包净 dead 6 条: `Record.Diff` (原 commit 1) + `Record.Metadata` (原 commit 1) + commit 2 新增 4 条. **commit 3 后最终进度 (2026-04-24)**: 2 条 drain (`Record.Diff` 由 Engine.ValidateTech/Biz 读, `Record.Metadata` 由新增 `TenantDenyGuard` 内置实现读), baseline 218 → 216. 4 条 (`TechVerdict / BizVerdict / ExecutionError / ExecutionProof`) 仍 dead — 最初预估 "Engine test 读字段值就能 drain" 错, scanner 只扫非 test 代码的 reader, _test 里 read 不算. **4 条合法 tracked debt 终态分类**: 按 memory `feedback_exported_field_delete_needs_review.md` 模式, 这 4 条是"外部 audit dashboard 消费, core module 无内部 reader"合法形态: - `Record.TechVerdict` / `BizVerdict`: 平台层审计 UI 读 Severity/Reason/ValidatorName 展示"哪一层拒绝/为何" - `Record.ExecutionError`: 平台层 watchdog / 审计追溯读生产失败原因 - `Record.ExecutionProof`: 平台层审计链完整性验证 (核对 WMS 返单号与 staging 记录一致) 均无 core 内部消费路径. 走 `D-A 档` 路径 (不 drain, 保留 exported 字段 + 进 tracked 但不 drain 计划). **Exit criteria 达成** (调整: 原目标 ≤ 212 不现实): staging 包 core-level scanner-clean, 剩余 4 条是 exported 字段 cross-module 外部消费 (platform 审计侧), 符合 memory 所述 "exported 字段无 consumer 是 scanner 视角局限" 合法形态. ### 2026-04-20 baseline 386 sweep 结论 (D 档候选清单, 待路线决策) alpha.7/8 drain 完 A/B/C 档 26 条 (baseline 438 → 386, 净 -52 含副作用激活). 本次 sweep 对 386 剩余按 `tag` + struct 归约的二次 triage 结果: **事实层**: - 193 / 386 条**有 json tag**: 按 `feedback_exported_field_delete_needs_review` 默认视作第 3 类 (parser-to-marshal-to-SDK/plugin downstream, scanner 视野不覆盖外部消费者). **不列入 drain, 不删**. - 193 / 386 条**无 tag**: 真候选池, 按 struct 归约约 20-25 个主题 (见下). **D-A 档 2026-04-20 重分类结论**: 初版列 8 主题 / ~36 字段, 按 push / pull / callback 三形态 verify 后 (见 `docs/api-reference.md` 新章节 "API 消费形态"), **7/8 条是合法外部 API 表面, 消费者在 scanner 视野外, 不是实现债务**. 真 drain 对象仅 `engine.SessionStats` 一条 (已完成). 其余 7 条保留, 不再列入 "应 drain" 队列, 原因见每条后缀标注. **D-A 档 (已分类, 1 真债务 + 7 合法外部 API)**: - [x] `engine.SessionStats` (TurnCount/InputTokens/OutputTokens/CostUSD/MessageCount) — 真 wire 为 pull + push 双路径 (2026-04-20): `Session.Stats()` 已有 pull API 保留 (消费者随时读快照), 新增 push 通道 `session_cost_threshold_crossed` observer 事件. trackEvents 尾部 30 行代码抽取为新方法 `applyTurn` (方便测试, 也解耦 emit 逻辑). 引入包级常量 `costThresholdsUSD = []float64{1,5,10,50,100}` 和 helper `crossedCostThresholds(last, cost) → []float64` (返回 half-open 区间 `(last, cost]` 内的升序档位, 支持单轮跨多档). Session 加 `lastCostThresholdEmitted float64` guard 防同档重复 emit. 锁策略: 锁内构造 stats 快照 + 组 payload (字段经 `stats.CostUSD / TurnCount / ...` SelectorExpr read, 这正是把 5 字段从 scanner "声明未读" 升为 "运行时真读" 的依据), 释放锁后再 emit (对齐 Close() 的 unlock-then-emit 模式, 避免持锁跨消费者回调). SessionStats struct godoc 升级双语, 新段落明确两条消费路径的各自定位 + 同源不变量 ("两路径不重叠: 要快照走 pull, 要成本告警走 push"). 5 条 regression test 一 sub-claim 一 test: Stats 5 字段多轮累加准确 / 首次跨档 payload 完整 (7 key 全 assert) / 已跨档同区间不重发 / 单轮跨多档升序每档一次 ($0→$15 发 $1/$5/$10) / 无跨越无事件 (0.9 累计不 emit). baseline 386 → 381. - [~] `engine.PlanApprovalEvent` (SessionID/Plan/FilePath/Steps/Approve/Reject) — **同步回调 (callback) 形态, 合法外部 API 保留** (2026-04-20 重分类): 经 `ApprovalPolicy.RequestApproval(ctx, event)` 传递, Approve/Reject 是消费者调用的回调函数字段. `ApprovalPolicy` 接口由 CLI 终端审批 / SaaS 企业审批工作流 / 测试 NoopApprovalPolicy 等外部消费端实现, scanner 视野不含外部 policy. 见 `docs/api-reference.md` "API 消费形态" 章节形态三. - [~] `engine.CheckpointSuggestedEvent` (Input/RiskPattern/RiskReason/ToolCallID/ToolName) — **订阅 (push) 形态, 合法外部 API 保留** (2026-04-20 重分类): 实现 `flyto.Event` 接口 (`EventType() string` 方法), 走 `Engine.Run` 返回的 `<-chan flyto.Event` 流. 消费者 `for evt := range ch` + type-switch `case *CheckpointSuggestedEvent` 读字段. scanner 视野不含外部消费端. - [~] `permission.DenialStats` (ConsecutiveDenials/LastDeniedInput/LastDeniedTool/TotalDenials) — **调取 (pull) 形态, 合法外部 API 保留** (2026-04-20 重分类): 由 `DenialTracker.Stats() DenialStats` 返回, 消费者 (CLI / SaaS / 测试 harness) 主动调读字段, 和 SessionStats 同构 pull API. scanner 视野不含外部 pull 调用. - [~] `permission.ClassifyResult` (DurationMs/Stage/Thinking/Usage) — **调取 (pull) 形态** (2026-04-20 重分类): `Classifier.Classify(ctx, req) (*ClassifyResult, error)` 返回值, 消费者主动调. 合法外部 API. - [~] `engine.InboxMessageEvent` (Data/Meta/ToolUseID/Type) — **订阅 (push) 形态** (2026-04-20 重分类): 实现 `flyto.Event`, 走 Engine.Run Event channel. 消费者经 `case *InboxMessageEvent` 读. 合法外部 API. - [~] `flyto.TeammateMessageReceivedEvent` (From/To/Type/MessageID/PayloadSize) — **订阅 (push) 形态** (2026-04-20 重分类): Agent Teams peer-to-peer 事件, 实现 `flyto.Event`. router consumer 走 channel + type-switch. 合法外部 API. - [~] `engine.ElicitationField` (Description/Required/Title/Type) + `ElicitationRequest` (Fields/Message/ServerName) — **同步回调 (callback) 形态** (2026-04-20 重分类): 经 `ElicitationHandler` 接口 (NoopElicitationHandler 已实现, 生产 handler 由 CLI/SaaS 消费端提供) 消费. 合法外部 API. **D-A 档分类结论**: 上述 `[~]` 标记条目**不列为实现债务**, 不计入应 drain 指标. 它们在 baseline 381 里占据 35 条, 保留于 scan-baseline.json 意为 "scanner 知道这些字段在本 module 无内部 consumer, ratchet 保证未来新加的类似位置字段不会悄悄漏掉", 不是"设计未连". 未来新增外部 API 载体时 baseline 会增长, 按 `docs/api-reference.md` "API 消费形态" 章节判据分类即可, 不走 drain 路径. **D-B 档 (特性框架 wire, ~12 主题 / ~70 字段) — evolve + 内部结构**: - [x] 缓存族 3 个 struct 真 drain / 归档 (2026-04-20): 按用户 4 问法则 (接口有消费? 必要? 外部? 内部加?) 分三条路径走完: - **`engine.FileCacheEntry` 6 字段真 drain**: Path 消除"lruEntry.path 双重存"冗余 (RecentFiles 改读 entry.Path, 删 lruEntry 里那份, FileCacheEntry.Path 成 single source of truth); ContentHash/Size/LineCount/ReadAt/WasModified 通过新增 `FileStateCache.Info(path) (FileCacheEntry, bool)` pull API (返回值副本, 外部消费安全) + reminders.go CheckFileModifications 升级消费元数据 (reminder 文本带 "at-read-time: N bytes / M lines / hash XX, read T ago" + WasModified 已标记提示), 从"声明未读"激活为真 consumer read. FileChangeChecker 接口同步加 Info 签名. godoc 双语解释 6 字段消费路径 (pull via Info/Get/Peek). - **`engine.CacheStats` 3 字段 (Entries/MaxSize/Evictions) `[~]` 归档**: 已在 docs/api-reference.md 列为 pull API (和 SessionStats/DenialStats 同列), godoc 升级双语写清消费形态 "调取 pull, 消费者主动调 FileStateCache.Stats() 读字段, scanner 视野内无内部 reader 是期望状态, 不强加内部 reader 过扫描器". 不减 baseline. - **`tools/builtin.FileStateCacheEntry` 5 字段 `[~]` 归档**: form 3 callback (FileStateCacheRecorder.RecordState 由外部实现接收), 和 engine.PlanApprovalEvent/ElicitationField 同构. godoc 升级双语讲清字段给外部 Recorder 消费的契约 (ContentHash/Size/LineCount/ModTime/IsPartialView 各自用途). 不减 baseline. baseline 367 → 361 (-6, FileCacheEntry 全部 drain). 下次跑扫描器时 8 条保留 (CacheStats 3 + FileStateCacheEntry 5) 显示归档分类,不算债务. - [x] `pkg/evolve.FeedbackRecord` (Entity/Metric/Value/Confidence/Timestamp/Meta) — 5 字段 drain 走**类型合并**路径 (2026-04-20): 按 4 问审判发现 evolve 包内同时存在两个字段完全相同、语义重合的 struct — `FeedbackRecord` (ParameterEvolver.Propose evidence) 和 `Feedback` (FeedbackChannel.Query 返回的 KPI 反馈, reflector_impls.go 内部真消费 `fb.Entity/fb.Metric`). "同一语义两份类型"是典型冗余设计, scanner 把 FeedbackRecord 5 字段报 dead 因为消费端早就并行用 Feedback 了. 合并: 删 FeedbackRecord, `ParameterEvolver.Propose(ctx, key, []Feedback)` 签名直接收 Feedback, ProposerFunc / DefaultParameterEvolver / examples/evolve_closed_loop 全部链路改用 Feedback. 合并保留更短更 idiomatic 的 Feedback 名, 删掉 "Record" 累赘后缀. 所有 evolve test 继续通过. baseline 372 → 367. - [x] `internal/syslib/bash.HeredocInfo` (Quoted/StripTabs/BodyStart/BodyEnd) — 4 字段真 drain 端到端 (2026-04-20): 按用户 4 问法则 (接口有消费? 必要? 外部? 内部加调用?) 重新审判, 这 4 字段不是一种债务: Quoted/StripTabs 是**冗余** (parser 自己从源码 token op/parseHeredocDelimiter 算, 是 source of truth; PreprocessHeredocs 的副产物无人消费), 删字段 + 对应 test 切换到 `Parse(input)` 验证 AST 侧 `HeredocStripTabs/HeredocQuoted` 真语义. BodyStart/BodyEnd 是**设计意图未 wire** (之前存的是 result.Len() 即 processed text 近似偏移, 作者注释自己标"近似"): 修成真原文 byte offset + 端到端 wire HeredocInfo → `ast.Node.HeredocBodyStart/End` → `RedirectionInfo.HeredocBodyStart/End` → `DangerInfo.HeredocBodyStart/End` → `checkpoint_suggested` observer 事件 payload (`heredoc_body_start` / `heredoc_body_end` key, 零值归因非 heredoc 场景时跳过). Reason clause 同时带 "source bytes N-M" 文本让人类可读 log 也承载位置. 回归 test 一主题一 sub-claim: `TestPreprocessHeredocs_BodyStartEndAddressesOriginalSource` 4 case (单 heredoc / 空行 body / 多 heredoc 同行 / strip-tabs 变体) 锁 `source[Start:End] == Body` 不变量; `TestAnalyzeDanger_HeredocBodyPos_LocatesSource` 端到端断言 `DangerInfo.HeredocBodyStart:HeredocBodyEnd` 切回 cmd 能命中 authorized_keys + Reason 含 "source bytes". advisor 审判发现 "快扫 vs 严谨"叙述错 (两侧都是完整词法分析, 只是历史独立编写), pin consumer 用 observer payload 是真 user-visible 面 (非 formal wire). baseline 376 → 372. - [x] `internal/syslib/bash.CommandInfo` (InPipeline/InSubshell/Operator/PipePosition/Position) — 5 字段真 wire 端到端 (2026-04-20): 和 alpha.8 `Background` 字段同系 (bash 语法上下文标记), 同构走"安全决策面激活"路径. 先 verify extract.go ctx 传播语义 (Position 在 NodeSubshell 内 reset 从 0 算 / NodeList+NodePipeline 累加 outer+inner; PipePosition 必须配合 InPipeline 查因非管道命令零值冲突; Operator 第一个命令从父 ctx 继承最外层为空), 按实际语义升级 5 字段双语 godoc 消除"边界含糊"嫌疑. `amplifyBackground` 扩展重命名为 `amplifySyntaxContext` 承接全部 6 字段 (含 Background), 一处集中构造 clause + pattern 前缀 (结构性字段 "(subshell) " / "| " / "& " 叠加, Position/Operator 走 Reason clause), 用 "; " 拼接, 避免各字段分散拼错. 所有 6 处调用点 (AnalyzeDanger) 从旧名更新, 语义向后兼容 (Background 行为不变). 5 sub-claim 一 test + 1 regression: InSubshell / InPipeline+PipePosition / Operator / Position / Multiple 组合 (`(true && rm -rf /)` 同时触发 Operator/InSubshell 验证 "; " 连接和 "(subshell) " Pattern 前缀不互相覆盖). Counter case 全覆盖 (standalone 不得有对应标注防假阳性). advisor 明确要求 "verify from code not godoc" — ship 前核实 3 个关键问题代码答案才写 godoc 和 helper, 不脑补. baseline 381 → 376. - [x] `internal/syslib/git.Info` 5 字段 drain (2026-04-20): 按 4 问法则重审, 本包从 day 1 零消费者 — speculative engineering 残留 (上游 claude-code-mod 同步存在双实现, TS 原版 Claude Code 根本没 git metadata API). 真诚分层确立: syslib/git 作"引擎内部底层 git 工具 (裸 exec.Command)"定位, 不建 builtin/git tool (对模型冗余, bash tool 够), pkg/context 通过 import 直接消费. 删 `Info.Root/DefaultBranch/Dirty` + `FindGitRoot/GetDefaultBranch` 函数 (真无消费场景, Dirty 与 Status 语义冗余); `pkg/context/context.go` `detectGitInfo` 替换为 `syslib/git.GetInfo` 调用, IsRepo/Branch/Status 从 dead 变真消费 (注入 system prompt `- Git repo: yes / - Git branch: X / - Git status: clean|N file(s) changed`). `GetBranch` 改用 `git branch --show-current` (detached HEAD 返回 "" 比 rev-parse 字面 "HEAD" 更易消费). DESIGN.md sandbox 白名单 + architecture.md 包描述同步. baseline 361 → 356. **后续纠正**: 原 Commit A 同时加了 `Run(ctx, cwd, env, args...)` 通用原语期望 sync_git 下沉, 但读 sync_git 发现它走 `execenv.Executor` DI 层 (ClassMemoryGit sandbox 路由), 用不了裸 Run — Run 撤回 (见 L695b). - [x] `internal/syslib/git.Run` 原语撤回 (2026-04-20): Commit A 的形式错误自我纠正. Run 本想作为 sync_git 4 runGit 变种的下沉目标, 但 sync_git.go:199-205,402-422 走的是 `g.executor.Command(ctx, execenv.Spec{Class: ClassMemoryGit, ...})` — execenv.Executor DI 层, 不是裸 exec.Command. sync_git 走 ClassMemoryGit 让云端 sandbox backend 决策路由 (system pod 或 tenant VM), 下沉到裸 Run 会破坏 sandbox 路由抽象. 同时 context.detectGitInfo 只用 GetInfo 不用 Run. Run 零真实消费者, 未来 sandbox M2 "预留" 是 speculative — 和本轮全程反对的"存利息"一个模式. 撤回: 删 `Run` + `mapToEnv` + 4 个 `TestRun_*` + `"context"` import; DESIGN.md 白名单移除 "Run 原语" 字样, architecture.md 包描述同步. baseline 不变 (Run 是函数非字段). 教训: 先枚举真消费者路径 (裸 exec vs DI 层) 再设计原语, 别凭命名相近就假设下盘. - [x] `syslib/git.ValidateRef` 贯通到 `pkg/memory.NewGitSyncAdapter` 构造期 (2026-04-20): 独立安全债务, 与 Run 原语撤回同一 session 清理. sync_git 的 `g.remote` / `g.branch` 两个用户配置值进入 11 处 git argv (fetch/push/rebase/reset/pull/init/log/ls-files/add/commit/diff), 原无构造期校验 — remote 以 "-" 开头即 flag injection (如 `--upload-pack=evil` 让 git 执行远端任意代码), branch 含 ".." 即路径遍历 (git 内部 ref 解析解到 .git/refs/../.. 越狱). 修复: export `validateRef` → `ValidateRef` (GetDiff 内部调用点同步), NewGitSyncAdapter defaulting 块后一次调 `gitlib.ValidateRef(remote)` + `gitlib.ValidateRef(branch)`, bad 值 panic (风格对齐既有 nil Executor panic). 加 2 test: `TestNewGitSyncAdapter_RejectsInjectedRemote` (`--upload-pack=evil` remote) + `TestNewGitSyncAdapter_RejectsPathTraversalBranch` (`feat/../etc` branch). baseline 不变 (纯参数校验, 无字段触及). - [x] turn 边界族 C1: `TurnStartEvent.ContextWindowTokens` + `TurnEndEvent.MaxTokens` 经 bridge serializer 透传 (2026-04-20): 按 4 问法则分类, 真消费路径是 engine emit → flyto.Event channel → bridge EventSerializer → SSE → 外部客户端 (TUI / Web UI). event_serializer.go 的 anonymous struct literal 是 choke point — 原 TurnStartEvent payload 只序列化 `{turn, model}` 丢 ContextWindowTokens, TurnEndEvent payload 只序列化 `{turn, input_tokens, output_tokens, cost_usd}` 丢 MaxTokens, 两个 engine 已 emit 的字段被 serializer 静默吞掉. 修复: 两 struct literal 加 `ContextWindow` / `MaxTokens` 字段 (JSON key 分别 `context_window` / `max_tokens`, omitempty 省零值避免对旧消费者添噪声). 新建 `pkg/bridge/event_serializer_test.go` 锁 JSON shape: 4 test (shape 断言 + omitempty 验证) × 2 event. 语义: ContextWindow 透传模型完整 context window token 数 (由 provider.Models() 提供, 消费层可渲染预算条不硬编码模型表); MaxTokens 透传本轮发给 provider 的实际 output 上限 (FastMode / reasoning 降档后的值, 消费层据此判断回复截断是命中上限还是自然停止). 教训: 新增 event 字段必须同步修 serializer anonymous struct + shape test, 否则被 bridge choke point 吃掉. baseline 356 → 354. - [x] **子 agent 观测链路端到端 wire** (2026-04-20 本 session 主题, 3 commit 端到端闭环): 父 agent 派出的子 agent 当前对引擎是黑盒 — worktree 模式挂牌不开工 (sa.Cwd 设了但 BashTool/FileEditTool 工具箱继承父 cwd), 子 agent 事件在 5 处 spawn 路径 (agent_executor sync/background/worktree, engine.go:1938 skill fork, engine.go:4682 记忆提取故意静默, team.go:378 worker, dream.go:456 dream) 全 drop 或只读少数事件类型, SubAgent.StartTime/EndTime/Description 从未 emit. 产品后果: TUI 树形展示中间节点空白 / 平台 session 成本漏计 / 审计流水缺失 / worktree 隔离是谎言. wire 方向 3 commit: - **commit A** ✅ (ctx cwd 透传, 2026-04-20 commit 914df89): pkg/tools 新增 `WithWorkdir(ctx, path)` / `WorkdirFromContext(ctx) string` 工具 ctx helper (type-hidden key 防外部构造污染); BashTool/BashToolBackground/FileEditTool/GitignoreTool 4 个 cwd-aware 工具 Execute 开头先读 ctx 覆盖, fallback 构造期 cwd; SubAgent.runLoop 每工具调用前注入 ctx = WithWorkdir(ctx, sa.Cwd); 4 sub-claim 测试全绿 (pwd 输出真落到 override, symlink guard 用新 cwd, Gitignore 三级 fallback, 空 ctx 回退不回归). baseline 352 → 351 (SubAgent.Cwd 从 dead 变 live wired). - **commit B** ✅ (事件回流 + 生命周期, 2026-04-20): pkg/engine 新增 SubAgentStartEvent (ID/Description/Cwd/Model/StartTime) + SubAgentEndEvent (ID/Duration/Status/Result/Error) + `event_emitter.go` (WithEventEmitter/EventEmitterFromContext, 私有 key type 防外部伪造); engine 主工具派发前 WithEventEmitter 包 ctx 用非阻塞 select 丢弃兜底防死锁; sa.runLoop 读 ctx emitter 一次, defer emit End, for-select 头部 emit Start, 中间 13 处 `ch <- &SubAgentEvent{SubAgentID: sa.ID, Inner: X}` 统一替换成 `pushEvt(X)` 辅助 — pushEvt 把 bare X 送到 sa 自己 ch (RunSync/RunSyncWithCallback 本地消费者 type-switch 匹配 *TextEvent 等, 顺带修潜在 silent bug — 原包装后 RunSync 从没收过 TextEvent), 同时在 forward path active 时 emit 包装 SubAgentEvent 到父 Run channel. SubAgentConfig.SilentEvents 兜底 flag, runMemoryExtraction 显式设 true (后台静默任务不污染用户事件流). 7 sub-claim 测试全绿 (Start/End/Inner/Silent/NoEmitter/Concurrent/Truncate + EventEmitter 单元). baseline 351 → 358 临时上升 (+7: SubAgentStartEvent 5 + SubAgentEndEvent 5 - wire 掉的 Description/StartTime/EndTime 3), 这 11 条是 tracked transient debt 本 session Commit C 下一刀 wire 完. scan-baseline.json 已 regenerate 接纳. - **commit C** ✅ (bridge 序列化 + 文档, 2026-04-20): 重构 event_serializer.go 把 17 case switch 提取成 `eventPayload(evt) (type, payload)` helper, Serialize 方法调用 helper 再包上 id/timestamp/sessionID — 让 SubAgentEvent 能递归取 Inner 的 (type, payload) 做扁平合并. 新增 3 case: SubAgentStartEvent → `subagent_start` {subagent_id, description, cwd, model, start_time_ms}; SubAgentEndEvent → `subagent_end` {subagent_id, duration_ms, status, result, error}; SubAgentEvent → `subagent_event` {subagent_id, event_type, ...inner_payload} 扁平 shape (消费端一次 JSON.parse 即可读到归属 + 业务字段, 无 nested unwrap). `mergePayload` helper 处理 JSON object 键合并, 冲突时 extras 胜 (subagent_id / event_type 永远外层优先). 7 sub-claim 测试全绿: Start 五字段 shape + Start 空串 omitempty + End 五字段 shape + End error 非空保留 + Event 扁平 shape (TextEvent inner) + Event ToolUse inner (id/name/input 扁平) + Event 嵌套 SubAgent (外层 ID 胜). docs/api-reference.md push 形态清单加 SubAgentStart/Event/End 三条 + SSE 事件格式章节补 3 个事件 payload 示例; docs/tools.md worktree 模式澄清"真隔离" + 可观测性 cross-reference. baseline 358 → 347 (11 dead fields 全部 wire). **全主题净效果 baseline 352 → 347 (-5)**: SubAgent.Cwd (commit A 工具 cwd 透传) + SubAgent.Description/StartTime/EndTime (commit B 进 SubAgentStartEvent / SubAgentEndEvent payload, commit C bridge 消费) + SubAgentEvent.SubAgentID (commit C bridge 扁平合入). 端到端产品兑现: Agent 工具 worktree 模式真隔离 (BashTool `pwd` 落在 wtInfo.Path, FileEdit symlink guard 用 worktree 根); 子 agent 活动对父 engine 可见 (TUI 树形 / SSE 子 agent 事件 / observer 审计 sink / session 成本统计按 subagent_id 归属). WorkerResult.Description 单独主题 "Team observability" 分离不在本次 scope (见本档 D-B 档 subagent 族条目下独立主题记录). - **commit D** ✅ (scope 补完, 2026-04-20): advisor sign-off 前检出的 3 个尾巴一并清: (1) 后台子 agent 观测路径: sa.runLoop ctx 无 emitter 时降级到 `sa.ParentEngine.Observer().Event("subagent_*", ...)` — RunBackground 的 bgCtx 从 engine.Context() 派生无 emitter, 之前后台子 agent 对审计 sink / 指标系统隐形, 现在经 observer 暴露 subagent_id + event_type + lifecycle 核心字段 (lossy 元数据, 非每内层业务字段 — 对齐 Observer 审计用法, 避免重复 pkg/bridge 已有的 17 case payload builder); (2) 5 个 observer fallback 测试全绿 (Start fields / End fields / InnerEvent metadata / Channel prefers over Observer 避免双发 / SilentEvents 两路都跳过); (3) Team.runWorker 集成测试 (TestRunWorkers_ForwardsSubAgentEventsToParentChannel) 证实 Team 路径也继承 ctx emitter, Worker 启动时 sa.runLoop 把 Start/End 转发到父 Run channel 且 SubAgentID 前后串联不乱 — Dream / skill fork 走同一 sa.runLoop 代码路径推导成立, 不单独加测; (4) sa.Run() channel 契约变更 (bare events, 不再 wrap SubAgentEvent) 写进 godoc + docs/api-reference.md push 形态清单, 标 "Breaking 2026-04-20" 给直接消费 sa.Run() 的外部 SDK 用户迁移指引. 一个**未验证**披露: commit B 声称"顺带修 RunSync 返回空串 silent bug" (原 wrapper 导致 type switch 匹配不到 *TextEvent, resultTexts 永远空) 是**基于代码读取 + Go switch 实验推断**, 未跑真 LLM 端到端验证 — 两种可能性都让业务变好, 但这是推断而非验证, 下次跑真 Agent 工具时观察输出即可反推. baseline 不变 347. **2026-04-20 晚续 session 验证**: scripted provider mock (emit 真 *flyto.TextEvent block_stop + UsageEvent end_turn) 驱动 sa.RunSync 端到端, 断言 result 含 "hello" PASS — 推断升为 validated. 见 `core/pkg/engine/subagent_silentbug_test.go` (commit 3edd2eb). - [x] `internal/syslib/bash.RedirectionInfo.SourceFD` 删除 (2026-04-20): 按 4 问法则审判走 **internal/ 包例外删除** 路径. 字段 godoc "源 fd(默认 1 for >, 0 for <)" 是描述性默认值, 非引擎行为承诺, struct 头 godoc (116-134) 只列 Heredoc 4 字段不背书 SourceFD. 三个剔除条件同时成立: (1) 包路径 `internal/syslib/bash`, Go 内部规则禁止外部 import, scanner 视野完整; (2) 0 reader / 0 writer (grep 全局只命中定义 + scan-baseline); (3) 同语义冗余 — parser AST `bash.Node` 根本无 FD 字段, `Operator` 已持 "2>&1" canonical 字符串, 消费者 (pkg/permission bash_security) 都读 Operator 不需要分解 FD 号. wire 路径需先补 parser + 再补消费者两层 speculative, 不走. advisor 复核通过. baseline 353 → 352. - [x] `Metadata.Destructive/ReadOnly` godoc 诚实化 (2026-04-20 晚续 session): 删"用于权限系统的快速判断"承诺 — 无 in-tree 消费者读本字段做权限判断, 权限路径由 `PermissionClass` 驱动. 改为 informational 元数据声明 (外部 SDK / TUI / audit 渲染标签 + evolve 工具生成器可用). 双语 godoc. 纯文档改动, baseline 不变. - [x] 大结果截断信号全链路补齐 (2026-04-20 晚续 session): `ToolCallResult.Truncated/StoredPath` 上游 orchestrator 写了, 下游两条 public surface 全漏 — `ToolResultEvent` (SSE 推给 HTTP 订阅者) 只 4 字段, `OperationEntry` (服务端操作日志给审计/rollback) 也没这 2 字段. UI 只能看截断摘要, 审计档案也缺"此次结果已截断"标记. 按 4 问 2 原则: 真 wire 缺口 (上游写下游漏) + exported SDK 可达默认保留. 修: (1) `flyto.ToolResultEvent` 加 Truncated + StoredPath 带双语 SECURITY note (path 是消费层 ResultProcessor 决定, 可能带 sandbox 内部结构); (2) `engine.go:4278` 构造 event 时填入 result.Truncated/StoredPath, 注释说明路径不脱敏 (不是工具输出, 是 ResultProcessor 落盘位置); (3) `pkg/engine.OperationEntry` 加同名 2 字段 + engine.go:4328 Record 时填入; (4) `ToolCallResult` godoc 重写 — 说明这 2 字段是 engine → Event/OperationEntry/Bridge → UI/audit 一条信任边界, 3 处 StoredPath 共享同一条安全约束; (5) `bridge event_serializer.go` tool_result 匿名 struct 加 truncated,omitempty + stored_path,omitempty 避免 L699 C2 同形的 choke-point 吞字段 bug. 3 sub-claim 测试: serializer TruncatedShape 断言 JSON 含 2 key + 值正确; serializer OmitsEmptyTruncation 断言 false/空串 omitempty 不加噪; OperationLog Record_PreservesTruncation 断言往返保真. baseline 净抵消 216 → 216: ToolCallResult 侧 -2 drain, OperationEntry 侧 +2 归档 pull API (外部审计 SDK/报表等激活消费者, rollback 不读). scope: 只补截断信号全链路, tools 族其他 11 字段 (ToolCapability 4 / DryRunResult 3 / Metadata 2 / Result.Data 1 / UndoInfo.Description 1 / OperationEntry 新 2) 各自独立审判不塞本 commit. - [x] Team observability (worker 可见性): `WorkerResult.Description` wire 进 task-notification XML (2026-04-20 晚续 session): 按 4 问 + 反向思考 审判. godoc "任务描述(用于追踪)" 意图两路消费 — (A) 调用方读 `[]WorkerResult` SDK exported 默认合法, (B) Leader 读 task-notification XML 反向识别"这是哪个业务任务". 路径 B 原是上游 wire 缺口: `runWorker` (team.go:435) 填了 `Description: desc`, `RunWorkers` 构造 XML 时只用 WorkerID/status/summary/AgentType/duration (team.go:321-327), Leader 看到的 task-id 是 random hex — 三个并发跑不同 prompt 的 Explore Worker 在 Leader 收件箱完全同形, 无法分辨. 修复: `taskNotificationTmpl` 加 `%s` 字段, `RunWorkers` 注入时 `xmlEscape(r.Description)` 参数一并塞入. 2 sub-claim 测试: `TestTaskNotificationTmpl_FormatOK` 扩断言 `南路径探索` 转义后出现; 新增 `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, 不扩到 Worker*Event 虚构需求. - [x] turn 边界族 C2: `TokenWarningState.IsAtBlockingLimit` 严重优先分发 + godoc 诚实化 (2026-04-20): 按 advisor 3 问验证 — (1) blocking 可达但已过锋 (post-turn 时本轮已完成, 下轮 pre-turn maybeCompact + ShouldCompact 兜底, 下层 provider context_length_exceeded 归类为 ErrContextOverflow); (2) 原 emit else-if 链从最弱阈值判起, 新加 blocking elif 会被 critical 分支吞 — 正是 IsAtBlockingLimit 成 dead 字段的根因; (3) godoc 承诺"无法继续对话"是引擎行为, 实际引擎不在此拒绝下轮, 语义造假. 修复: 提取 `PickWarningCode(state) string` pure function 严重优先分发 (blocking → critical → warning → ""), engine.go:3944 warning emit 用 switch-on-code 替换原 else-if, 新增 `context_window_blocked` code 带 "上下文窗口已占满, 下轮 pre-compact 将被强制触发" 消息 (可观测信号, 非引擎行为承诺). godoc 重写 IsAtBlockingLimit: 从"无法继续对话" → "100% 占满, 仅 observability signal, 真正兜底是下轮 maybeCompact / forceCompact, 严格蕴含 Error 和 Warning 阈值 true". 加 2 test: `TestCalculateWarningState_BlockingImpliesCriticalAndWarning` 锁 implication chain (blocking → Error + Warning 同 true), `TestPickWarningCode_SeverityOrder` 5 case 穷举 nil / empty / warning-only / critical / blocking 各自返回值. scope 纪律: 不改引擎真拒下轮 (pre-compact 路径已有, 改 engine 行为算另一主题). baseline 354 → 353. - [x] tools 族深审 (2026-04-21, 12 字段分档归位, 1 真 wire + 11 归档, baseline 213 → 212): 按 4 问 + 反向思考 审判 scanner 标 dead 的 12 字段. 原计划 1 commit 纯归档, 产品经理反向挑战"引擎该 gate 不该甩锅给外部 UI", 拆成 2 commit: - **commit 1 (`f814bd5`) MinConfidence safety gate 真 wire**: 从 "pull API 归档" 改分类为 "真实现债务". 预检发现 `ToolCapability.MinConfidence` godoc "最低置信度要求" 暗示引擎 gate 但零 gate site = 造假, 且 LLM 经 prompt 自报置信度是既有能力不需要模型新特性. 三层 wire: (1) `capability.go` 新增 const `ConfidenceInputField = "_flyto_confidence"` + helper `CheckConfidenceGate(cap, input)` (MinConfidence=0 原样放行, >0 时解析/拒绝/剥除); MinConfidence godoc 双语 Wire 段明述 buildToolDefs 提示 + orchestrator gate 两层消费路径. (2) `orchestrator.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` + `DryRunResult` 3 (WouldAffect / Preview / EstimatedImpact) + `UndoInfo.Description` + `OperationEntry.Output/TurnNumber` + `ImageResult.Width/Height`. 消费者在 core 外: audit UI / preview UI / safety dashboard / 外部 rollback UI / 外部审计 SDK. tests 锁 forward (capability_test.go / fileedit_test.go / filewrite_test.go 等), UndoInfo.Description 话术明确"tests 构造不断言 -- exported 字段给外部 rollback UI, 无 internal assert-site" (feedback_verify_real_wire_before_test). struct/字段级 godoc 升级双语 + 写清 pull 消费路径. 和 `engine.CacheStats` / `FileStateCacheEntry` / `PlanApprovalEvent` / `CheckpointSuggestedEvent` 等 `[~]` 归档同列, **未来 session 不再重审**. - **B2 godoc 诚实化 (2 字段 UndoMethod/UndoToolName)**: 原 godoc `"tool"=调用撤销工具` 暗示引擎 dispatcher 读本字段选 undo 路径, 实际 rollback 走 `UndoInfo.ToolName` (动态, GenerateUndo 捕获) 不读 ToolCapability 静态版. advisor 挑战后诚实化为 "informational 静态声明, 静态给 UI 执行前分类 / 动态给引擎执行时派发, 互补非冗余". 不是删字段 -- 静态和动态都有独立价值 (静态让 UI 不跑也能渲染 "此工具可撤销"). - **B3 OperationEntry.Output/TurnNumber 两路径澄清**: struct godoc 明述 "OperationLog.GetByMessage() 调取 vs observer 事件 opEventRecorded 里的 turn_number 推送是两个互不相交的 sink, 非冗余". 下次 reader 看到 audit_observer.go:167 也读 turn_number 不会误判为"已有消费者". - **B4 ImageResult.Width/Height 话术**: 采纳 advisor 建议 "外部 type-assert 表面 (SDK consumer 经 `if img, ok := res.Data.(*ImageResult); ok` 读)", 不写 "future log/validator" (speculative). - baseline 213 → 212 (MinConfidence 从 dead 升 live, 归档 11 字段保留于 scan-baseline.json 意为 "scanner 知道这些在本 module 无 internal consumer, ratchet 保证未来新加的类似位置不悄悄漏掉", 不走 drain). - [x] evolve 族深审 (2026-04-21, 11 字段分档归位, baseline 不降): 按 4 问 + 反向思考 审判 scanner 标 dead 的 14 字段 (其中 `LLMCallOpts.Temperature` C 档已独立归档, `FeedbackRecord` 5 字段上 session 已合并到 `Feedback`, 本 sweep 处理剩余 11 字段). 分两档结案: - **A1 合法 pull API 归档 (7 字段)**: `AggregatedStats.Sum` + `ChangeEvent.Change` + `ChangeEvent.IsLock` + `ShadowResult.BaselineBreakdown` + `ShadowResult.CandidateBreakdown` + `ShadowResult.Meta` + `ReplayEvent.Meta`. 消费者在 core 外 (watch-only 面板读 Stats() / 审计 dashboard 订阅 Watch / 灰度 UI 看 breakdown / 外部 LogReplayer 填 replay 上下文). tests 锁 forward propagation (`reflector_impls_test.go`/`parameter_store_file_test.go`/`shadow_runner_default_test.go`), 两个 Meta 是扩展槽不锁 test. 各 struct godoc 升级双语 + 写清 pull 消费路径 (commit `22478e6`). **未来 session 不再重审**, 和 `engine.CacheStats`/`FileStateCacheEntry`/`PlanApprovalEvent`/`CheckpointSuggestedEvent` 等 `[~]` 归档同列. - **A2 C 方案 evolve→engine adapter 缺口 (6 字段, 独立主题, 等业务场景触发)**: `RuntimeToolMetadata` 4 (ConcurrencySafe/ReadOnly/IsEvolved/Version) + `ToolResult` 2 (Output/IsError). 整条 "Agent 自造工具 → Engine 工具注册表 → 模型可见" 能力在 core 内从未接. `engine_integration.go:13` 原注释示范 `for _, t := range evolver.EngineTools()` 是造假 — Evolver 类型无 EngineTools() 方法, 无 engine.Tools().Register() 调用点. commit `22478e6` 改顶注释陈述现状: subagent / skill / memory 三路已覆盖 "能力沉淀" 需求, C 方案独有价值是"让模型在工具面板直接看见自己积累的招式" (skill 在 prompt 层积累思路, tool 在注册层积累能力), 在行业 platform (logistics/erp) 遇到 RPA 形态反复任务时显现. 字段保留 exported 让未来 adapter 直接复用 shape. **不排期到当前 sweep**, 等真实业务场景 (客户反复跑相同物流查询 / 报表流水) 触发再独立主题接入. - `LLMCallOpts.Temperature` (C 档剩 1 条, 要扩 flyto.Request 跨 provider) 不触, 保持 open 等独立设计决策. - baseline 216 → 216 (全字段保留, 无 drain; 分类话术从 "placeholder" 升为 "已分档结案"). - 缓存族: 本 session 全部处理完毕 — `engine.FileCacheEntry` 6 字段真 drain, `engine.CacheStats` 3 字段 `[~]` pull API 归档, `tools/builtin.FileStateCacheEntry` 5 字段 `[~]` callback form 归档. - ~~subagent 族~~ **重分类: 非 drain 候选, 是上游消费 bug**. 2026-04-20 审判发现 6 字段全部是"上游调用端该读不读": SubAgent.Cwd 被 set 但 BashTool/FileEditTool 继承父 cwd 导致 worktree 隔离失效; SubAgentID 被 set 但 3 处消费路径 (RunSync/RunBackground/记忆提取) 全 drop SubAgentEvent 导致子 agent 对父 engine 完全不可见; StartTime/EndTime 从没进 observer/session stats; Description 从没 emit. 下一主题 "子 agent 观测链路端到端 wire" 处理 (见下). - ~~独立主题 Team observability~~: `WorkerResult.Description` 2026-04-20 晚续 session 已完成 (wire 进 task-notification XML, 见 L707 上方新增条目). Worker Start/End 事件**不单独实现** — Worker 是 SubAgent, commit D 集成测试证实 team_test.go:285 路径下 subagent_start/subagent_end 已闭环, 重复加 Worker*Event 是虚构需求. - 计划进度族 (`PlanStep`/`StepProgress`/`PlanProgressEvent`/`PlanProgressSnapshot` 共 5 字段): 进度事件 subscribe 端未读. - bash 剩余: 全部处理完毕 — alpha.8 wire 主字段 (HeredocTag/Body/StripTabs/Quoted), `CommandInfo` 5 + `HeredocInfo` 4 (2026-04-20 前半 session) drain, `RedirectionInfo.SourceFD` (2026-04-20 本 session) 经 internal/ 包例外删除 (0 消费者 + parser AST 无 FD 支持 + `Operator` 已持 canonical 字符串 "2>&1", 冗余). - git (`git.Info` 5): 本 session 已 drain + `Run` 原语落地 (详见 L695 下 `internal/syslib/git.Info` 条目). - tools 族: 全部处理完毕 — 2026-04-20 `Metadata.Destructive/ReadOnly` godoc 诚实化 + `ToolCallResult.Truncated/StoredPath` drain, 2026-04-21 `Result.Data` vision wire drain + `MinConfidence` safety gate 真 wire + 余 11 字段分档归档 (详见上方 "tools 族深审 (2026-04-21)" 条目). - cache (`ToolStabilityReport` 4): 工具稳定性报告未 surface. - permission 次要 (`Response` 2 + `DenyRule` 1 + `LearningStats` 1 + `SedCheckResult` 1 + `SuggestedRule` 1). - builtin 次要 (`ImageResult` 2 `[~]` 归档 + `FileEditResultData` 2 + `GrepResult` 2 + `SkillEntryDesc` 2 + `SedEditInfo` 1). `ImageResult.MediaType/Base64` 2026-04-21 vision wire drain, `Width/Height` 2 字段同日 `[~]` pull API 归档 (见 tools 族深审). - 杂项: `context.RestoreItem`/`CompactResult`, `plugin.Plugin`/`Skill`/`ValidationResult`, `execenv.Spec`, `engine.ProcessedInput`/`SessionInfo`/`OperationEntry`/`FileSnapshot`/`WorktreeInfo`/`AgentDefinition`. **D-C 档 (注释标 LEGACY 但仍需真 wire, 4 主题 / ~17 字段)** — 注释历史措辞不等于"可删". 字段一旦声明就是契约, 默认 wire 保留, 只在枚举完所有 external consumer (SDK/plugin/CLI/platform) 都确认不需要后才考虑 deprecate, 本 sweep 不走该路径: - `transport.StreamEvent` (9): `client.go:14` 注释标 "LEGACY: StreamEvent/StreamEventType/UsageInfo 类型仍保留" + "StreamEvent 类型从公共 API 消失". public stream 已换 flyto.Event, 但内部 SSE 解析器产物仍是 StreamEvent — wire 方向: 内部 parser → flyto.Event 的 translator 读字段 (BlockID/BlockName/PartialJSON/Usage 等), 再发出 flyto.Event, 让 translator 真正读这些字段. - `transport.StreamStats` (3) + `transport.RetryInfo` (1): 同 LEGACY 族, stream guard / retry 层诊断字段, 应接 observer 事件或错误消息 surface. - `query.StreamEvent` (4): `pkg/query` SDK 类型, 即便 internal transport 已换 flyto.Event, query 层消费端应真读字段 (Block/Delta/Type/Usage). - `retry.OverflowInfo` (1): retry 溢出信息, 错误消息或诊断事件 surface. **决策点 (flag, 未启动 drain)**: 剩 ~20 主题级真债务, 主题密度比 alpha.7 (26 条) 低一档, drain 走 alpha.8 节奏估 2-4 个 session. 选项: 1. **启动 D 档 drain** — 按 D-A → D-B → D-C 顺序一主题一 commit, 节奏同 alpha.8 (sub-claim checklist + 一 sub-claim 一 test). alpha.9+ 周期. 2. **锁 386 切 TODO.md P1 消费层 7 条** (SQL 只读校验器 / DB Dry-run / ML 验证器 / CAS / 熔断 / Staging / 影子表) — 消费层是端到端产品价值点, 真债务是内部洁净度, 优先级由 user 判. 3. **局部 drain D-A 档 3-5 高价值主题后切 P1** — 兼顾 (SessionStats/PlanApprovalEvent/DenialStats/ClassifyResult 这类 user-facing 信号先落地, 其余留档). `pkg/evolve.LLMCallOpts.Temperature` (C 档剩 1 条) 设计决策 (扩 flyto.Request 跨 provider) 与本 sweep 正交, 保留 open 不强制打勾. --- ## 代码风格 / Lint --- ## 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_tokens` per 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 调新 `persistRoundTraces` walk + 写库 (Run 任何结果都写 — converged / max-rounds / error). 6 测试 -race 绿 (quotedispatch 3 + quotedispatchstore 1 + internal/server 2). 详 ADR-0008 v2.6.1 + CHANGELOG `Unreleased (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 多组配对; `verdictSchema` human_questions 数组; `HumanInputProvider.Ask([]HumanQuestion) ([]string, error)` slice 契约 (答数=问数按位对应) 4 实现; handler channel `chan []string` + answer 端点 `Answers []string` + 按 channel 类型分派 (phase 0 chan string 不动, 最小爆炸半径); UI `AwaitQuestionsPanel` 卡片; main_agent.md wire-contract 同步. 测试: agentprompt + quotedispatch (`TestRun_AwaitMultiQuestion`) + handler (`TestQuoteDispatchAnswer_MultiQuestion`) 全模块 -race 绿. **真验证待 staging 实跑** (需 deploy + prompt). 详 ADR-0008 v3.5 + CHANGELOG `Unreleased`. - [ ] ⚪ **L909-H billcost dispatch 小债清理** (P3, 2026-05-28 登记). 三条: (1) `index.html` settings 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, 见 memory `project_concurrent_run_race_audit` + ADR-0019), 但**文件系统级不按租户隔离** — 多租户带文件工具的负载路由到这三个端点会互相读写文件. **当前不是活跃风险**: 客户报价业务走的是模型 B (`quote-dispatch` 每请求独立引擎 + `tools.None()`, 完全隔离, 见 api-reference.md "Run 端点选型与引擎隔离模型" 节). **产出 (多租户流量进模型 A 端点前必须收口)**: 给模型 A 端点接**每请求沙盒 + 独立工作目录** — 引擎已支持 (`Config.Executor` 注入 sandbox backend, 见 memory `project_sandbox_local_vs_cloud`; `tools.WithWorkdir` per-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 `79a5be2` wire/openai.go DeepSeek 顶级 prompt_cache_hit_tokens 字段 fallback (Usage struct 加 PromptCacheHitTokens / PromptCacheMissTokens + buildUsageEvent 嵌套优先 fallback; 2 测试) + C2 `01b2022` deepseek 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) + C3 `a6f0c1d` capability-probe 接入 deepseek + caching ladder 路径 (cachingProvider 走 100/300/600/1200 ladder 触发 deepseek auto-cache; 7/7 capability ✓) + C4 `02cf0b3` TD-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 决策得到强力实证) + C5 `ff08515` quote-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 `09b6bc1` Bug W wire 层 transport-level dedup hotfix (单 message 内同 tool_use_id 重复 silent skip, 跟 final_text_duplicate_blocks 同源, 切出 ADR-0007 范围) + C2 `dac31d6` ModelInfo 加 ToolNameRegex / ReasoningPassbackMode / ProviderKind 3 字段 (零值=未知=零回归) + C3 `f77b2c3` 4 direct provider modelInfo 静态填值 (anthropic regex `^[a-zA-Z0-9_-]{1,64}$` + openai/minimax/gemini `^[a-zA-Z0-9_-]+$` + 全 ProviderKind=direct) + C4 `17f7ba4` wire 层 capability-aware (openaiMsg 加 ReasoningContent omitempty / StreamRequest 加 2 字段 / flytoMessagesToOpenAI mode=="string" 时 inject reasoning_content / wire.ValidateToolNames pre-flight 校验返 ErrModelToolUnsupported typed error / openrouter Provider 接通) + C5 `16423ac` OpenRouter 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 顺序**: C1 `f140eb1` 业务 fix tool 名 `billcost.reflect` → `billcost_reflect` (CLEVER 注释入档 OpenAI regex 限制) + C2 `e009350` wire OpenRouter parseNonSSEError 解嵌套 metadata.raw (SiliconFlow `{code,message}` / OpenAI passthrough 形态; 解一层不递归保 surface 小; 3 测试) + C3 `960445c` EngineError.Error() 拼 Detail (Go 经典 wrapping 形态字符串路径; 2 测试) + C4 `152903d` 加 5 typed ErrorCode 词汇表 (ErrProviderHTTPStatus / NonSSE / MidStreamErr / WireUnmarshal / ModelToolUnsupported; ADR-0006 § 3 红线 godoc 入档只分类不行为) + C5 `b3a083c` wire→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.md` 8 节体例对齐 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 范围, 留 TODO `wire OpenAI-compat reasoning_content roundtripping` follow-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云仓数据库设计说明书.xlsx` Sheet `CWMACCT` `ShipCostCfgMaster` L1 + `ShipCostCfg` L21, `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 []string` vs 一行一省 `ProvinceName + CityName` 1:N / 无 `StandardCostSysNo` 关联 base 标准成本 / `Carrier="圆通"` 名字 vs `ShipTypeId / 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 通)**: `199fd11` C1 docs (ADR-0004 + CHANGELOG + TODO) → `045ec70` C2 大切 (schema migration `price_adjustments` drop, 新 `ship_cost_cfg_master + ship_cost_cfg` 镜像 WMS + parser `Adjustment` → `AdjustmentMaster + AdjustmentDetail + AdjustmentBundle` 全字段 json tag snake_case 顺手关 alpha.6 marshal mismatch bug + store `SaveShipCostCfg + ListShipCostCfg + CountShipCostCfgMaster` + LLM `ExtractShipCostCfg` + WMS 字段 prompt + `parseShipCostCfgContent` + workflow `AdjustmentExtractor` 新签名 + web `bundleConfirmPayload` 新 wire shape, 1173+/656− 行) → `2ce9076` C3 review UI 卡片重做 (master 4 字段 + 3 折叠高级 + N 行 detail 子表 9 列 + 加行删行 + style.css grid 布局) → `b314a2e` C4 bill detail 接 `ListShipCostCfg` (server.go 加 `adjustmentBundleDTO` + 扩 `handleGetBill` 返 `{ bill, adjustments[], warning? }`, app.js 加 `toggleBillDetail` inline 展开 + `renderBillDetail` meta + 每 bundle image + master `
` + detail `` 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 顺序**: `eec48fe` C1 SessionStore 接口 (Create/Get/Delete 三方法) + InMemoryStore drop-in 替换 server.sessions map + 4 handler 改造 + TOCTOU race 折叠 (319 行 sessionstore + 改 server.go 182 行); `beaff60` C2 Postgres 后端 + `internal/db/` 共享池 + `--postgres-dsn` flag + docker-compose pg + healthcheck + persistent volume + release.yml POSTGRES_PASSWORD secret + testcontainers-go 真 pg 测试 (~510 行 + main.go 53 行 + docker-compose 48 行); `96e893a` C3 ADR-0003 (387 行 8 节, 三层 pin 物理事实 + sticky routing phase 1 ip_hash + phase 2 X-Session-ID 升级路径 + cache miss 503 fallback) + Caddyfile 注释; 本 commit C4 TODO + CHANGELOG + CLAUDE.md 同步. **真相**: 多副本真阻塞是三层进程内 pin (server.permCh + Session.pendingPermissions + engine.sessionState), 后两层在 core 引擎层平台层不能解, 必须 LB sticky routing; SessionStore 价值降级为"replica 重启不丢元数据 + 滚动部署 drain + Postgres audit". **关键决策**: drop Redis 档 (元数据 payload 太薄, 共享 staging pg 池更经济); drop staging Postgres 后端 (PM 接受 YAGNI, ADR-0003 § 5.5 登记触发条件 = staging 真接入消费者); engine.SnapshotStore 接线 cache miss 自动恢复历史留 follow-up. **不引正式 migration 工具** (1 张表 plain CREATE IF NOT EXISTS 自检足够). **测试**: 6 InMemoryStore + 5 testcontainers Postgres + 2 db pool, 全模块 -race 全绿; baseline 220 不变 (scanner 只扫 core/, 不扫 platform). **PM 部署侧必做** (v0.4 release 前): Gitea secrets 配 POSTGRES_PASSWORD. - **v0.3.3 发版 (2026-04-26)**: L407 follow-up 三件套 — A README 文档地图 (commit `fea0d27`, 70 行 markdown 串 27+ markdown + 4 入口 + 7 读者分组) + B godoc.flytoex.net 子域 (pkgsite Go API HTML, 子域因 pkgsite href 绝对路径不能挂 sub-path; Dockerfile.godoc 多阶段拷整 monorepo 源 + go mod download all + ENTRYPOINT pkgsite -list=false; 不鉴权对齐 pkg.go.dev) + C docs.flytoex.net 子域 (mkdocs-material@9.5.31 整合站, Dockerfile.docs python:3.12-alpine + nginx:alpine 两阶段; mkdocs.yml docs_dir=/work + config /config/mkdocs.yml sibling 绕开 mkdocs "docs_dir 不能是 config 父目录" 约束; nav 12 分组串 README+CONSUMERS+CONTRIBUTING+FLYTO+ADR-0001/2+CHANGELOG+TODO+7 provider+architecture+writing-guide). DNS 加 2 条 A 记录 (godoc.flytoex.net + docs.flytoex.net → 45.145.229.197 DNS-only 灰云) 经 Cloudflare API token PUT /zones//dns_records, 不走 PM web UI. release.yml 加 godoc + docs 两个 build-push step. 本地 docker build 两 image 实测通 (godoc 61.8s + curl /git.flytoex.net/... 200; docs 5.83s mkdocs build + nginx serve / 200 中文 title 渲染). baseline 219 不变 (纯文档 + deploy infra). 上一变更 **v0.3.0/0.3.1/0.3.2 发版 (2026-04-26)**: CHANGELOG `Unreleased (v0.3-dev)` → `## v0.3.0 (2026-04-26)` 结构化重组对齐 v0.2.0 段 8 节格式; v0.3.1 hotfix go.sum tidy (golang.org/x/net v0.50.0 transitive missing, GOWORK=off Dockerfile build fail); v0.3.2 hotfix #2 (release.yml deploy script export ANTHROPIC_API_KEY + Gitea API PUT secret, 用 ~/.git-credentials 里 yuanwei token 自动配). 覆盖面: counterfactual + reverse_think + shadowdb + Temp/TopP + L692 业务 REST/SSE + L407 文档自动化三件套 + ADR-0001/ADR-0002 立项. push tag v0.3.0/0.3.1/0.3.2 触发 release.yml: dead-field-ratchet → docs drift gate (v0.3.0 新加) → buildx 4 image (含本版加的 godoc + docs) → SSH HK-133 deploy → caddy restart 让 /swagger/* + godoc.flytoex.net + docs.flytoex.net 立即生效. - **2026-04-26 L407 消费层文档自动化三件套 (7 commit, 含 1 path bug 顺手修)**: queued task 锁定 swag (注解式 OpenAPI 2.0) > huma (code-first 要重写 1263 行 server.go) 路线; SDK 自动生成 (Stainless 模式) 不做 rule of two 等 5+ 语言客户端. **commit 顺序**: `89c38d3` C0 顺手修部署 path bug `/v1/* → /api/v1/*` (Caddyfile `handle /api/v1/*` 不剥前缀致 server.go 注册 `/v1/*` 经 hub.flytoex.net 全 404, 上一会话 commit 5 没真实经 Caddy 实测, 触发 memory `feedback_validate_network_path_before_deploy`); `26bc732` C1 server.go 8 handler swag 注解 + 3 named response type (HealthResponse/StatusResponse/ListToolsResponse) 替代 ad-hoc map + swag.go seed + docs/{swagger.json 22.5K 12 schema, swagger.yaml 12.3K, docs.go 23.1K Go embed} 首版; `bce7670` C2 cmd/common --swagger flag + Swagger UI endpoint (httpSwagger v2 包内嵌, side-effect import docs, authMiddleware/rateLimit allow-list 加 /swagger/); `22b6388` C3 core/Makefile docs-swag/grpc/consumers/all 4 target + tool install (swag@v1.16.6 + protoc-gen-doc@v1.5.1 pin 与 staticcheck@v0.7.0 同模式) + ROOT git rev-parse + 顶部 export PATH 含 GOPATH/bin + grpc-api.md 首版 278 行 + .gitea/release.yml docs drift gate (apt-get protoc + make docs-install + docs-swag/grpc + git diff --exit-code 4 产物); `a9b04ab` C4 docs/CONSUMERS.md 顶层 wrapper 133 行 (端口拓扑/env/flag/OIDC auth 流程图含 allow-list/业务 REST 一次性+多轮会话 curl 例子/观测 gRPC SafetyChain 指引/进一步阅读链); `bf5af1d` C5 Caddyfile handle /swagger/* → common:8080 + docker-compose --swagger flag (HK-133 lab 默认开, 生产另份 compose 关); 本 commit C6 TODO/CHANGELOG/CLAUDE.md 同步. **CI drift gate** 与 dead-field-ratchet 平级在 build-push job 开头, 业界对照 Stripe/Anthropic/OpenAI 都对 OpenAPI spec 跑同等闸 (PR 必须 commit 生成产物). **smoke**: ANTHROPIC_API_KEY=fake go run ./cmd/common --rest-addr=:18080 --swagger → /api/v1/health 200 + /swagger/index.html 200 + /swagger/doc.json 200 返回 commit 1 嵌入 spec; build + -race 全绿. **三件套全产**: 业务 REST swagger.json (12 schema) + 观测 gRPC grpc-api.md (HealthService + SafetyChainService 字段表) + CONSUMERS.md (端口/env/flag/auth 流程/curl 例子). **不做**: SDK 自动生成 (Stainless 模式 rule of two, 等 5+ 语言客户端); .proto 字段注释完善 (health.proto 部分字段 description 列空, 后续工作). - **2026-04-26 L692 platform/common 业务 REST/SSE 通道激活 (6 commit)**: 3-agent review reconcile (调研 11 LLM API + grpc-gateway 健康度 / 质疑 6 道含 raw bearer auth 与 OIDC 不兼容致命 + grpc-gateway 与 admin 重复致命 / 设计 4 备选 + 推荐 A 修正版), PM 拍板. **commit 顺序**: `01f08e7` C1 server.go 拆 buildHandler/Serve/ListenAndServe-wrapper 让出 signal handling; `8189d05` C2 Verifier 替代 BearerToken 走 auth.HTTPMiddleware (raw shared-secret + ConstantTimeCompare 删除, dev 模式 Verifier=nil 与 admin 一致); `694bd07` C3 server.New 接受外部 engine 不再内嵌 anthropic provider 写死, 加 Attach + HandlePermission 两 API; `e1db327` C4 cmd/common 加 `--rest-addr` flag, 装配 anthropic provider + engine + s.Attach + 第三 listener wire (signal handler 三路协调 grpc.GracefulStop / httpSrv.Shutdown / restCancel, errCh capacity 升 3); `c35e761` C5 docker-compose expose 8080 + command 加 --rest-addr=:8080 + ANTHROPIC_API_KEY 必填 environment, Caddyfile `/api/v1/*` → common:8080 (flush_interval -1 + response_header_timeout 0 SSE 透传), README topology + curl 例子同步; 本 commit C6 ADR-0002 + 文档同步. **核心决策**: REST/SSE 唯一业务通道 + gRPC 仅观测面 (SafetyChain / Health) + 不为 C# 单独加业务 RPC + 不加 grpc-gateway (admin 已有观测面 REST handler) + Tool 级 SafetyChain 装饰留给行业 platform (cmd/common 保持纯 transport, verdictStore 接线就绪等行业写数据). **业界对照**: 11 LLM API 全 REST/SSE 单通道, 业界共识与 PM 方向对齐. 4 tracked debt 登记 (L693 多副本 SessionStore P2 / L694 cross-transport request-id P3 / L695 SSE 带宽监控 P3 / L407 Swagger 与 ADR-0002 关联). 测试: server_test.go 既有 546 行用 httptest 直接打 handler 不动, 删 4 raw bearer TestAuth_*, 加 1 ctx 测试. -race 全绿. cmd/common --help 验证 --rest-addr flag 出现. - **v0.2.0 发版 (2026-04-24)**: CHANGELOG `Unreleased (v0.2-dev)` → `## v0.2.0 (2026-04-24)` 结构化重组对齐 v0.1.0 段格式 (核心新增 / platform/common 层 / 关键设计原则 / 已知限制 / 发布事实 / 详细变更); 新起 `## Unreleased (v0.3-dev)` 空段. 覆盖面: evolve 9/9 矩阵 + SQL 工具链 × 3 + validator/circuitbreaker/reflector/staging 4 新子包 + safety chain C1-C4 platform/common 装配+观测+gRPC. Baseline 212 → 216 (+4 合法 tracked debt). TODO 46 → 47 done / 15 → 14 open. - (2026-04-24, staging 子包): `pkg/staging/` **commit 3/3 完成 (L436 check off)** — `Engine` 混合控制状态机 (Stage / ValidateTech / ValidateBiz 内部主动推进, MarkExecuted / MarkFailed 外部推 arc 薄转发) + fail-closed 语义 (Validator error / DependencyGuard error 均合成 Block verdict 或 ErrDependencyDenied, 不静默放行) + 内置 `TenantDenyGuard` 示例 (metadata-driven guard 典型形态). ~530 行 (含 14 engine test + 内置 guard). baseline 218 → 216 (Record.Diff / Record.Metadata drain); 剩余 4 条 (TechVerdict/BizVerdict/ExecutionError/ExecutionProof) 合法 tracked debt — "外部 audit dashboard 消费, core 无内部 reader" 形态, 按 memory `feedback_exported_field_delete_needs_review.md`. - `2c38e46` (2026-04-24): `pkg/staging/` commit 2/3 — `Store` 接口 (9 方法) + `InMemoryStore` 参考实现. MarkExecuted 幂等 first-write-wins 保 proof, MarkFailed 幂等保 reason. 17 test 含 2 race 并发 (ID collision / CAS race). baseline 223 → 218 (9 drain + 4 新 dead). - `d9992d5` (2026-04-24): `pkg/staging/` commit 1/3 — 7 状态机 + Record + DependencyGuard interface + AllowAlwaysGuard. 决策包级 record, 做法 I 整体打包. baseline 212 → 223 (tracked debt 登记 + exit criteria 声明). - `979a304` (2026-04-24): `pkg/reflector/` umbrella 包 — 表达 "反射器" 产品抽象, 不引新接口, 4 adapter (ValidatorAsEvaluator / EvaluatorAsValidator / ValidatorAsReflector / EvaluatorAsReflector) 跨家族互转. 反向 Reflector → Validator/Evaluator 刻意不做. Option d 胜 Option a (Agent Teams 3 角色 review: 真合并违 Go 惯例 + sync/async 语义不可同一接口). tracked debt: GA 场景若需 "按 fitness 级别映射 Severity" 的连续分级, 需加 `WithSeverityFunc(func(fitness) Severity)` option; 当前二值 (Warn/Block) 是 validator.Severity 的物理约束. - `1a08c47` (2026-04-24): C4 platform/common 安全链 gRPC 暴露 — `safetychain.proto` (2 RPC: ListVerdicts / ListBreakerStates) + `SafetyChainServer` + cmd/common wire. 候选 A 端到端闭合 (core + common 装配 + HTTP 观测 + gRPC). 工具链 install protoc-gen-go / protoc-gen-go-grpc 到 $GOPATH/bin (一次性). - `753ec03` (2026-04-24): C3 platform/common admin 观测端点 — `VerdictStore` + `RingStore` + `admin.WithSafetyChain` + 2 新 HTTP 端点 (verdicts / breakers). 鉴权和 /admin/tenant 同级, opt-in 才挂载默认 404. - `cb8cd2f` (2026-04-24): C2 platform/common safetychain 装配层 — `Assemble()` 一行装配 + `BreakerScopePolicy` 三工厂 (NoOp/DestructiveOnly/PerTool) + `BreakerRegistry`. 无默认显式 opt-in, 行业 platform 自选 backend + 作用域. - `1b0a860` (2026-04-24): C1 core 安全链 enforcement — `validator.AlwaysApprove{}` 显式 opt-out + `NewValidatedTool` 构造期 nil panic. 候选 A (platform 装配框架) 启动前的 core-level enforcement, 消灭 "以为开了审批实际没开" 的静默安全假象. - `981a2ea` (2026-04-23): 模块 23 立项归档 + 消费层分类翻盘 (SQL 3 件套从 "不属于引擎层" 挪入核心) - `a935604` (2026-04-23): SQL Dry-run 三路 (UPDATE/DELETE/INSERT, 方案 E before+after SELECT) - `bf31278` (2026-04-23): SQL CAS 乐观锁 (StagingDB newtype + maxRetries=0 fail-fast, `modernc.org/sqlite` test-only dep) - `79670c7` (2026-04-23): SQL 只读校验器 (纯字符串 quote-aware 解析 0 DB 依赖) - 2026-04-23 doc drift 修复 (本 commit): CHANGELOG "已知限制" 移除 SQL 条 + `v0.2-dev` 段加 SQL 工具链纪念段; 本统计块切换文件内口径; CLAUDE.md 分层统计对齐 (引擎 4 / provider 2 / platform 9); memory `project_version_roadmap.md` v0.1 发版事实更新. ## 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)**: `NormalizeMessagesForAPI` godoc 写 "发送前清理" 但全仓零调用, 唯一 normalize 在 session restore. 长对话中途 compaction/注入产出的非法序列 (孤立 tool_result 等) 照样发出去 provider 400. **修复**: engine.go runLoop 在 maybeCompact 后、BuildAndStream 前用 `DefaultNormalizePipelineWithObserver(e.observer, e.strictMode)` 规范化一份副本喂 send (raw history 不动, 每轮一次). regression test `TestRunLoop_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** -> 截断检测不误报. regression `TestOpenAICompatClient_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" 但该调用在非测试代码零存在 (注释承诺代码没做). - 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 多轮工具循环末尾吐 `` 乱码 = 本地 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`): runMemoryExtraction `for 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. regression `memory_extraction_silentbug_test.go`. 诊断价值: 修后 harness 无 failed 事件 -> 排除"引擎吞错". - **item 1c 真根因 -- 子 agent 漏发 Tools** (`807f511`, 重大): `SubAgent.runLoop` 的 flyto.Request **漏 Tools 字段** (注释吹传 allToolDefs 但赋值不存在) -> 所有子 agent 发模型零工具 -> 引不出工具调用. 修: `flytoReq.Tools = apiToolDefsToFlyto(sa.allToolDefs)`. regression `subagent_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 起 🟢.) - [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-mode` flag (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` 库. realm `flyto` + client `flytocall` (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}/events` SSE 重放全转正 (租户隔离, 跨租户 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.}}` 汇总; 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_revisions` append-only 快照表 (Put 同事务落行 / Publish 翻状态 / Delete 连带删) + Store `ListRevisions`/`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 异常增多再上.