TODO 已完成
0.1 EngineObserver 接口体系 ✅
- [x] EventObserver 核心事件接口(Event + Error)
- [x] MetricObserver 指标接口(可选,type assertion 检测)
- [x] TraceObserver 调用链接口(可选,SpanStart/SpanEnd)
0.2 默认实现 ✅
- [x] NoopObserver(零开销空实现,未配置时默认使用)
- [x] StderrObserver(开发调试,支持级别过滤)
- [x] CompositeObserver(多路复合,同时发到多个后端)
- [x] BufferedObserver(异步批量,不阻塞热路径)
0.3 StrictMode 严格模式 ✅
- [x] ToolResultPairing / CompactFailure / NormalizerError 三个独立开关
- [x] Check 方法同时处理 panic 和 observer 记录
0.4 Engine 接入 ✅
- [x] Config.Observer / Config.StrictMode 配置项
- [x] Engine 构造时 nil 安全初始化(NoopObserver)
- [x] runLoop 关键路径埋点(API 调用,工具执行,压缩,会话结束)
1.1 AST 解析替代字符串分割 ✅
- [x] 实现 Bash AST 解析器(heredoc,引号,命令替换,进程替换)
- [x] Heredoc 边界检测(
<<EOF,<<-EOF,<<'EOF' 三种变体)
- [x] 单/双引号和 ANSI-C 引用
$'...' 的准确追踪
- [x] 命令替换
$() 的递归分析
- [x] 算术表达式
(()) 中 << 与 heredoc << 的区分
1.2 环境变量前缀跳过 ✅
- [x]
VAR=value command 中识别真正的命令名
- [x] 嵌入
$(...) 的动态性检测
- [x]
env VAR=x command / sudo -E command 等前缀处理
1.3 引号感知命令分割 ✅
- [x] AST 解析器天然处理引号(不需要占位符)
- [x] 续行处理(解析器层面)
- [x] 嵌套引号的正确处理
1.4 进程管理 ✅
- [x] 进程组(Setpgid)正确设置
- [x] 优雅终止(SIGTERM → 5s → SIGKILL)
- [x] 敏感环境变量过滤
1.5 输出截断策略 ✅
- [x] stdout(200KB)/stderr(56KB) 分别截断
- [x] CJK 字符显示宽度计算
- [x] 头尾保留截断(80%/20%)+ magic bytes 二进制检测
1.6 后台运行 (run_in_background) ✅
- [x] BackgroundTaskStore 状态持久化
- [x] 状态查询和手动取消
- [x] 断点续传(session 重连恢复)- SessionSnapshot + FileSnapshotStore 原子写入 + ResumeConversation
- [x] 自动后台化判断(阻塞时间阈值)- AssistantBlockingBudgetMs=15s,主 agent BashTool 触发,lineCh 移交后台 goroutine
1.7 命令分类 ✅
- [x] 6 类命令分类集合(search/read/list/write/silent/general)
- [x] 管道链中的语义传播(降级逻辑)
- [x] 中性命令(echo 等)的跳过
2.1 Curly Quote 保留 ✅(早期方案精妙设计)
- [x] findActualString() 引号规范化匹配
- [x] preserveQuoteStyle() 弯引号风格保留
- [x] 开/闭引号上下文检测 + 缩写撇号处理
2.2 Desanitization ✅
- [x] API 消毒标签的反消毒( → 等)
- [x] 对 old_string 和 new_string 同时应用
2.3 两步验证设计 ✅
- [x] validate() - 快速失败,不写文件,权限检查前运行
- [x] apply() - 原子执行,带文件缓存更新
- [x] CRLF 规范化 + 空白差异检测 + Levenshtein 相似匹配
2.4 文件缓存集成 ✅
补完 ✅
- [x] Diff 预览 - generateUnifiedDiff(3 行上下文)
- [x] 符号链接安全检测 - Lstat 检测 + 目标路径披露 + 阻止编辑
- [x] 原子写入 - tmp + rename(同目录,跨 FS rename 失败自动清理)
3.1 图片处理 ✅
- [x] magic bytes 检测(不信任扩展名)+ base64 内联
- [x] image.DecodeConfig 提取尺寸
- [x] 大图(>10MB)仅返回元数据
- [x] EXIF 方向校正 - JPEG(纯Go) + TIFF(纯Go解码器/LZW/PackBits) + WebP(RIFF自解析+x/image) + HEIC(CGO+libheif,含 TIFF/HEIC magic 检测)
3.2 PDF 支持 ✅
- [x] magic bytes(%PDF-)验证
- [x] pages 参数解析("1-5", "3", "10-20")
- [x] /Type /Page 启发式页数估算
- [x] 20 页/请求限制
3.3 Jupyter Notebook ✅
- [x] .ipynb JSON 完整解析
- [x] In[N]/Out[N] 代码单元 + Markdown + 错误追踪
- [x] 支持 string 和 string[] 两种 source 格式
3.4 设备文件阻止 ✅
- [x] 路径黑名单(/dev/zero, /dev/random, /proc/self/mem 等)
- [x] os.ModeDevice 检测兜底
3.5 路径安全 ✅
- [x] Clean + 绝对路径强制
- [x] Lstat 符号链接检测 + EvalSymlinks 解析
- [x] UTF-8 BOM 跳过 + UTF-16 BOM 检测 + 二进制 null byte 检测
4.1 双引擎策略 ✅(升华设计)
- [x] Grep: RipgrepEngine(rg 可用)+ BuiltinGrepEngine(纯 Go 兜底)
- [x] Glob: GitGlobEngine(git ls-files)+ WalkGlobEngine(纯 Go 兜底)
- [x] 自动检测引擎(DetectGrepEngine / DetectGlobEngine)
4.2 多行匹配 ✅
- [x] Grep
multiline: true(rg: -U --multiline-dotall, builtin: (?s))
- [x] 预计算行偏移表 + 二分查找行号
4.3 类型过滤 ✅
- [x] 30+ 语言类型映射表
- [x] rg --type / builtin 扩展名过滤
4.4 分页 + 排序 ✅(升华设计)
- [x] head_limit 0=无限逃逸口,默认 250
- [x] offset 分页
- [x] 升华:逗号分割 glob(不按空格,文件名有空格更常见)
- [x] 升华:≤200 文件 mtime 并行排序,>200 字母序 + 提示
4.5 安全加固 ✅(G8-D review)
- [x] WalkGlobEngine symlink 路径逃逸修复(symlinkSafe + EvalSymlinks 前缀校验)
- [x] collectFilesForGrep symlink 路径逃逸修复(同上)
- [x] 根目录内的合法 symlink 不被误杀(仅拦截跨根目录的越界 symlink)
- [x] 测试:TestGlobTool_SymlinkEscape + TestGrepTool_SymlinkEscape
5.1 自动模式分类器 ✅
- [x] 两阶段 AI 评估(快速 + 深度思考)
- [x] 决策缓存(同一命令复用决策,5分钟 TTL)
- [x] thinking 预算管理(Stage 2 BudgetTokens=1024)
5.2 Compound 命令完整处理 ✅
- [x] 50 子命令上限(MaxSubcommands=50,超限触发 Ask)
- [x] 递归深度限制(MaxRecursionDepth=20,防嵌套栈溢出)
- [x] 逻辑短路语义理解 → 故意不实现(ELEVATED 注释说明安全考量)
5.3 Sed 脚本验证 ✅
- [x]
e 标志检测(执行 shell 命令 - 极危险!)
- [x] 多命令脚本逐条验证(extractSedExpressions + containsDangerousOperations)
- [x] sed 地址模式理解(stripSedAddress 支持数字/范围/正则/自定义分隔符地址)
5.4 重定向目标分析 ✅
- [x] 静态 vs 动态目标区分(IsStaticRedirectTarget 增强)
- [x]
$() / 变量 / glob 的识别(含 \r,=,! 等边界防护)
- [x] 文件描述符 dup
2>&1 的逻辑(& 检测 + hasDynamicRedirect 集成)
5.5 权限决策缓存 → 规则应用管道 + 去重 ✅
- [x] 规则应用管道(RuleApplier)--将学习建议自动应用为 session 规则
- [x] tool_use_id 去重(PermissionDedup)--FIFO 防止 SDK 重传
- [x] 集成到权限引擎 Check() 流程
- [x] 设计决策:不做命令指纹缓存(安全风险:同 cwd 不同含义),改为规则持久化
- [x] 权限 Handler 返回异常:当作拒绝处理 + 记录错误(permission.Engine 已实现)
- [x] 权限规则解析失败:跳过该规则 + 不影响其他规则(RuleApplier 防御性解析)
- [x] 权限 Handler 超时:goroutine + select + buffered-chan 模式,默认 5 分钟拒绝(NewEngineWithTimeout)
6.1 压缩后恢复精细度 ✅
- [x] 文件恢复 token 预算(最多 5 个文件,每个 5K,总预算 50K)
- [x] 技能恢复优先级排序
- [x] MCP 指令增量恢复
6.2 消息分组 ✅
- [x] 按 API 往返分组(GroupByAPIRound)
- [x] 孤立 tool_result 检测
6.3 压缩前图像移除 ✅
- [x] 压缩 API 调用前剥离图像块
- [x] Document/嵌套图像的处理
6.4 多策略压缩 ✅
- [x] 部分压缩(保留最近消息前缀)
- [x] 反应式压缩(基于 token 预算的尾部裁剪)
- [x] 策略选择逻辑(何时用部分/完整)
6.5 三层压缩降级 + 断路器改进 ✅
- [x] 三层降级策略:单次压缩 → 砍头重试 → 分块压缩
- [x] 砍头重试(truncateAndRetryCompact,最多 5 次,逐步砍最旧组)
- [x] 分块压缩(chunkedCompact,按轮次边界分块,并行 goroutine)
- [x] 分块切分保证轮次边界完整性(chunkByTokenBudget)
- [x] 断路器改进:rate limit (429/529) 不计入失败次数
- [x] 断路器改进:时间重置(默认 5 分钟后自动恢复)
- [x] 断路器内嵌到 Compressor,CompactTiered 自动管理
- [x] isPromptTooLong / isRateLimit 错误分类辅助函数
- [x] 压缩 API 调用三层防御:提示词禁止工具调用 + 不传 tools 参数 + tool_use 块当文本处理
- [x] 压缩结果为空:降级到最近 5 条消息(compactFallbackMessages = 5,compactFull + compactPartial 均覆盖)
- [x] 失败计数持久化(CircuitBreakerPersister 接口 + FilePersister,per-project cwd hash,TTL=1h,原子写入,engine.go 自动激活)
7.1 消息规范化管道 ✅(升华重构)
- [x] MessageNormalizer 接口 + NormalizePipeline 可组合管道
- [x] 9 个内置步骤(Priority 排序,独立可测试):
- OrphanToolResultRemover (10) - 孤立 tool_result 移除
- ErrorContentStripper (15) - 错误内容自动剥离(防坏图毁会话)
- OrphanThinkingFilter (18) - 纯 thinking assistant 过滤
- EmptyMessageFilter (20) - 空消息过滤
- WhitespaceAssistantFilter (22) - 空白 assistant 过滤
- ToolUseInputNormalizer (25) - tool_use 输入规范化(内部字段剥离+别名)
- ConsecutiveRoleMerger (30) - 连续同角色合并
- ImageValidator (50) - 超大图片自动剥离
- AttachmentReorderer (5) - 附件上浮(预留,待附件概念引入后启用)
- [x] NormalizeMessagesForAPI() 向后兼容包装
- [x] Message.Metadata 扩展字段(附件标记,虚拟消息等)
- [x] 43 个测试全部通过(含 Pipeline 集成测试 + 各步骤独立测试)
7.2 stop_reason 完整处理 ✅
- [x] max_tokens 恢复策略(先低后高 + ContextOverflowHandler 自动修正)
- [x] stop_sequence 自定义支持(API 层 StopSequences 字段 + engine 层 stop_reason 处理)
- [x] EngineObserver 接口(EventObserver + MetricObserver + TraceObserver)
- [x] NoopObserver / StderrObserver / CompositeObserver / BufferedObserver 四种实现
- [x] StrictMode 严格模式(测试/安全评估环境开启,配对异常/压缩失败/规范化异常分别控制)
- [x] ToolResultPairingNormalizer 4 种 case(注入合成 tool_result / 剥离孤立 / 去重 tool_use / 去重 tool_result)
- [x] Engine.runLoop 关键路径埋点(API 调用完成 / 工具执行 / 压缩触发 / 会话结束)
- [x] DefaultNormalizePipelineWithObserver 支持 Observer 注入
- [x] 诊断快照函数 buildDiagnosticDetail(不泄露用户数据)
- [x] 完整测试覆盖(observer_test / strict_test / norm_tool_result_pairing_test)
7.4 Token 预算精细度 ✅
- [x] Prompt caching 效应的预算重算(GetTokenCountFromUsage / GetBillingTokens)
- [x] Extended thinking 预算分配(EffectiveContextWindowWithThinking)
- [x] 动态上下文窗口查询(OnModelSwitch)
- [x] 混合估算法(EstimateCurrentUsage: 精确锚点 + 粗估增量)
- [x] Sibling 回溯(并行工具调用 api_response_id 追踪)
- [x] 有效窗口多层计算(AutoCompact / Warning / Error / Blocking 四级阈值)
- [x] Observer 埋点(token_budget_estimated / token_budget_model_switch_overflow)
- [x] Engine 集成(runLoop 中的 token 警告 + 压缩阈值使用 TokenBudgetManager)
- [x] 完整测试覆盖(38 个测试用例)
7.5 查询链追踪 ✅
- [x] 查询链 UUID(QueryChainTracking.ChainId,chain__ 格式)
- [x] 分叉点记录(Fork 继承 ChainId + Depth+1 + ParentAgentId)
- [x] 成本归因(Observer 事件自动注入 query_chain_id,BigQuery 可按链汇总 token/成本)
7.6 查询循环防御性 ✅
- [x] 工具执行超时:优雅终止 + 返回已收集的部分输出(bash.go ExecTimeout + timedOut 标记)
- [x] 工具结果超大:截断 + 存磁盘 + 返回摘要+路径(ResultStore)
- [x] 文件引用不存在:InputProcessor 报错 + 保留原始文本(input.go)
- [x] 图片读取失败:FileRead 返回错误消息 + 主流程继续(不 crash)
- [x] 工具不存在:返回可用工具列表(orchestrator.go getToolSchemaHint)
- [x] 工具输入 JSON 无效:返回对应工具 InputSchema 提示(orchestrator.go + engine.go json.Valid() 预检)
- [x] 工具返回非 UTF-8:strings.ToValidUTF8 替换 + warning 标记(orchestrator.go)
- [x] 输入过大(>100K token):截断 + maxInputChars = 100_000 常量(engine.go runLoop)
8.1 错误分类精细化 ✅
- [x] HTTP 状态码 → 错误类型的完整映射(20种 ErrorCategory 枚举)
- [x] X-Anthropic-* header 检查(AnthropicClassifier: ratelimit/retry/reset)
- [x] AI 网关指纹识别(HTML 消毒 + CloudFlare title 提取 + overloaded_error 检测)
- [x] ErrorClassifier 接口 + Provider Adapter 模式(Default/Anthropic/Composite)
- [x] SSL 错误码目录(19码)+ 连接诊断 DiagnosticHinter 接口
- [x] RetryInfo 结构化(retry-after + x-should-retry + reset timestamp)
- [x] Token 差值解析(prompt_too_long 响应式压缩跳步)
- [x] client.go 集成:结构化 APIError 替代 fmt.Errorf
8.2 重试策略完善 ✅
- [x] RetryPolicy 接口 + CompositeRetryPolicy 叠加
- [x] ExponentialBackoff(指数退避+25%抖动+MaxDelay+MaxDuration)
- [x] ForegroundOnly(后台 529 不重试,防容量级联放大)
- [x] ConsecutiveLimit(连续 N 次同类错误放弃)
- [x] ServerDirective(尊重 x-should-retry)
- [x] Retryer 执行器(Do + Observer 回调 + context 取消)
- [x] ContextOverflowHandler(max_tokens 溢出自动修正)
- [x] Anthropic 子集:SubscriptionAwareRetry + FastModeCooldown + ModelFallback
- [x] NewAnthropicRetryPolicy 工厂函数(6层策略组合)
- [x] 49 个测试覆盖全部策略路径
8.3 API 预连接 ✅
- [x] TCP+TLS 握手预热(Preconnector.Warmup fire-and-forget HEAD)
- [x] HTTP keep-alive 配置(TransportConfig: MaxIdleConns/IdleTimeout/HTTP2)
- [x] DNS 预解析(DNSCache TTL 缓存 + Prefetch fire-and-forget)
- [x] WithTransport 选项(Client + Preconnector 共享连接池)
- [x] 14 个测试(预热/幂等/失败静默/共享池/DNS缓存/TTL过期)
8.4 SSE 边界情况 ✅
- [x] StreamGuard 流守卫(叠加在 parseSSE 裸解析之上)
- [x] 空响应检测(200 但无 SSE 事件 → 代理故障报错)
- [x] 部分流检测(有 message_start 但无 stop_reason → 网络中断报错)
- [x] 空闲看门狗(可配置超时默认 90s + 警告 + 主动中止流)
- [x] 停顿诊断(>30s 间隔记录 stall 次数/累计时间 + OnStall 回调)
- [x] Scanner 错误处理(bufio.ErrTooLong / I/O 错误)
- [x] 合法空响应识别(有 stop_reason 但无 content block 不误报)
- [x] 13 个测试覆盖全部边界路径
- [x] 429/529 重试:指数退避 + ForegroundOnly(后台 529 直接失败)(模块 8.2)
- [x] 401 认证失败:结构化 APIError 分类 + 用户提示(errors.go)
- [x] 模型返回不完整 JSON(tool_use input delta):json.Valid() 预检 + getToolSchemaHint 友好错误(engine.go)
9.1 Hook 输出影响行为 ✅
- [x] HookHandler 接口 + ShellHandler + CallbackHandler(CLI/SDK 双模式)
- [x] HookDef.Handler 字段(Handler > Command 优先级)
- [x] Manager.Execute 多后端适配(executeOne 自动选择后端)
- [x] ParseToolHookResponse(exit 2 / decision=block → 阻止工具,fail-open)
- [x] ParseStopHookResponse(exit ≠ 0 / decision=stop → 停止循环)
- [x] ParsePostCompactHookResponse(stdout → 带来源标记的补充摘要)
- [x] engine.go 5 个触发点:pre_tool_use(block 即短路)/ post_tool_use / permission / stop / session
- [x] permission_decision 埋点(hook 自动决策时记录)
- [x] 29 个测试(Handler/解析/Manager 多后端/阻止/停止/摘要)
9.2 pre/post-sampling hook ✅
- [x] pre-sampling: API 调用前触发(同步,exit 非零终止本轮,推送 WarningEvent)
- [x] post-sampling: API 响应后,工具执行前触发(异步 fire-and-forget,用 rootCtx 防泄漏)
9.3 插件级 Hook 注册 ✅
- [x] 插件 Hook 与全局 Hook 的优先级(注册顺序保证:全局先于插件执行)
- [x] 动态注册/取消(HookDef.Source + UnregisterBySource/UnregisterAllBySource)
- [x] Engine 插件-hook 桥接(syncPluginHooks + LoadPlugin/EnablePlugin/DisablePlugin)
- [x] 会话级 hook 注册(addFunctionHook/removeFunctionHook)→ 延期至未来模块,已记录扩展点注释
- [x] Hook 命令不存在:跳过 + 警告日志(ShellHandler exec.LookPath 失败 fail-open)
- [x] Hook 输出非 JSON:当作纯文本日志(ParseToolHookResponse 非 JSON 时忽略解析)
- [x] Hook 超时:终止子进程 + 继续主流程(Manager.Execute ctx 传播 + fail-open)
10.1 路径遍历防护 ✅
- [x]
../../../ 规范化验证(validateEntryName + confinedPath + validateBaseDir)
- [x] 符号链接目标检查(checkSymlinkWithMode + WithStrictSymlink 严格/宽松双模式)
- [x] 循环链接深度限制(filepath.EvalSymlinks 依赖 OS MAXSYMLINKS=40,注释说明)
- [x] isCJK 修复:移除 U+F900-U+FAFF 兼容区(防止同名异 codepoint 产生重复文件)
- [x] ScanMemoryDir 路径确认(filepath.Rel 验证扫描文件在 baseDir 内)
10.2 团队记忆同步 ✅
- [x] 云端同步机制(SyncAdapter 接口 + WithSyncAdapter + maybePull/maybePush)
- [x] 冲突解决(ConflictPolicy 四策略:LocalWins/ServerWins/Merge/Fail)
- [x] Pull 时机控制(PullPolicy:OnSessionStart/WithTTL/Always/Never)
- [x] GitSyncAdapter(独立/嵌入双模式,支持 ConflictFail 冲突检测)
- [x] NoopSyncAdapter(默认,向后兼容,IsAvailable=false 零开销)
- [x] HTTP 后端(HTTPSyncAdapter)→ 延期至 P2,已记录扩展点
10.3 自动提取代理 ✅
- [x] 后台 fork 运行(scheduleMemoryExtraction + SubAgent.historyMessages 传入对话历史)
- [x] 提取触发条件(ShouldExtract 每 5 轮一次,BuildPrompt 加 newMessageCount 精准定位)
- [x] 与主代理的互斥(hasMemoryWritesSince 扫描 assistant 消息;单飞+pendingSnap 后置补跑)
- [x] MemoryDirRestrict(SubAgent Edit/Write 只允许写入记忆目录)
- [x] memory.Store.Dir() 接口方法(hasMemoryWritesSince 和 MemoryDirRestrict 用)
- [x] BuildPrompt 并行读写策略提示词(Turn 1 所有 Read,Turn 2 所有 Write)
- [x] NewFileStoreWithBaseDir(测试辅助构造函数)
10.4 新鲜度警告 ✅
- [x] 超过 N 天的记忆警告(FreshnessText/FreshnessNote)
- [x] 配置化阈值(FreshnessConfig.GlobalThreshold + TypeOverrides,支持 sub-day)
- [x] MEMORY.md 索引双重截断(200行/25KB)+ WARNING 提示
- [x] UpdateIndex 用 FreshnessConfig 替换硬编码 30 天(IndexAnnotation)
- [x] CheckMemoryRelevance 运行时注入 FreshnessNote(按记忆类型分阈值)
- [x] Config.FreshnessConfig 注入链路(engine.New → fileStore → ReminderSystem)
11.0 工具名格式统一 ✅
- [x] mcp__serverName__toolName 格式(无条件前缀)
- [x] parseToolFullName 解析新格式
- [x] manager.go / bridge.go 全部更新
11.1 Transport 接口 + 多传输支持 ✅
- [x] Transport 接口(Send/Recv/Close,raw JSON 字节层)
- [x] AuthProvider 接口(per-request,支持动态 token 刷新)
- [x] StaticTokenAuth + NoopAuth 内置实现
- [x] StdioTransport(子进程管理 + newline-delimited JSON)
- [x] SSETransport(HTTP GET SSE + POST,2024-11-05 规范)
- [x] HTTPTransport(HTTP Streamable,2025-03-26 规范)
11.2 Client 重构 ✅
- [x] Client 使用 Transport 接口(不感知传输细节)
- [x] dispatchLoop 异步路由(JSON-RPC pending map 迁至 Client 层)
- [x] ToolListChanged 通知 → 脏标记 + 懒惰刷新
- [x] ConnectAll 并发限制(stdio≤2,remote≤5)
- [x] OAuth 由消费层实现 AuthProvider 接口注入(engine 核心不感知)
11.3 Schema 转换完整性 ✅
- [x] oneOf/anyOf 保留(不强制注入 type 字段)
- [x] 缺失 type 时补 "object"
11.4 资源预取和缓存 ✅
- [x] 资源缓存(ResourceCache TTL,键格式 serverName:uri)
- [x] 缓存过期策略(TTL,0=永不过期;ToolListChanged 联动 InvalidateServer)
- [x] Manager.ReadResource + InvalidateResourceCache + InvalidateServerResourceCache
11.5 Elicitation 处理 ✅
- [x] elicitation 消息解析(ElicitationCreateParams/Schema/Result 类型 + JSON 序列化)
- [x] 用户确认流程集成(ElicitationHandler 接口 + NoopElicitationHandler + func 适配器)
- [x] dispatchLoop 识别 server-to-client 请求(有 ID + 有 Method → handleServerRequest)
- [x] Client.SetElicitationHandler + handleElicitationCreate + sendServerResponse
- [x] Manager.SetElicitationHandler + ConnectAll/ConnectOne 注入(Initialize 前)
- [x] engine.Config.ElicitationHandler 字段
- [x] engine.ElicitationHandler 接口 + ElicitationHandlerFunc 适配器
11.6 MCP 防御性 ✅
- [x] MCP 服务器启动失败:跳过该服务器 + 警告 + 其他服务器不受影响(ConnectAll 独立 error 收集)
- [x] MCP 工具调用超时:ctx 超时传播 → Transport 返回 error → 工具返回 error result
- [x] MCP 服务器崩溃中途:Client.onClose 回调(dispatchLoop EOF 触发)+ Manager.reconnectLoop(5次指数退避±25%抖动)+ removeServerOnFailure(清 clients/toolCache/resourceCache + onServerRemoved 回调 + Observer 事件 mcp_reconnecting/mcp_reconnected/mcp_reconnect_failed/mcp_server_removed),10 个测试
12.1 依赖解析 ✅
- [x] DFS 递归依赖解析(ResolveClosure,三色标记拓扑排序)
- [x] 循环依赖检测(ErrDependencyCycle)
- [x] 依赖缺失检测(ErrDependencyMissing)
- [x] 跨来源依赖拒绝(ErrDependencyCrossSource:项目级→用户级禁止)
- [x] VerifyAndDemote 固定点迭代传播禁用
- [x] FindReverseDependents 反向依赖查询
12.2 LoadResult + 结构化错误 ✅
- [x] LoadResult{Enabled/Disabled/Errors/Warnings} - 单个插件失败不阻断其他
- [x] PluginError 10 种错误码(manifest/skills/hooks/mcp/依赖/冲突)
- [x] ValidationResult 用于预检(ValidateManifest 独立于加载流程)
12.3 Claude Code 格式兼容 ✅
- [x] manifest mcpServers 字段与 Claude Code 格式对齐
- [x] mcp_servers 旧字段向后兼容(UnmarshalJSON 双字段)
- [x] Plugin.Source 字段(Builtin/User/Project 三级)
12.4 SDK 内置注册 ✅
- [x] RegisterBuiltin(BuiltinDef) - 无文件系统代码注册插件
- [x] ErrBuiltinConflict - 与文件系统插件冲突检测
12.5 Plugin 完整性校验 ✅ (原 DXT/MCPB Bundle, 2026-04-15 反转重命名)
- [x] ~~Bundle 解压 (.dxt/.mcpb)~~ 放弃. 2026-04-15 经 8 轮反向推敲后决策: Anthropic
.mcpb PKCS#7 桌面应用范式不适合 Go 引擎 (shell out openssl), 且业内流行的开源 agent 项目 (如 OpenClaw / LangChain / OpenHands) 都不支持 .mcpb bundle 格式. Flyto 走 "本地目录 + 语言无关 MCP subprocess" 模型, 不兼容 .mcpb bundle. 删除 core/pkg/plugin/bundle.go + bundle_test.go (净删 500 行). 详见 core/pkg/plugin/doc.go 的"与其他 agent 生态对比"段落.
- [x] 插件完整性校验 (SHA-256 sidecar 模型) - 2026-04-15 完成. 替换原 scaffolding (commit 58c3ab5 的 placeholderEd25519Verifier + SecurityMetadata) 为真实现:
core/pkg/plugin/integrity.go 提供 SHA256IntegrityVerifier (宽松模式, 基于可选 plugin.checksum sidecar 文件) + RejectUnsignedVerifier (严格模式 decorator) + ComputeChecksum / WriteChecksumFile 公开 SDK (供第三方打包器 / CI / 未来 flyto plugin checksum CLI 使用). 威胁模型: 防本地篡改 (其他进程 / Dropbox 同步 / 开发者误改), 不防远程伪造 (需未来 Ed25519/Sshsig 实现). 哈希算法: filepath.WalkDir 排序 + relpath + \n + length_u64_be + content 流式 SHA-256, 跨平台确定性, 排除 plugin.checksum 自身防自引用. 12 个单元测试全部通过 (含 round-trip / 篡改检测 / plugin.json 关键文件保护 / 严格模式 / bad hex / 幂等). SecurityMetadata manifest 字段已删除 (改用 sidecar 避免循环依赖). SignatureVerifier interface 契约改为 Verify(dir, *Manifest) error, 未来 Ed25519/Sshsig/Sigstore/PKCS7 可作为新实现接入. 详见 integrity.go 文件头的"威胁模型"和"哈希算法"段落.
12.6 插件配置 Schema ✅
- [x] ConfigFieldDef(Key/Type/Required/Default/Description/Secret)
- [x] ValidatePluginConfig:Required 字段缺失 → ErrConfigValidation;类型约束验证
- [x] maskSecretFields:Secret=true 字段日志脱敏
- [x] pluginConfigStore 两层 map(插件名 → configMap,线程安全)
- [x] Host.GetPluginConfig / GetPluginConfigField / SetPluginConfig
- [x] Manifest.ConfigSchema 字段(向后兼容,nil 时跳过验证)
- [x] PluginToolDef 结构 (manifest.tools 字段): Name / Description / InputSchema / Command / Args / Env / WorkDir / TimeoutSeconds / PermissionClass / ConcurrencySafe / ReadOnly / Destructive
- [x]
pluginShellTool 实现 tools.Tool interface (Unix filter 范式: stdin JSON / stdout 结果 / exit code 状态)
- [x]
NewPluginShellTool(def, pluginDir, pluginName) 公开 factory - 让 SDK 嵌入场景可以手动构造 tool
- [x]
loadPluginTools loader 内部函数 - 把 manifest.Tools 翻译成 []tools.Tool, 跳过非法声明
- [x]
Plugin.Tools []tools.Tool 字段 - 存运行时实例
- [x]
Host.GetAllTools() - 收集所有启用 plugin 的 declarative tool, 供消费层注册到 tools.Registry
- [x] Process group kill (plugin_tool_unix.go + plugin_tool_windows.go): 子进程用 Setpgid=true 放独立 group, cancel 时
syscall.Kill(-pid, SIGKILL) 连带终止 shell 孙进程 (经典 Docker/systemd 做法), 避免 /bin/sh -c 'sleep' 这类 subshell 的 timeout 失效问题. Windows 退化为 kill 单进程 (Job Object 方案未来再做)
- [x] 18 个单元测试覆盖 happy path / 非零 exit / timeout (含 process group kill 验证) / context cancel / env 注入 / stdin JSON / 默认 WorkDir / 相对 WorkDir / command not found / Metadata / 默认 PermClass / 默认 schema / 自定义 schema / loadPluginTools 跳过非法 / 空输入 / interface 编译检查
- [x] plugin 扩展 tool 从"只能 MCP server"升级为"MCP server 或 declarative" 两条路径, 详见
core/pkg/plugin/doc.go 和 core/pkg/plugin/plugin_tool.go 文件头
12.8 Plugin MCP server engine 集成 ✅ (2026-04-15 新增)
- [x]
Engine.mcpMgr *mcp.Manager 字段 (engine-owned, 不放 plugin.Host, plugin 包保持零 internal/mcp 依赖)
- [x]
syncPluginMCPServers() 方法 (clean-slate rebuild, 对称 syncPluginTools): Step1 关闭 plugin 前缀的 server + unregister proxy tool, Step2 遍历 Host.GetAllMCPServers() 用 ConnectOne 启动并发现 tool 注册为 mcpProxyTool
- [x]
mcpProxyTool 类型 (实现 tools.Tool interface) 转发 CallTool 到 mcp.Manager, 支持 text/image/resource/unknown 多类型 content flatten
- [x] server key 命名空间
plugin.<pluginName>.<serverName> + agent 侧 tool 名 <pluginName>:<serverName>/<toolName> 对齐 pluginShellTool 的 <plugin>:<tool> 约定
- [x]
parsePluginMCPServerKey 严格按 prefix + 首个 dot 分割, 区分 plugin-owned 和 user-configured 避免误伤 settings.json 的 server
- [x]
DisablePlugin 新增 shutdownPluginMCPServers step, 先 unregister proxy tool 再 CloseOne, fail-safe 顺序
- [x]
Engine.Close() 4a2 步骤调用 mcpMgr.CloseAll() 优先于 plugins.Close(), 避免 rootCancel 无法 kill stdio 孙进程
- [x]
New / LoadPlugin / EnablePlugin 三处调用点对称追加 syncPluginMCPServers
- [x] 9 个单元测试: 命名空间 round-trip / foreign key 拒绝 / tool name 格式 / ToolCallResult 转换 (text/error/nil/mixed) / InputSchema fallback / Execute 非 object 输入降级为 IsError Result
- [x] stdio / sse / http / ws 四种 transport 全启用 (internal/mcp 已支持, 无额外代码成本)
- [x]
internal/mcp/stdio_transport.go MCP stdio server subprocess env 隔离 - 2026-04-15 完成. 原 L102 无条件 cmd.Env = os.Environ() 继承 Flyto 主进程全部环境变量 (包括 ANTHROPIC_API_KEY / OPENAI_API_KEY / AWS_SECRET_ACCESS_KEY), plugin 的不可信 MCP server 可读到. 本次修复: (1) 新建 core/pkg/execenv 包提供共享的 MinimalEnv (白名单 PATH/HOME/LANG/LC_ALL + extra 覆盖) 和 ExpandEnvMap (${VAR} / $VAR 宿主 env 展开, 未设置变量硬 error). (2) stdio_transport.go 的 startProcess 改为 ExpandEnvMap + MinimalEnv, 启动期解析展开失败直接返回 error. (3) plugin_tool.go 删除本地 minimalExecEnv, 改用共享 execenv 包, 同时获得 ${VAR} 展开能力. 策略对 plugin-owned 和 user-configured MCP server 一视同仁不分叉 - 反向讨论结论: Flyto 目标场景 (飞驼云仓等 B2B SaaS) 下双轨制的宽松轨是结构性死代码. 前缀区分 (parsePluginMCPServerKey) 只服务 lifecycle 隔离, 和 env 策略正交. 测试: execenv 包 11 个单元测试 (白名单 / extra 覆盖 / ${VAR} 展开 / 未设置 error / 空字符串合法 / $$ 转义) + stdio transport 5 个集成测试 (NoOsEnvLeak / PathLookupStillWorks / EnvVarExpansion / MissingEnvVarFailsStartup / ExplicitEmptyEnvVarIsOK). 详见 core/pkg/execenv/env.go 文件头和 core/pkg/plugin/doc.go 的"子进程 env 隔离"段落. (P2 安全修复)
- [x] 🟢 stdio 真子进程集成测试 - 2026-04-15 完成. Go self-exec 模式: TestMain 检测
__FAKE_MCP_SERVER__ env var 将测试二进制分叉为 fake MCP server (echo/env_check/exit_server 三个工具), 端到端验证 StdioTransport → Client → Manager 全链路 JSON-RPC 通信. 5 个集成测试: (1) InitializeAndListTools - 完整握手 + 工具发现 + serverInfo 验证, (2) CallTool - 参数序列化→子进程解析→结果返回, (3) EnvIsolation - 宿主敏感 env 不泄漏 + 显式声明 env 传递 + PATH 白名单可用, (4) ManagerConnectAndCallTool - Manager.ConnectOne + AllTools mcp__server__tool 命名 + CallTool/CallToolByName 路由, (5) SubprocessExit - os.Exit(42) crash 检测 + IsAlive() + ProcessError(). 全量 93 个包测试绿 (5 新 + 88 已有). 精妙之处: env 传递本身验证了 execenv 隔离路径 -- __FAKE_MCP_SERVER__ 必须通过 MCPServerConfig.Env + ${VAR} 展开才能到达子进程. 文件: internal/mcp/stdio_integration_test.go (~300 行). (P3 feature)
- [x] 🟡 L511 审计衍生任务:
pkg/hooks/executor.go:71 hook executor env 泄漏 - 2026-04-15 完成. 原代码 cmd.Env = mergeEnv(os.Environ(), env) 无差别继承宿主全部 env, 第三方 plugin 的 hook 脚本可读到 ANTHROPIC_API_KEY. 设计分叉与 L511 不同: 审计发现 hook 场景下"用户自配 hook"是主流路径不是死代码 (引擎内部 lifecycle + settings.json 用户 hook 都走 Source==""), 一刀切白名单会断掉存量 git push / ssh-agent / $GITHUB_TOKEN 类用户 hook, 爆炸半径远大于威胁模型收益. 最终方案: 按 HookDef.PluginDir 分流 — PluginDir 非空 (plugin-owned) 走 execenv.MinimalEnv 白名单 + FLYTO_PLUGIN_ROOT 注入, 对齐 L511 威胁模型; PluginDir 空 (user-configured / 引擎内部 lifecycle) 保持 os.Environ() 继承, 对齐 Git hooks / systemd Exec= / npm pre-scripts 默认语义. 选 PluginDir 而非 Source 作为分叉信号, 因为 PluginDir 是更强的"来自插件"证据, Source 未来可能被 SaaS 多租户前缀污染. 反向讨论 + 跨行业 (GitHub Actions env: / systemd PassEnvironment= / npm scripts CVE 史) 升华见本次 session 设计记录. 测试: 3 条新回归 (TestExecutor_PluginHookEnvIsolation / TestExecutor_PluginHookReceivesPluginRoot / TestExecutor_UserHookInheritsEnv) + 全量 go test ./pkg/hooks/... 绿 + go build ./... 干净. 文档同步: pkg/plugin/doc.go 子进程 env 隔离段落新增"Hook executor 特例"小节. Follow-up (非阻塞): 给 plugin-owned hook 加 HookDef.Env 字段作显式逃生口 (走 execenv.ExpandEnvMap 做 ${VAR} 展开), 对齐 tools[].env / mcp_servers[].env, 朝"手机应用式权限清单"产品愿景迈进. (P1 安全修复, 和 L511 同类但设计分叉)
- [x] 🟢 L511 审计闭环扫描 follow-up (P3 一致性债): 2026-04-15 全仓
rg 'os\.Environ' 完整扫描, 发现原 3 点审计手册漏掉 2 个点. pkg/memory/sync_git.go:378 是引擎内部调用 git binary 的继承点, 需要 SSH_AUTH_SOCK / HOME / GIT_AUTHOR_, 审计为刻意继承不改. pkg/tools/builtin/bash.go:833 的 filterSensitiveEnv() 8 条黑名单 (+ bash_background.go:360 同源第二入口), 2026-04-15 L513 专项评估后删除. 评估路线: 先反向论证三个"不应升级白名单"的理由 (白名单边界在开放 shell 场景下无法枚举 / Permission 是主防线 / 云端 SaaS 沙盒会自然解决), 再反问"为什么保留黑名单", 发现 4 个结构性缺陷: (a) 枚举严重不全 — 漏 GOOGLE_API_KEY / AZURE_CLIENT_SECRET / AWS_ACCESS_KEY_ID / AWS_SESSION_TOKEN / NPM_TOKEN / KUBECONFIG / DATABASE_URL / HF_TOKEN / SLACK_BOT_TOKEN / MINIMAX_TOKEN_PLAN_KEY (本仓库自己在用的 key) 等 20+ 条, 过去一年多无人维护, 说明"加新敏感 env 同步枚举"的隐式纪律从未建立; (b) GH_TOKEN 是实锤 bug — 早期方案明确保留 GITHUB_TOKEN / GH_TOKEN 不过滤, 因为 gh CLI wrapper 需要, Flyto 反向过滤会在未来 Agent 用 gh 时直接挂; (c) 职责越界 — env 隔离应由消费层负责, 引擎假设 os.Environ() 就是该继承的, 消费层 (platform/ 层云端沙盒 / 未来 CLI 二进制) 在 engine.New 之前自行决定进程 env; (d) 防错了层 — 原黑名单的威胁模型是"用户把 API key 放在 shell env 里被 Agent 泄漏", 这是产品 UX 问题, 应靠把 SecretStore (engine.SetSecret / WithSecret) 的 CLI 入口做易用, 不是引擎里打补丁. 跨行业对齐: Claude Code 早期方案默认 return process.env 完全不过滤, 仅当 CLAUDE_CODE_SUBPROCESS_ENV_SCRUB 被 claude-code-action 在 allowed_non_write_users 场景自动打开时才启用 20 条黑名单 (威胁模型: 外部 PR 作者 prompt injection); Cursor / Aider / Jupyter / VS Code tasks 全都全继承. Flyto 架构比早期方案更干净: 把"云端 scrub 开关"职责完全推给 platform/ 层 (platform 在 engine.New 前 os.Unsetenv 即可, 一次保护所有 8 个入口), core 引擎保持最简. 反向思考覆盖"失去什么": 唯一失去场景是用户把秘密放 ~/.zshrc 不用 SecretStore 被 prompt injection 诱导 env|curl attacker, 4 个可接受理由 — SecretStore 是产品明确路径, Permission 层是主防线, 零生产影响 (无 CLI 二进制), 行业共识. 最终方案: 删除 filterSensitiveEnv + sensitiveEnvKeys (bash.go L58-71 + L831-846) + TestFilterSensitiveEnv (bash_test.go L431-444), bash.go:373 和 bash_background.go:360 改为 env := os.Environ(), bash_background.go 加 "os" import, bash_secret_ctx.go 注释移除对 filterSensitiveEnv 的引用, 顺便修 GH_TOKEN bug. 文档同步: pkg/plugin/doc.go 清单从"8 入口 3 类"改为"8 入口 2 类" (Bash 从类 C 上升为类 B, 类 C 消失), 新增"Bash 工具 env 策略: L513 审计删除黑名单" 大段 (~80 行, 归档 4 个删除原因 + 反向讨论 + 跨行业对齐 + 禁止修改方向 + follow-up); 审计规则 A/B/C → A/B + 废弃类 C 明示. 反向归档: 不再需要维护 sensitiveEnvKeys 枚举, CI 检查 lint 也不需要. Follow-up 已闭合 2026-04-15 (commit 4df876b): 审计前提错误 — CLI 入口不在 core/cmd/flyto/ 而在 tui/cmd/flyto/main.go (107 行, 早已存在). 兑现方式收敛到 -secret-env VAR1,VAR2: 从 os.Getenv 读值登记到 engine.SetSecret, fail-fast 不降级. 原清单中的 --secret KEY=VAL (shell history 泄漏) / --secret-file (文件格式维护成本) / OS keychain (跨平台依赖) 均已评估并放弃, 不作 follow-up. tui/ 定位同步冻结为 dogfood CLI + L513 兑现点, 不再加大功能, 见 memory project_tui_positioning.md. (P3 一致性债, 已闭合 2026-04-15*, 决策: 删除类 C 黑名单, 对齐类 B 全继承)
- [x] 🟢 L515 dogfood 首次端到端 smoke 审计: 2026-04-15 首次真实模型调用 dogfood (tui/cmd/flyto + -secret-env), Test 1-4 全 PASS, cost $0.1029. Finding 1 (已修):
tui/internal/render.go 的 truncate() 函数纯字节切片破坏 UTF-8 多字节字符, 中文/日文/emoji 被切半产生 U+FFFD 替换符. 通过 Test 2 读 FLYTO.md 发现 "反对理由" 的 "反" 被从字节中间切开. 修复: 新增 safeHeadBytes / safeTailBytes 用 utf8.RuneStart snap 切点到 rune 边界; 写 7 条回归测试含 [10,300] maxLen 全量 stress; dogfood 重跑验证 "反...的设计必须标注:" 干净. Finding 2 (不改, 归档): Read 工具在 checker.go:393-398 对非危险路径 auto-approve, dangerousPaths 覆盖 .ssh/passwd/shadow 但刻意不覆盖 .aws/.kube/.docker/.netrc/.pypirc 等秘密文件. 审计后决策保持不改: 扩展黑名单会重蹈 L513 覆辙 (枚举不全), 正确解法在 platform/internal/sandbox/ 容器沙盒 (云端路线). 注释归档落在 filesystem.go 的 dangerousPaths 声明上方. Memory: 扩展 project_sandbox_local_vs_cloud.md 的 Read + dangerousPaths 衍生归档段落. (P2 安全审计闭合 + P1 UTF-8 bug 修复, 双轨产出)
- [x] 🟢 L511 审计衍生任务:
pkg/evolve/tool_builder.go:325, 360 evolve 子进程 env 继承 - 2026-04-15 审计决策保持现状, 不改代码. 评估结论: evolve 的信任模型和 plugin tool 根本不同 — 脚本由 LLM 在用户当前 session 里生成 + ApprovalFunc 人类实时审批 + SecretGuard 持久化前扫密钥 + MaxPerSession 速率限制, 等价于"用户手敲 bash", 不应套 L511 第三方代码威胁模型. 产品价值硬依赖 env 继承 ($GITHUB_TOKEN / $GOOGLE_APPLICATION_CREDENTIALS / $DOCKER_PASSWORD 支撑"Agent 自主造工具"卖点), 切白名单会打碎核心 UX. 本地 Flyto 无进程沙盒 (对齐 Claude Code / Cursor / Aider / Copilot 业内默认), 此前提下 env 白名单只堵 1/10 攻击面 ($ANTHROPIC_API_KEY 显式读取), 文件系统 / 网络 / /proc 绕过都在射程外, 是安全剧场. 行业对比: Jupyter / VS Code tasks / Claude Code bash 工具全都全量继承, 唯一沙盒化的 OpenAI Code Interpreter 是云服务, 与本地 CLI 信任模型不同. 产品决策 (2026-04-15): (a) 本地 CLI 保持 os.Environ() 继承, 不引入沙盒; (b) 未来云端 SaaS (飞驼云仓等平台化产品) 必须上真实进程沙盒 (Docker / gVisor / Firecracker), 但封装属 platform/ 层而非 core/, 通过依赖注入替换 exec.Cmd 行为. 文档同步: pkg/plugin/doc.go 子进程 env 隔离段落新增"Evolve 工具的例外"大段说明 + pkg/evolve/tool_builder.go 两个 execute 函数加 NOTE 导流注释, 明确标记"禁止未来审计顺手改白名单". Memory: project_sandbox_local_vs_cloud.md 存档本地 vs 云端沙盒分叉决策. (P2 安全审计闭合, 刻意非修复)
13.1 预定义代理类型 🟢
- [x] Explore agent(优先 Glob/Grep,只读工具集)
- [x] Plan agent(任务分解,不执行)
- [x] Verification agent(验证流程)
13.2 工具过滤精细度 🟢
- [x] 不同代理模式不同白名单(三层过滤:父工具集 → AllowedTools 交集 → DisallowedTools 差集)
- [x] ALL_AGENT_DISALLOWED_TOOLS / ASYNC_AGENT_ALLOWED_TOOLS 分离(globalDisallowed + AgentDefinition.DisallowedTools)
13.3 Prompt Cache 共享 ✅
- [x] 子代理复用主代理 system prompt bytes:SharedSystemPromptBytes 三级优先级(SubAgentConfig 显式 → 父 Config 字段 → 独立渲染)
- [x] cache key 匹配:cachedToolDefs 快照 + toolDefsSnapshot(),SpawnSubAgent 读缓存不触发 Track(),防止 turnCount 偏移导致 cache_control 位置漂移
13.4 工具权限精细控制 ✅
- [x] MCP 前缀自动通过(mcp__ 工具跳过 AllowedTools 交集过滤,仍受 DisallowedTools/globalDisallowed 约束)
- [x] DefaultGlobalDisallowedTools 补齐(ExitPlanMode/EnterPlanMode/AskUserQuestion/TaskOutput/TaskStop + Agent)
- [x] AllowedSubAgentTypes 字段(限制此 Agent 可 spawn 的子 Agent 类型,canUseTool 运行时检查 agent_type JSON 字段)
- [x] BackgroundAllowedTools 字段(Background=true 时附加工具白名单,MCP 仍自动通过,仓储场景可定制)
- [x] AgentDefLoader 接口 + FileAgentDefLoader + ScanAgentDefsDir(从目录加载自定义 AgentDef YAML,与 ScanSkillsDir 对称)
14.1 SkillDef + SkillRegistry ✅
- [x] SkillDef 结构体(Name/Description/Context/AllowedTools/Model/Paths/AgentType...)
- [x] SkillRegistry(Register/RegisterBuiltin/RegisterAll/Get/List/Invoke)
- [x] Engine.skillRegistry 字段 + SkillRegistry() 访问器
- [x] SetupSkillTool(文件发现 + SkillTool 执行器绑定)
14.2 Frontmatter 完整支持 ✅
- [x] context: fork/inline 执行模式
- [x] agent 字段(fork 时的代理类型)
- [x] model 覆盖(model: inherit 支持)
- [x] user-invocable / argument-hint / version / when_to_use
- [x] allowed-tools: 单行逗号 + YAML 列表双格式
- [x] paths: glob 激活过滤字段(已解析,执行 P1)
- [x] glob 花括号保护({ts,tsx} 不被逗号拆分)
14.3 文件发现 ✅
- [x] ScanSkillsDir:子目录格式(name/SKILL.md)优先于扁平格式(name.md)
- [x] LoadSkillFile:单文件解析,Source + SkillDir 字段
- [x] ExpandPrompt:$ARGUMENTS / ${FLYTO_SKILL_DIR} / ${FLYTO_SESSION_ID} 替换
14.4 Inline/Fork 执行 ✅
- [x] invokeInline:展开模板,作为工具结果返回给 LLM
- [x] invokeFork:SpawnSubAgent,fork 深度限制(≤2),超限降级为 inline
- [x] 深度通过 context.Value 传播(hitchhiker pattern)
- [x] pkg/tools/builtin/skill.go - SkillExecutor 接口 + SkillResult + SkillEntryDesc
- [x] SkillTool 实现 tools.Tool 接口,SetExecutor 注入
- [x] 动态 Description 包含 Skill 列表(8000 字符截断)
- [x] inline/fork 输出格式(AllowedTools 信息性追加)
14.P1 ✅
- [x] paths glob 运行时激活过滤:SkillDef.Paths + MatchesCwd(glob+前缀双模式) + SkillRegistry.ListForCwd
- [x] IsSkillPrompt 标记:/斜杠命令引擎内展开,runConfig.isSkillPrompt=true
- [x] Session-level Hook 隔离:ResolveHooksMgr + NewCompositeManager,session hooks 不污染全局
- [x] hide-from-slash-command-tool frontmatter 字段(UserInvocable=false)
15.1 PromptBundle + BundleRegistry ✅
- [x] Section struct(Static/CacheBreak/Compute)
- [x] SectionRegistry 实例绑定缓存(Reset/Invalidate)
- [x] PromptBundle 接口(StaticSections + DynamicSections)
- [x] BundleKey{ModelFamily, Scenario} + BundleRegistry(Register/Resolve/SetDefault)
- [x] NewDefaultBundleRegistry 工厂(预装 claude+programming)
15.2 缓存边界优化 ✅
- [x] SYSTEM_PROMPT_DYNAMIC_BOUNDARY 改为结构化 SystemPromptBlock.CacheScope
- [x] 静态段落合并为 ephemeral 块(将来升级为 global scope)
- [x] 动态段落合并为 ephemeral 块(会话级缓存)
- [x] volatile 段落(DANGEROUS_uncached 等价)CacheScope="" 不缓存
- [x] buildAPIRequest 按块设置 cache_control
15.3 默认 Bundle(claude+programming)✅
- [x] 8 个静态 sections(对应早期方案 的 7 个函数 + git_protocol)
- [x] 6 个动态 sections(instructions/tool_descs/env_info/summarize/evolve/append)
- [x] Context value helpers 注入运行时状态(cwd/modelID/toolDescs 等)
15.4 SDK 扩展 API ✅
- [x] Engine.RegisterPromptBundle(key, bundle) 公开注册接口
- [x] Engine.ResetSectionCache() 手动失效
- [x] compact 后自动 Reset section cache
- [x] Config.Scenario + Config.ModelFamily 控制 Bundle 选择
- [x] inferModelFamily 从模型 ID 推断族("claude-*" → "claude")
15.7 中文 Bundle ✅
- [x] prompts_zh.go - 9 个静态段落完整中文翻译(语义与英文版一字不差)
- [x] NewChineseBundle() - 中文静态段落 + 继承 DefaultBundle 动态段落
- [x] ChineseBundleKeys() - 5 个中文模型族预设 key(qwen/deepseek/ernie/glm/hunyuan)
- [x] RegisterChineseBundle(registry) - 一键批量注册便捷函数
- [x] 9 个测试(静态中文验证,动态继承,BuildPromptBlocks 集成,批量注册)
15.6 P1 补充 ✅
- [x] BundleOverlay 类型(基于 base Bundle 覆盖指定 section,链式调用,其余继承)
- [x] OverrideStatic / OverrideDynamic / OverrideVolatile / OverrideSection 四种覆盖方式
- [x] NewBundleFromFunc 函数式 Bundle 工厂(快速原型/测试用)
- [x] WithPromptLanguage / PromptLanguageFromCtx context 语言偏好(P2 预留)
- [x] WithBundleKey(key) RunOption(per-request Bundle 切换,SaaS 多场景)
- [x] BundleKeyFor(modelFamily, scenario) 便捷构造函数
15.5 测试 ✅(47+ 测试)
- [x] SectionRegistry 缓存/重置/精确失效
- [x] BundleRegistry 精确匹配/回退/覆盖
- [x] BuildPromptBlocks 输出形状(缓存开关/volatile/空 section 跳过)
- [x] DefaultBundle 静态/动态 sections 验证
- [x] Builder 集成测试(BuildSystemPromptBlocks/向后兼容)
- [x] BundleOverlay 无覆盖/静态/动态/volatile/链式/未知名称/集成测试
- [x] BundleFromFunc 基本用例/nil 函数
- [x] PromptLanguage context helpers
16.1 Dream 引擎 ✅
- [x] 三层门槛触发:时间(24h)+ 会话数(5 次)+ 扫描节流(10 分钟)+ 文件锁
- [x] 分布式文件锁(flock),防止多进程并发 consolidation
- [x] 锁回滚机制(任务被 kill 时恢复 mtime)
- [x] mtime-as-state:lock 文件 mtime = lastConsolidatedAt(消除 crash 重复触发 bug)
- [x] SessionProvider 接口(跨行业抽象)+ FileSessionProvider(CLI JSONL 扫描)
- [x] PeriodicInterval:SDK/API 长驻进程定时触发(CLI 不需要)
16.2 Dream 四阶段提示 ✅
- [x] Phase 1 - Orient:ls 记忆目录 + 读 MEMORY.md
- [x] Phase 2 - Gather:transcript grep 示例(TranscriptDir 可选)
- [x] Phase 3 - Consolidate:合并信号到现有话题,转换相对日期,修正矛盾
- [x] Phase 4 - Prune:更新 MEMORY.md 索引,<200行/<25KB 双重限制
- [x] Session hint:传入自上次巩固以来的会话 ID 列表
- [x] Bash 只读约束注释(指令层 + MemoryDirRestrict 兜底)
16.3 DreamTask ✅
- [x] DreamTaskStore 后台任务注册和状态追踪(5 个阶段枚举)
- [x] Turns []DreamTurn 进度报告(30 条滚动窗口,空轮跳过)
- [x] FilesTouched 去重追踪(通过 onMessage 回调从 tool_use 提取 file_path)
- [x] 可中断(Context cancel + 锁回滚)
16.4 Stop Hook 集成 ✅
- [x] 查询循环结束时 RecordSession() + go CheckAndRun(rootCtx)
- [x] ActivityDream 引用计数(防止 Close() 误判空闲)
- [x] Engine.Close() 等待 Dream goroutine 退出(3s 超时保护)
16.5 SubAgent 扩展 ✅
- [x] RunSyncWithCallback(onTurn 回调,每 assistant 轮次触发)
- [x] ToolUseInfo(Name + FilePath 简化结构,消费方不需要解析 blocks)
17.1 计划生成(P0)✅
- [x] EnterPlanMode 工具(设置 plan 权限模式 + 保存 prePlanMode)
- [x] ExitPlanMode 工具(从文件读计划,触发 PlanApprovalEvent)
- [x] PlanStore 接口(FilePlanStore + MemoryPlanStore)
- [x] Word slug 唯一文件名(路径遍历防护)
- [x] ApprovalPolicy 接口(单用户/多人/自动审批可扩展)
17.2 计划步骤(P1)✅
- [x] PlanStep{ID, Description, Tools, Complexity, Deps} 依赖图
- [x] 按步骤顺序或并行执行(消费方决策,引擎暴露步骤即可)- ReadySteps() + Kahn 算法封装
- [x] 步骤失败时的重规划 - RegisterStep 动态追加 + SkipDependents 拓扑传播
- [x] 进度追踪和报告 - PlanProgress + PlanProgressSnapshot + PlanProgressEvent + AttachProgress
18.1 协调器角色 ✅
- [x] Leader Agent 分解任务 → 分配给 Worker Agents(Team struct)
- [x] 专用系统提示(编排任务,不直接执行)- Config.IsOrchestrator + orchestratorGuidance 5条原则
- [x] Worker 工具集限制(通过 AgentDefinition.AllowedTools/DisallowedTools)
18.2 任务通知 ✅
- [x] Worker 完成时生成
<task-notification> XML 块
- [x] 包含:task-id, status, summary, agent-type, duration-ms
- [x] Leader 据此决定下一步(通过 pendingTeamNotifications 注入 runLoop)
18.3 Scratchpad ✅
- [x] Scratchpad TTL 键值暂存区(线程安全,惰性过期,Set/Get/Delete/Clear/Keys/Len)
- [x] scratchpad_write / scratchpad_read / scratchpad_list 三件套内置工具
- [x] ScratchpadStore 接口解循环导入(builtin 包不直接依赖 engine 包)
- [x] FileScratchpad 文件持久化(SHA-256文件名+原子写入+惰性过期,Config.ScratchpadDir触发,跨进程共享,25测试)
18.4 Worker 生命周期 ✅
- [x] 并行启动多个 Worker(sync.WaitGroup + goroutine)
- [x] Worker 失败处理(WorkerResult.Error 字段)
- [x] 资源清理(InboxRouter.Close)
19.1 核心接口 ✅
- [x] BridgeTransport 接口(SessionConn 工厂 + http.Handler)
- [x] SessionConn 接口(Send/Recv/Done/Close)
- [x] BridgeEvent(JSON 可序列化,带 Last-Event-ID)
- [x] ClientMessage(prompt/permission_reply/close 统一结构)
- [x] EventSerializer(engine.Event → BridgeEvent,atomic seq ID)
19.2 消息去重与批量上传 ✅
- [x] BoundedUUIDSet(环形数组+map,O(1) 去重,FIFO 淘汰)
- [x] SerialBatchEventUploader(串行+背压,loop 替代递归)
19.3 SSE Transport ✅
- [x] SSETransport(GET /events + POST /messages)
- [x] Last-Event-ID 断线重连(事件环形缓冲,ResumeWindow 条)
- [x] X-Accel-Buffering: no(nginx 防缓冲)
- [x] Ping keepalive goroutine(15s 注释行维持连接)
- [x] 写超时保护(防慢客户端 goroutine 泄漏)
20.1 会话生命周期管理 ✅
- [x] DaemonManager(goroutine pool,Accept 循环,双重检查创建)
- [x] SessionPool + capacityWake(chan struct{} 广播唤醒,O(1))
- [x] SessionIsolation 接口(SharedIsolation + IsolatedIsolation + 工厂)
20.2 健康检测 ✅
- [x] HeartbeatService(单 goroutine 扫描所有会话,O(n))
- [x] IdleTimer(可重置定时器,channel 信号控制,无 time.Timer Race)
- [x] CrashRecovery(指数退避重试,MaxRetries,OnCrash/OnGiveUp 回调)
20.3 远程计划(来自17.3)🟢 ✅
- [x] 本地进程提交计划到守护进程(FilePlanQueue + PlanCommandServer UDS)
- [x] 轮询执行进度和结果(Status + plan JSON 文件直读)
- [x] 步骤粒度状态追踪(StepExecStatus + onStepDone 实时更新)
- [x] 崩溃恢复(RecoverPending:running→pending at-least-once)
- [x] TTL 自动清理(24h 后删除终态计划文件)
- [x] Engine 接线(EnablePlanQueue/PlanQueueDir/PlanQueueSessionID,FLYTO_PLAN_SOCK)
- [x] 多租户 API Key(TenantConfig + map 索引 + authMiddleware)
- [x] SessionPool 容量控制(MaxSessions + 5s 等待超时)
- [x] 持久 SSE(GET /v1/sessions/{id}/events,长连接)
- [x] POST /v1/sessions/{id}/messages → 202 + 异步 runAndStream
- [x] Last-Event-ID 断线重连(事件环形缓冲 sseEventRecord)
- [x] 会话空闲超时(IdleTimer 集成)
- [x] 权限转发(HandlePermission + channel 桥接两个独立 HTTP 请求)
- [x] CORS / 速率限制 / 日志 / 恢复中间件继承自早期方案
- [x] SessionPool.Release() 修复:closed 状态下静默返回防 double-close panic
- [x] 测试:35 个测试覆盖全部 handler + 中间件 + 历史缓冲 + 速率限制
21.1 内存 Inbox(同进程通信)✅
- [x] 本地进程间消息传递(MemoryInbox,buffered channel 256)
- [x] 用于 Team Leader ↔ Worker 通信(InboxRouter)
- [x] UDS 实现(跨进程)- UDSServer fire-and-forget + FLYTO_SESSION_SOCK 注入 + MonitorTool(monitor_progress)
21.2 消息协议 🟢
- [x] 统一 Message 结构体(ID/Type/From/To/Timestamp/Payload)
- [x] 8 种消息类型(PermissionRequest/Response/IdleNotification/TaskAssignment/Shutdown等)
- [x] 各消息类型的 Payload 结构体 + JSON 序列化
- [x] InboxRouter(agentName → Inbox 映射,自动创建,线程安全)
22.1 API 交互细节 ✅
- [x] stop_reason 不可靠:不依赖 API 返回的 stop_reason,自己追踪 tool_use block(engine.go hasToolUseBlocks)
- [x] max_tokens 先低后高:默认 8K(p99 才 4.9K),截断后升级到 64K,仅一次(engine.go maxTokensEscalated)
- [x] 529 重试区分前台/后台:后台任务不重试,防止级联雪崩(api/retry IsForeground)
- [x] 空响应兜底:partial-stream 检测(收到 message_start 但无 content block),重试 ≤2 次(engine.go isPartialStream + ErrStreamTruncated)
- [x] 不完整 JSON 缓存:tool_use input 的 JSON 可能跨 delta,字符串拼接而非 partial 解析(engine.go block.partialJSON +=)
22.2 缓存和性能细节 ✅
- [x] TTL 缓存后台刷新:过期时返回旧值,后台 goroutine 更新,thundering herd 防护(internal/cache/ttl.go + 11 个测试)
- [x] LRU Peek vs Get:观察不改变驱逐顺序(filecache.go O(1) LRU + Peek 不更新 LRU)
- [x] API 预连接:初始化时 HEAD 请求预热 TCP+TLS(模块 8.3 Preconnector)
- [x] Prompt cache 内容 hash:PromptHashTracker SHA-256,变化时自动 ResetSectionCache(internal/cache/prompt_hash.go)
- [x] 子代理复用主代理 system prompt bytes:SharedSystemPromptBytes 三级优先级传递(subagent.go)
- [x] Per-tool schema hash:ToolSchemaTracker SHA-256 逐工具追踪,StableFirstWithBoundary 排序 + cache_control,防止不稳定工具破坏缓存前缀(internal/cache/tool_schema_hash.go + ~30 个测试)
22.3 安全细节 ✅
- [x] Bash 占位符随机盐:sed -i 解析中用 crypto/rand 8字节盐防 & 注入(sed_edit_parser.go)
- [x] 权限分类器截断兜底:max_tokens 导致空响应时降级到 DecisionAsk(classifier_ai.go collectStreamResponse stopReason)
- [x] 重定向目标静态性检查:区分
> file (静态) vs > $var (动态)(permission/redirect.go)
22.4 消息处理细节 ✅
- [x] FlushGate:历史消息 POST 期间排队新消息,保证顺序(engine.go FlushGate)
- [x] normalizeMessagesForAPI:孤立 tool_result 修复(ToolResultPairingNormalizer),OrphanThinkingFilter(normalize.go 9步管道)
- [x] 会话恢复:孤立 thinking 过滤 + 中断哨兵注入(session_manager.go ResumeSession + maybeInjectResumeSentinel)
INF-1 可观测性(EngineObserver)✅(与模块 0 相同,详见顶部)
- [x] EventObserver / MetricObserver / TraceObserver 接口
- [x] StderrObserver / NoopObserver / CompositeObserver / BufferedObserver
- [x] 所有关键路径埋点(API 调用,工具执行,权限检查,压缩,会话)
- [x] StrictMode(严格模式:测试环境异常时 panic 而非静默修复)
- [x] MockObserver(测试用)
- [x] ToolCapability 协议(DryRun / Reversible / CapabilityProvider 可选接口)
- [x] FileEdit 前自动备份原文件
- [x] FileWrite 前自动备份(如果文件已存在)
- [x] Memory Save 前自动备份(Backupper 接口解耦循环依赖,InjectBackupper 后置注入)
- [x] 备份存储在 ~/.flyto/history//(内容寻址去重)
- [x] 回滚 API:Engine.Rollback(ctx, messageID)
- [x] 备份清理(FileHistory.Prune:MaxAgeDays=30 + MaxVersions=50 两阶段清理)
- [x] Observer 埋点:每次备份/回滚记录事件
- [x] FileEdit / FileWrite 实现 DryRunnable + Reversible + CapabilityProvider
- [x] OperationLog 统一操作日志(Saga 补偿模式)
- [x] Orchestrator 自动为 Reversible 工具生成 UndoInfo
- [x] Engine.runLoop 工具执行后记录 OperationLog
INF-3 优雅关闭 ✅
- [x] Engine.Close() 分层关闭:防重入(atomic) → cancel root context → 等待 → 分层清理 → Observer 最后刷新
- [x] Root context 贯穿引擎生命周期(Dream/Memory extraction 用 e.rootCtx 而非 background)
- [x] closeWithTimeout 每个资源独立超时保护(一个挂起不阻塞其他)
- [x] observerCloser 接口检测(BufferedObserver/CompositeObserver/自定义实现统一关闭)
- [x] Config.CloseTimeout 可配置(默认 10s,仓储 daemon 可设 30s)
- [x] engine_closing / engine_closed Observer 事件
- [x] Engine.Closed() / Engine.Context() 暴露生命周期状态
- [x] SIGINT/SIGTERM 处理:Engine 只提供 Close(),信号处理在 cmd/server 层(Engine 是库不是进程)
- [x] 10 个测试(防重入/context 取消/Observer 刷新/超时/事件)
INF-4 会话活动追踪 ✅
- [x] ActivityTracker 核心(引用计数 + reason 分类 + 线程安全)
- [x] 心跳机制(busy 时 HeartbeatInterval 定时触发 OnHeartbeat)
- [x] 空闲检测(IdleDelay 延迟后触发 OnIdle,新活动可取消)
- [x] OnBusy/OnIdle/OnHeartbeat 回调(CLI/SDK/API/daemon 通用)
- [x] Engine 集成:API 调用 + 工具执行 Start/Stop
- [x] Config.ActivityConfig 可配置
- [x] Engine.Activity() 暴露追踪器
- [x] Close 时停止定时器 + 记录最终统计
- [x] Observer 事件:engine_busy / engine_idle / engine_heartbeat / activity_tracker_closed
- [x] 缺失埋点补齐:subagent_background_start/complete,session_closed,memory_extraction_start/complete
- [x] 12 个测试覆盖 Start/Stop/OnBusy/OnIdle/OnHeartbeat/Close/Observer
INF-7 引擎竞态修复 ✅(2026-04-07)
- [x]
pkg/security/audit_test.go - NoopAuditSink + CompositeAuditSink 测试(10 个用例,含跨行业 Extra 字段验证)
- [x]
close_test.go idleCalled bool → atomic.Bool(OnIdle 回调从 timer goroutine 写,无锁 plain bool = DATA RACE)
- [x]
activity.go heartbeat goroutine 捕获 refcount 快照(lock 释放后再读 t.refcount = DATA RACE)
- [x]
close_test.go eventCollector 加 sync.Mutex(Event() 从 timer goroutine + test goroutine 并发调用)
- [x]
bash_background.go BackgroundBashTask 加字段级 sync.RWMutex,Status/ExitCode/EndTime 通过 setFinal()/GetStatus() 保护
- [x]
stream_guard.go Watch goroutine 加 sync.WaitGroup,确保 idleWatchdog 退出后再 close(guardedCh)
- [x]
stdio_transport.go 加 waitOnce sync.Once,monitorProcess/cleanupProcess 通过 cmdWait() 保证 exec.Cmd.Wait() 只调用一次
- [x] 全量
-race 验证:20 个包全部通过,零 DATA RACE
- [x]
pkg/memory/memory.go - fileStore 加 sync.RWMutex:Save/Delete 写锁,List/FindRelevant 读锁,UpdateIndex 写锁(TOCTOU 修复:scan→build→write 原子化)
INF-5 安全审计 ✅
- [x] 秘密扫描:Memory 写入前检测 API key,密码等敏感信息
pkg/security/secret_rule.go - SecretRule + 45 条内置 gitleaks 规则
pkg/security/secret_scanner.go - SecretGuard 接口 + DefaultSecretGuard(默认全路径,豁免白名单收窄)
pkg/security/secret_scanner.go - OnBlocked 回调(引擎注入,触发 secret_scan_blocked 事件)
pkg/tools/builtin/filewrite.go - NewFileWriteToolWithGuard 注入点
pkg/tools/builtin/fileedit.go - NewFileEditToolWithGuard 注入点(只扫 new_string)
pkg/memory/memory.go - WithSecretGuard option,Save() 扫描(防止 Agent 写入带 key 的记忆)
pkg/evolve/tool_builder.go - NewToolBuilderWithGuard,save() 扫描(防止 Script 含 key)
- [x] 操作审计日志:谁在什么时间对什么文件做了什么修改
pkg/security/audit.go - AuditEntry + AuditSink 接口 + NoopAuditSink + CompositeAuditSink
pkg/engine/audit_local.go - LocalAuditSink(JSONL 追加写入,默认 ~/.flyto/audit.jsonl)
pkg/engine/audit_observer.go - AuditObserver 桥接 EventObserver → AuditSink
engine.Config.AuditSink - 设置后自动叠加 AuditObserver 进 Observer 链
- [x] 仓储场景:库存数据变更审计(谁改了多少)
- AuditEntry.Extra map[string]string 支持跨行业扩展字段
- [x] 全路径接线(兼容性修复)
- ActivityTracker refcount 泄漏修复(hook 拦截所有工具时的 continue 路径)
- AuditObserver 通过 Config.AuditSink 自动注入(之前只实现未接线)
- secret_scan_blocked 事件通过 OnBlocked 回调触发(之前从未 fire)
- DreamEngine.Close() + Engine.Close() 接线(等待异步 Dream goroutine)
- ActivitySubAgent + ActivityDream 埋点(之前常量定义但从未使用)
INF-6 版本兼容 ✅
- [x] Transcript FormatVersion int(格式迁移用)+ EngineVersion string(audit 用)分层
- [x] Transcript MaxSupportedVersion 保护(新格式文件被旧引擎读取时明确报错)
- [x] Transcript 迁移骨架:MigrateFunc 注册表 + migrateTranscript()(当前空表,无历史债)
- [x] Frontmatter.Version int 字段(旧文件缺失规范化为 1,未知 key 静默忽略)
- [x] Memory frontmatter MaxSupportedVersion 保护(version > max 跳过文件)
- [x] Memory 迁移骨架:FrontmatterMigrateFunc 注册表 + migrateFrontmatter()(当前空表)
- [x] Save() 写入 version: 1;ScanMemoryDir 调用迁移骨架
引擎层--框架质量修复(代码审查)
- [x] 🔴 P0: compact.go 全局状态隔离(contextWindowProvider/compactModelProvider 迁入 Compressor 实例字段,修复多租户同进程竞争)
- [x] 🔴 P0: permission/checker.go 硬编码工具名(PermissionClass 动态注册表,第三方工具自动参与安全检查)
- [x] 🔴 P0: compact.go 裸 http.DefaultClient(CompactHTTPClient 接口 + SetHTTPClient 注入,引擎层可托管重试/认证)
引擎层--凭据安全
- [x] 🔴 P0: engine.SetSecret() 凭据注入接口(SecretStore + Redact + Environ;Bash 子进程 env 注入;工具输出 value-level 脱敏;WithSecret per-request RunOption)
- [x] 🟡 P1: OperationLog 凭据脱敏(Record 时 Output 使用 redactedOutput,防止明文 secret 进入持久日志;2026-04-07)
引擎层--SDK 编排能力
- [x] 🔴 P0: CheckpointEvent + WithCheckpointHandler RunOption(工具声明 RequiresCheckpoint=true;执行前 deny-safe 暂停确认;panic recovery;invokeCheckpointHandler)
- [x] 🟡 P1: checkpoint_suggested 事件自动识别(高风险命令模式匹配;2026-04-07)
permission.AnalyzeDanger 结构化危险分析(返回 DangerInfo{Reason, Pattern})
flyto.CheckpointSuggestedEvent 新事件类型(建议型,不阻塞执行流)
engine.emitCheckpointSuggested 在工具执行前分析 Bash/FileEdit/FileWrite
- 29 个 permission 单元测试 + 6 个 engine 集成测试
- [x] 🟢 ~~P2: 场景化编排文档(SSH/DB/系统配置三个场景完整示例)~~ (2026-04-17 拆分为 L952a-d): 原条目把"自动化 API 文档 / HTTP Swagger / 详细使用教程 / 非编程场景提示词工程"四件本质不同的事混为一条, 导致搁置一年不动. 产品经理反向挑战 "是提示词工程还是文档" + "谁说没有开发者 + Swagger 式自动化" 暴露混同. 拆分后每件事独立优先级:
- [x] 🟢 ~~L952a: Go SDK godoc 自动化文档发布~~ (P3, 2026-04-17 完成, 重心从 HTML 渲染转到 lint 防腐). 交付:
core/Makefile 三目标 (docs-lint 钉 staticcheck@v0.7.0 自举跑 ST1000/1020/1021, docs-serve 自举跑 pkgsite 本地 HTML, docs-install 固定版本装 GOPATH/bin). 清债: 141 个 ST1000 + 4 个 ST1020 全量清零. 141 个文件顶部描述注释块从 package 前整块迁移到后 (Go 惯例对齐, 保留 build tag 位置); 2 个缺 package doc 的包新建 doc.go (pkg/inbox, pkg/providers/shared); 4 个 exported method 注释笔误修 (engine.go ResultStoreRef/FileHistoryRef/OperationLogRef, session_snapshot.go BuildSnapshot). 反向决策: 原计划加 CONTRIBUTING 说明被砍, make help 自承载操作文档, 零文档熵增. 未做: CI 集成 (无 CI 平台), GitHub Pages 部署 (要 module 改名到 VCS 路径). go build/vet/test 全量验证 31 个 package 全绿.
- [x] 🟡 ~~L952d: 引擎层可复用 Section 库 (技术工具原语)~~ (P2, 2026-04-17 完成). 最终交付: 只做 DB Section, SSH / SystemConfig 经产品经理 5 次反向挑战后确认不做, L952d 结束.
- 完成的:
core/pkg/context/sections/db_ops.go 60 行提示词 + 37 行 Go 包装 + db_ops_test.go 7 个契约测试. 首次导出引擎公共 Section API. 读取数据块按早期方案 GrepTool head_limit 策略写成"判断力引导".
- 明确不做 SSH / SystemConfig (反向挑战结论):
- 维度错误: L952 原 TODO 列的 "SSH/DB/系统配置" 三元组本身维度混乱 — SSH 是动作载体 (远程 shell), 和 DB/系统配置的操作对象维度不同. SSH 属于 "运维场景" 下的一个手段, 不和其他两个并列.
- SSH 无独立维度: SSH 是 Bash 的远程子集, 应由 "BashOps Section" 或消费层"运维 Bundle" 覆盖, 不独立.
- SystemConfig 无独立维度: 是 FileEdit 在
/etc/* 上的路径特例, 应由消费层 Bundle 自定义 Section 处理, 引擎不管.
- 无工具链背书: 两者都没 L1011-1017 级别的专用工具链在建, Section 里能引用的都是通用工具 (Bash/FileEdit) — 写了也是空心训诫.
- 惯性推进识别: "顺着 DB 就推 SSH/SystemConfig" 是
feedback_evaluate_at_each_step 反模式典型 — 没有真实驱动力, 只有 TODO 三元组惯性.
- 未来新 Section 的判据 (三道闸门, 缺一不可):
- 独立对象维度 (不是别的 Section 的子集)
- 专用工具链背书 (不是通用工具的特例)
- 消费层有真实复用场景
- 模板已立:
core/pkg/context/sections/ 子包 + DBOps 导出模式 + 契约测试结构, 后续新 Section (如 NetworkOps / HTTPOps) 按此模板走, 独立立 TODO, 不再受 L952 三元组束缚.
引擎层(已就绪,无需再实现)
- [x]
tools.DryRunnable 接口(pkg/tools/tool.go)
- [x]
tools.Reversible 接口(pkg/tools/tool.go)
- [x]
tools.CapabilityProvider 接口(pkg/tools/tool.go)
- [x]
security.AuditSink / AuditEntry 接口(pkg/security/audit.go)
- [x]
permission.Handler 接口(pkg/permission/permission.go)
- [x]
engine.Config.AuditSink 注入点(pkg/engine/config.go)
CLI TUI 消费层(ccm/tui/ - P0+P1 完成, 新功能冻结 2026-04-15)
- [x] 🔴
tui/ 独立模块(go.mod + go.work + glamour/bubbletea/lipgloss)
- [x] 🔴 Bubble Tea 主模型 (
app.go) - 消息列表/工具块/Spinner/输入框/状态栏/覆盖层/思考块
- [x] 🔴 Spinner - 照抄早期方案帧序列(darwin/linux差异,10帧弹跳,80ms)
- [x] 🔴 工具块渲染 - 状态图标/Tab展开/折叠/进度描述/摘要
- [x] 🔴 状态栏 - model/cwd/tokens/cost 两端对齐自适应宽度
- [x] 🔴 引擎事件桥接 + Checkpoint 通道 + deny-safe 对话框
- [x] 🔴 权限对话框(黄色边框,y/n)+ Checkpoint 对话框(红色双边框,大写Y)
- [x] 🔴 30+ 斜杠命令(help/clear/model/cost/compact/exit/diff/commit/review/plan/status/session/export/rename/files/context/add-dir/fast/effort/theme/skills/mcp/hooks/permissions/config/memory/keybindings/btw/stats/debug/copy/branch/resume/agents)
- [x] 🔴 输入历史(↑↓导航,Ctrl+R 搜索覆盖层,draft 暂存)
- [x] 🔴 Markdown 渲染(glamour,GFM+代码高亮,快速路径跳过纯文本)
- [x] 🔴 思考块(T键展开/折叠,全局切换)
- [x] 🔴 ! bash 前缀直接执行(不经过模型)
- [x] 🔴 粘贴截断(>10000字符,占位符引用系统)
- [x] 🔴 覆盖层系统(模型选择器/历史搜索/帮助/状态/diff/权限/MCP/hooks)
- [x] 🔴 ANSI 直通(bash/grep 输出颜色原样透传到终端)
- [x] 🔴 非交互模式(-p flag)
- [x] 🟢 防闪烁三层修复(streamDirty+SpinnerTick延迟flush + maxLines=500行截断 + viewport.Height*3虚拟渲染)
- [x] 🟢 Ctrl+Z 挂起(tea.Suspend,fg 恢复)
- [x] 🟡 TurnStartEvent 携带 ContextWindowTokens(引擎下发模型规范,消费者无需硬编码)
- [x] 🟢 状态栏 context 进度条(████░░ 45%,绿/黄/红,引擎上报值驱动)
- [x] 🟢 Toast 通知系统(3s自动消失,max=3,SpinnerTick清理)
- [x] 🟢 Edit/Write工具块 inline diff(LCS行级,+N -N,最多30行,自动折叠context)
- [x] 🟢 Shift+Up 复制最近助手回复到输入框
- [x] 🟢 /doctor 环境诊断(API Key/工具/目录/剪贴板/模型信息)
- [x] 🟡 Session.Send 支持 RunOption(P1 - 运行时模型切换/checkpoint handler, 2026-04-15 完成: 签名加 opts ...RunOption, caller opts 在前 Session history 在后覆盖, 3 个单测 + daemon/platform server 0-arg 向后兼容)
agent-engine CLI 修复(2026-04-08 发现)
- [x] 🟢
--include-thinking flag:json/stream-json 默认不输出 thinking,显式加 flag 才输出(2026-04-08)
- [x] 🟢
--json-schema 跨 Provider 路径接通:flyto.ResponseFormat.JSONSchema + buildFlytoRequest + OpenAI wrapper + MiniMax Anthropic 模式 AdaptSchema(2026-04-08)
- [x] 🟢 Provider schema 适配补全:ollama/lmstudio 补 CheckToolCount+DereferenceSchema+AdaptSchema+ResponseFormat;minimax streamOpenAI + openrouter 补 AdaptSchema(2026-04-08)
- [x] 🟢 Gemini provider
NeedsThinking 接通:per-request ThinkingBudget + modelSupportsThinking() 检查(2026-04-08)
- [x] 🟢 Gemini
ResponseFormat 接通:geminiGenConfig 加 responseMimeType/responseSchema + Tools $ref 展开 + 6 个单元测试(2026-04-08)
消费层集成测试(2026-04-08 立项)
- [x] 🟡
tui/agent-engine/ os/exec 集成测试(8 个测试,2026-04-08)
- [x] 🟢
tools/capability-probe/ 集成测试(P2, 与 CAP-4 合并推进) - 2026-04-16 完成: CAP-4 JSON 输出(L1167)/持久化(L1169)/交叉验证(L1175) 全部就绪后, 新建 main_test.go 27 个测试: lookupDocumented 命中/未命中/MaxTools; boolCap nil/true/false; intPtr/boolPtr; capToTristate 7 分支(non-probed/bool/float64/nil/string); capToInt 4 分支; capToExhaustive 3 态; isFullyProbed 5 场景(nil/all/partial/empty/six-of-seven); buildModelCapabilities MaxTools 交叉验证 5 场景(match-non-exhaustive/mismatch-exhaustive/match-exhaustive/no-documented/zero-toolcount). 纯逻辑测试, 无 API 调用
INF-8.1 pkg/flyto 公共契约包 ✅
- [x]
pkg/flyto/doc.go - 包定位说明
- [x]
pkg/flyto/engine.go - Engine 接口 + RunOption(不透明类型)
- [x]
pkg/flyto/provider.go - ModelProvider 接口(纯工厂模式,GCD 非 LCM)
- [x]
pkg/flyto/events.go - 所有事件类型 + UsageEvent(provider→engine 用量汇报)
- [x]
pkg/flyto/message.go - Message/Block/Role 类型 + 便捷构造函数
- [x]
pkg/flyto/tool.go - Tool 结构体
- [x]
pkg/flyto/observer.go - EventObserver / MetricObserver / TraceObserver 接口
- [x]
pkg/flyto/errors.go - ErrorCode / EngineError / WrapError / IsRetryable
INF-8.2 内部协议适配器
- [x]
internal/wire/openai.go(原 openai_compat.go,已重命名)- OpenAI Chat Completions SSE 客户端
- 流式文本/Thinking/ToolUse 事件转换
- 工具调用 partial JSON 累积(finish_reason 时统一发出)
- mid-stream error 处理(OpenRouter 格式)
- FetchOllamaModels / FetchOpenAIModels / FetchOpenRouterModels
- ✅ 修复:reasoning_content(o1/o3 字符串)+ reasoning_details(OpenRouter 数组)替代旧 reasoning 字段
- ✅ 新增:completion_tokens_details.reasoning_tokens 字段
- [x]
internal/wire/anthropic.go - Anthropic Messages API SSE 解析器(独立于 client.go)
- 直接产出 flyto.Event(无中间 StreamEvent 类型)
- 关键修复:cache tokens 只从 message_start 读取,避免 message_delta 双重计数
- [x]
internal/wire/gemini.go - Google Gemini SSE 解析器(第三种格式)✅(2026-04-07)
- 每块是完整 GenerateContentResponse,thought: true 标记 thinking
- functionCall 整体到达(无流式参数拼接),usageMetadata.cachedContentTokenCount
- crypto/rand 生成工具调用 ID(Gemini API 不分配 ID)
- flytoMessagesToGemini:ToolUseID → ToolName 映射(functionResponse 需要函数名)
- 支持 Google AI(API key 查询参数)和 Vertex AI(Bearer token)双认证模式
INF-8.3 Provider 工厂实现 ✅
- [x]
pkg/providers/anthropic/ - 包装 internal/transport/client.go,支持 Thinking/Caching
- [x]
pkg/providers/minimax/ - 三种模式(Native/OpenAI/Anthropic),默认 Native
- [x]
pkg/providers/openai/ - 静态模型表(GPT-4o/4.1/5/o1/o3/o3-mini)
- [x]
pkg/providers/openrouter/ - Live 拉取 /api/v1/models,统一 reasoning 参数
- [x]
pkg/providers/ollama/ - 本地 Ollama,Live 拉取 /api/tags
- [x]
pkg/providers/lmstudio/ - 本地 LM Studio,Live 拉取 /v1/models
INF-8.4 SSE 架构重构 ✅(2026-04)
- [x]
internal/transport/client.go 瘦身:删除 SSE 解析代码(parseSSE/handleSSEEvent/sendEvent/sseXxx structs)
- client.go 变为纯 HTTP transport;SSE 解析责任完全移至 wire 包
- [x]
internal/transport/stream_guard.go 泛化:从 api.StreamEvent → flyto.Event
- 13 个测试全部更新为 flyto.Event 语义
- [x]
pkg/providers/anthropic/provider.go:删除 convertStream goroutine(已无需二次转换)
- [x]
pkg/providers/minimax/provider.go:删除 convertAnthropicStream(同上)
- [x]
pkg/permission/classifier_ai.go:事件循环迁移为 flyto.Event 类型断言
- [x]
pkg/engine/engine.go:主流式循环迁移为 flyto.Event(删除 blocks 状态机 ~100 行)
- [x]
pkg/engine/subagent.go:子 Agent 流式循环同步迁移
INF-8.5 待完成(P1)
- [x] ✅
internal/wire/openai.go 修复(重命名自 openai_compat.go,2026-04-07)
- [x] ✅
internal/wire/gemini.go - Gemini native SSE 解析器(2026-04-07)
- [x] ✅
pkg/providers/gemini/ - Gemini provider 工厂(Google AI / Vertex AI,2026-04-07)
- Google AI Studio(API key)和 Vertex AI(Bearer token)双认证模式
- 静态模型表:Gemini 2.5 Pro/Flash,2.0 Flash,1.5 Pro/Flash/Flash-8B(含 thinking 变体)
- [x] ✅
pkg/engine/engine.go Config 增加 Provider flyto.ModelProvider 字段(2026-04-07)
- Provider 非 nil 时通过 flyto.ModelProvider 接口调用;nil 时回退 api.Client(向后兼容)
- 新增 buildFlytoRequest / apiMessagesToFlyto / apiToolDefsToFlyto 转换辅助函数
- 转换时跳过 thinking 块(Anthropic 专有,非 Anthropic provider 不支持)
- [x] ✅
pkg/engine/events.go 改为 pkg/flyto 完整类型别名(2026-04-07)
- 解法:将 flyto.Event.eventType() → exported EventType(),解除密封接口限制
- SubAgentEvent/PlanProgressEvent 留在 engine 包,直接实现 flyto.Event(导出方法无包限制)
- 4 个测试文件同步更新 eventType() → EventType()
- [x] 🟡 ~~P1: flysafe 迁移(从 BaseURL hack → minimax.New(Config{APIKey: "..."}))~~ - 已废弃(G9-C:Provider 必填,不再需要 BaseURL hack) - 2026-04-14 补打勾: 原 TODO 末尾作者已标 "已废弃" 但忘记改 checkbox, 本次 audit 粗筛发现并补打勾, 状态同步.
CAP-1: flyto.Request 意图字段 ✅(2026-04)
- [x]
pkg/flyto/provider.go:Request 加 NeedsThinking bool
- thinking 是有感知的(消费者主动声明):引擎无法判断业务复杂度,必须由消费者显式开启
- NeedsThinking=true → provider 按模型 SupportsThinking 自动注入 budget(不支持则 silently skip)
- 替代方案:<保留 Config 级别配置(ThinkingBudget)> - 否决:无法 per-request 动态控制
- [x]
pkg/flyto/provider.go:ModelInfo 加 CachingMinTokens int(官方文档 + probe 实测)
- [x] Caching 改为引擎自动决策(无感知):system 超过 CachingMinTokens 自动注入 cache_control,消费者无需声明
CAP-2: Provider 自动 Caching 决策 ✅(2026-04)
- [x]
pkg/providers/anthropic/provider.go:buildRequest 读 NeedsCaching + CachingMinTokens
- 估算 system prompt token 数(字符数 / 4 粗估),≥ 阈值自动加 SetSystemBlocks + cache_control
- Beta.PromptCaching 自动注入(消费者无需感知 beta header)
- [x]
pkg/providers/minimax/provider.go:Anthropic 兼容模式同步支持
- [x]
pkg/providers/openrouter/provider.go:SupportsCaching=false 路径直接 skip,无警告
- [x] 测试:system 刚好低于阈值 → 不加 cache_control;超过阈值 → 加;OpenRouter → 不加
CAP-3: StructuredOut Provider 自适应 ✅(2026-04)
- [x] Anthropic provider:ResponseFormat=json_object → 走 system 引导路径 + markdown fence strip
wrapFenceStrip goroutine:包装 channel,在引擎层统一 strip ```json...```(消费者无需关心)
stripFences(s string) string:strip ```json\n...\n``` 格式
- [x] OpenAI compat:ResponseFormat=json_object 正常透传(已实现,保持)
- [x]
pkg/flyto/provider.go:ResponseFormat 注释补充"Anthropic 路径仅作 system 引导提示"
- [x]
internal/wire/schema.go:实现 DereferenceSchema(schema json.RawMessage) json.RawMessage
- 递归展开所有
$ref 引用(仅支持内部 $defs,外部 URL $ref 报错)
- 结果不含
$ref/$defs 字段,是完全展开的 inline schema
- [x]
pkg/providers/minimax/provider.go:发送工具前自动调用 DereferenceSchema
- [x]
pkg/providers/openrouter/provider.go:发送工具前自动调用 DereferenceSchema
(OpenRouter 自身无问题,但路由到 Gemini 时有 bug;统一处理,无害于其他模型)
- [x] 测试:$ref 嵌套引用展开正确;循环 $ref 检测并报错;无 $ref 的 schema 原样返回(6+ 个测试)
- [x]
internal/wire/schema.go:实现 AdaptSchema(schema json.RawMessage, provider string) json.RawMessage
- Anthropic:移除 minimum/maximum/multipleOf, minLength/maxLength, pattern;minItems > 1 降为 1;裁剪值写入 description
- OpenAI strict:移除 allOf/not/if/then/else/contains/prefixItems/unevaluatedItems
- MiniMax/OpenRouter/其他:原样返回(probe 确认全支持)
- [x] Anthropic provider buildRequest 调用 AdaptSchema
- [x] OpenAI provider Stream 调用 AdaptSchema(保守统一 openai-strict 裁剪)
- [x] 测试:Anthropic 路径 min/max 被移除并写入 description;OpenAI strict allOf 被移除;passthrough 验证;minItems 边界;空输入;递归裂剪(8 个测试)
- [x]
pkg/flyto/provider.go:ModelInfo 加 MaxTools int(0=未知/无限制)
- [x] 已知值:OpenAI Chat API=128(OpenAPI spec);Anthropic strict=20(官方文档);MiniMax=未找到上限@256(probe)
- [x]
internal/wire/CheckToolCount + 各 provider 接线:发送前检查 len(tools) > MaxTools
- Anthropic provider:modelMaxTools 函数 + CheckToolCount(strict 模式预留)
- MiniMax provider:minimaxMaxTools=0 + CheckToolCount(probe 实测 @256 未发现上限)
- OpenRouter provider:MaxTools=0(透传底层,自身无限制)
- OpenAI provider:openaiMaxTools=128 常量 + CheckToolCount + 静态表 MaxTools 字段(2026-04)
(不静默截断--截断会改变工具集语义;明确报错让消费者决策)
- [x] 测试:工具数 ≤ 限制通过;超出限制返回可读错误(wire/tools_test.go 4 个测试 + openai/provider_test.go 3 个测试)
CAP-8: Schema 复杂度限制保护 ✅(2026-04)
- [x]
internal/wire/schema.go:实现 ValidateSchemaComplexity(schema json.RawMessage, provider string) error
- OpenAI:嵌套深度 ≤ 10 检测;总属性数 ≤ 5000 检测;enum 值数 ≤ 1000 检测
- 返回
*SchemaComplexityError(可 type-assert)含 Dimension/Actual/Limit 结构化字段
- [x] 超出限制时返回可读 error("schema nesting_depth 12 exceeds OpenAI limit of 10")
- [x] 其他 provider 限制待 probe 验证后补充(当前非 openai-strict 始终返回 nil)
- [x] 测试:嵌套超限/不超限;总属性超限;enum 超限;其他 provider passthrough;边界值(8 个测试)
CAP-4: 能力探测自动化 🟢 P2
- [x]
tools/capability-probe/main.go:输出增加结构化 JSON(Markdown 为副产品) - 2026-04-15 审计关闭: 已实现. writeJSONReport (L705-729) 写入 ~/.flyto/capabilities/capabilities.json, main() L562 调用. CapabilityReport 含 SchemaVersion/GeneratedAt/Models, 每个能力有 Value/Source/Exhaustive/Evidence/Note 五字段. Markdown 保留为 human-readable fallback (printMatrix L554).
- 字段:provider / model / capabilities map / evidence(threshold,cr,rd 原始数字)/ timestamp
- [x] 探测结果持久化 - 2026-04-15 完成. 启动时
loadExistingReport() 从 ~/.flyto/capabilities/capabilities.json 加载已有数据; isFullyProbed() 检查 7 个可实测字段是否全部 source=probed; 全部 probed 的 target 跳过探测 (输出 ⏭ 跳过), 部分/未 probed 的重新探测. 新增 --force flag 忽略缓存全量重跑. 已有数据预加载到 allCapabilities map, 新结果覆盖旧数据, 保证 JSON 报告包含所有历史 target. --dry-run 也显示 [cached, skip] 标记. capToTristate/capToInt/capToExhaustive 三个 helper 从 cached Capability 重建 Markdown 矩阵行. (P2 feature)
- [x] MiniMax 工具数量上限 probe(文档未记录,需实测二分查找) - 2026-04-16 完成: M2.7-highspeed ≥128 (exhaustive=false, 测到 128 未触顶); M2.7 重跑成功 ≥128, 不再 529. 结合 provider.go 注释 "probed @256 no limit found", MiniMax 大概率无硬上限
- [x] MiniMax JSON Schema 特性支持 probe(enum/nested/array/$ref/数值约束等) - 2026-04-16 完成: 新增 probeSchemaFeatures() + trySchemaFeature(), 测试 4 项 schema 特性 (enum/nested_object/array/numeric_min_max), $ref 已由 SchemaRef 覆盖. probe() 步骤 8, 前置 ToolUse==tsYes. ModelCapabilities.SchemaFeatures map 持久化到 capabilities.json, 不纳入 isFullyProbed (补充数据). 实测: M2.7-highspeed 4/4 全 ✓; M2.7 因 529 ToolUse=ERR 跳过, 待重跑
- [x] Anthropic/OpenAI 工具数量边界验证(对照官方文档:strict≤20 / ≤128) - 2026-04-15 完成: documentedInfo 加 MaxTools *int 字段 + intPtr helper; documentedCapabilities 表 OpenAI gpt-4o 填 MaxTools=128 (OpenAPI spec), Anthropic 留 nil (normal mode 无上限, strict≤20 是 schema 约束非工具数); buildCapabilities() 步骤 4 交叉验证: probed exhaustive=true 且 probed!=documented 时写入 ProbeErrors, 所有有文档值的 target 记入 Evidence["documented"]; printMatrix ToolCount 列输出
N [doc:M] 格式, mismatch 时附 ⚠ 标记
已修复(本批 5 项)
- [x]
session_manager.go:279,393 - Session.closed 数据竞争:改为 IsClosed()
- [x]
session.go:104 - trackEvents goroutine 泄漏:传入 ctx,select 保护写 outCh
- [x]
audit_local.go:83 - Close 前无 fsync:Close 前加 file.Sync()
- [x]
session_persist.go:225 - sessionID 路径穿越:改用 filepath.Base(sessionID)
- [x]
worktree.go:79 - git 失败不清理目录:失败时调用 removeWorktree + RemoveAll
已修复(Session 生命周期 4 项,2026-04-09)
- [x]
session.go - Close() 无幂等保护:加 closeOnce sync.Once,多次调用安全
- [x]
session.go - Close() 不关闭 pending permission channels:遍历 pendingPermissions 发 false 并清空 map,唤醒所有 WaitForPermission goroutine
- [x]
session.go - trackEvents goroutine 在 session Close() 时无法退出:增加 done chan struct{} 字段,Close() 时广播,trackEvents select 增加 <-s.done 分支并 drain rawCh
- [x]
session.go - WaitForPermission goroutine 在 session Close() 时无法唤醒:select 增加 <-s.done 分支
可靠性
- [x] 🟡
session_manager.go:381 - autoSave goroutine 静默丢弃错误:改为记录到 stderr 或 observer(P2)✅已修复
- [x] 🟡
session_manager.go:351 - Close() 不等 cleanupLoop 退出就清空 sessions map:加 channel 同步(P2)✅已修复
- [x] 🟡
worktree.go:66 - TOCTOU:stat+clean 非原子(实际风险低,branchName 含 UnixNano)(P3)✅已修复(os.Lstat + symlink 分支处理;branchName 改用 crypto/rand 8字节后缀)
- [x] 🟢
file_history.go:305 - 备份写入非原子:WriteFile 中途崩溃留下截断文件,内容寻址 Stat 命中后回滚读到损坏数据(P3)✅已修复(tmp+rename 原子写入,参考 session_snapshot.go)
规范化管道
- [x] 🟡
norm_tool_result_pairing.go - 多次运行非幂等:合成 tool_result 应检查 Metadata["synthetic"] 跳过(P2)- 2026-04-13 audit 否决: False positive. 代码已通过阶段 2 基于 ID 的去重集合结构性幂等 (synthetic tool_result 追加后其 ToolUseID 进入 toolResultIDs, 下次 scan 不会再被认为 missing). 不改 production 代码, 但加 TestToolResultPairing_Idempotent 锁定 4 种 case 的 Run1→Run2→Run3 不变量防未来回归. commit 7995d22.
- [x] 🟡
norm_consecutive_role.go:35 - cloneMessage 浅拷贝:Content 中 Input map 共享引用(P3)✅已修复(逐元素深拷贝 Input map)
- [x] 🟢
norm_tool_result_pairing.go - 阶段3重复扫描 result 构建 allToolUseIDs/allToolResultIDs(P3)✅已修复(直接复用阶段2的 toolUseIDs/toolResultIDs)
历史债 / 魔法数字
- [x] 🟢
session_manager.go:284,session_persist.go:274,file_history.go:323 - 冒泡排序:改 sort.Slice(P3)✅审计后关闭(2026-04-15: 三文件全部已用 sort.Slice, session_persist 注释明确说"早期方案冒泡→改进 sort.Slice")
- [x] 🟢 多文件魔法数字 -
64(buffer),0700/0600/0755(权限),20MB,100(maxSnapshots) → 集中为常量(P3)✅审计后关闭(2026-04-15: 0700/0600 是 Go 惯用八进制比 const 更清晰, 64*1024 buffer 同理, maxSnapshots 是 struct 可配置字段非裸常量)
- [x] 🟢
norm_error_content_strip.go:92 - else 分支死代码(P3)✅已修复(2026-04-09:提前 continue 消除不可达分支)
- [x] 🟡
normalize.go:62 - AttachmentReorderer 注释掉,98 行死代码:待上层附件标记设计完成后激活或删除(P2)- 2026-04-13 audit: 文件已精简到 93 行, AttachmentReorderer 只剩 2 行 trip-wire 注释 (// &AttachmentReorderer{} + 说明), 98 行死代码不存在. 2 行保留作为 trip-wire 提醒未来实现附件标记时激活
- [x] 🟢
worktree.go:117 + audit_observer.go:117 - "agent-worktree-" 前缀两处硬编码 → 共享常量(P3)✅审计后关闭(2026-04-15: worktree.go L32 已是 const worktreeBranchPrefix, audit_observer.go 无此前缀引用)
- [x] 🟢
session_snapshot.go:79 - PartialToolUse 结构体从未填充 → 移除或补全(P3)✅审计后关闭(2026-04-15: L81-84 LEGACY 注释明确说明"为未来逐 block 保存扩展预留", 有意预留不是死代码)
- [x] 🟡
session_persist.go:222 + session_snapshot.go:149 - /tmp 降级路径:改用 os.UserCacheDir()(P2)- 2026-04-13 audit: 两处都已迁移. session_persist.go:223-224 和 session_snapshot.go:149-150 均有注释 "降级:用 cache 目录而非 /tmp" + os.UserCacheDir() 调用
抽象设计(需讨论,不直接改)
- [x] 🟡
session.go - pendingPermissions map+channel 模式:生命周期已修复(closeOnce+done channel),可进一步抽象为 PermissionResolver 类型(P2,可选重构)- 2026-04-13 audit 否决: TODO 作者自己标了 "(P2, 可选重构)" 和 "生命周期已修复", 是作者的诚实不确定性.audit 5 问确认: (1) pendingPermissions 是 Session 内部实现细节, 消费者通过 WaitForPermission/ResolvePermission 公共 API 交互, 看不到也不需要看到内部结构; (2) 现有代码注释明确说明 race/lifecycle 防御意图 + 两处 delete 幂等 + done channel 广播, session_test.go 有完整覆盖; (3) 抽成 PermissionResolver 只是把 30 行代码搬家, 不解锁任何新能力.与 L1190 同类 cargo cult SRP, 直接否决.详见 handoff §10.15
- [x] 🟡
file_history.go - FileHistory 职责过多(备份/回滚/清理/可观测性):拆分为 BackupManager + RollbackExecutor + RetentionPolicy(P2)- 2026-04-13 完成 (换实现): TODO 的 "拆成 3 个类" 框架是错的 - RetentionPolicy 已经是独立值对象, Backup 和 Rollback 是同一个"文件历史快照生命周期"概念不应硬拆, 且已有 memory.Backupper (1 方法, 写侧) + tools/builtin.FileHistoryRecorder (2 方法, 写侧) 两个 precedent 窄接口.真实缺口是 consumer-facing 只读查询路径 - Engine.FileHistoryRef() 返回具体 *FileHistory 具体类型, 消费者无法 mock 测试.修复: 新加 FileHistoryView 接口 (CanRollback + SnapshotCount, 只读) + Engine.FileHistoryView() 访问器, 保留 FileHistoryRef() 向后兼容.3 个新测试全通 (结构化 typing + mock 可插拔 + 真实 FileHistory 接口调用), 14 个原 FileHistory 测试零回归.这是 L1191 之后第二条落地的抽象改进, 同样复用"narrow role interface + 后置/结构化注入"模式.详见 handoff §10.13
- [x] 🟡
normalizer.go - Priority 隐式依赖链:改为显式 DependsOn 声明或文档化依赖图(P2)- 2026-04-13 完成 (文档化选项 + 契约测试): audit 发现 10 个 built-in normalizer 中只有 3 对真实依赖 (tool_result_pairing→orphan_tool_result / empty_message→whitespace_assistant / structural→consecutive_role / transformation→image_validator).改成完整 DependsOn + 拓扑排序是过度设计 (没有活跃外部消费者).改用文档化依赖图 + 契约测试: (1) normalize.go DefaultNormalizePipeline 注释里每个 priority 旁标注依赖原因 + 完整依赖图 + 消费者插入指南; (2) 新 normalize_ordering_contract_test.go 2 个测试 - OrderingContract 锁定精确顺序, DependencyInvariants 锁定 4 条偏序 (允许新增 normalizer 只要保持偏序).未来改 Priority 破坏依赖会立刻 fail CI, 零静默破坏风险.详见 handoff §10.14
- [x] 🟡
audit_observer.go:138 - secret_detected:rule_ids 暴露规则名:只记录 secret_detected:count=N(P1 安全)✅已修复
- [x] 🟡 ~~
audit_observer.go:65 - operation_recorded 缺少工具 Input 字段~~ (P2) - 2026-04-17 完成: 产品经理反向挑战"我们不是有凭据功能了吗?"推翻了原"需要新增 SecretGuard regex 脱敏层"的过度设计, 改为复用现有 SecretStore.Redact (CLAUDE.md 原则 10 叠加而非替换). 顺手修真 bug: 原 engine.go:3806 调用 OperationLog.Record 时 OperationEntry.Input 字段从未赋值, 导致事件 input_len 永远为 0 + audit Extra tool_input_bytes 永远不写入 (day-1 死代码). 完整改动: (a) Config.AuditIncludeToolInput bool (默认 false, 对齐早期方案双 env 才开) + Config.AuditInputMaxBytes int (超限截断); (b) AuditObserver.SetInputAudit(include, maxBytes) 后置注入 (与 SetToolRegistry 同模式, 不扩构造签名); (c) OperationLog.Record 事件 payload 加 tool_input 字段, 约定调用方必须传已 Redact 的 Input; (d) engine.go:3782 建 inputByID map + Record 调用点 redactedInput := effectiveSecrets.Redact(...) 与 Output 同层入口统一脱敏; (e) audit_observer.handleOperationRecorded 根据开关写 Extra["tool_input"] + 超 maxBytes 时加 Extra["tool_input_truncated"]="true". 新增 5 个测试 (audit_observer_l1223_test.go): DefaultOff / EnabledNoTruncation / Truncated / EnabledAtBoundary (边界不截断) / EnabledButNoInputField (防御空值). 全仓 go test ./... 零回归
- [x] 🟡
audit_observer.go:31 - AuditObserver 无接口难测试:抽出 Observer 接口(P2)- 2026-04-13 audit 否决: False positive. flyto.EventObserver 已是接口, security.AuditSink 已是接口, AuditObserver 正确实现 EventObserver 作为 concrete bridge. 测试通过 fake AuditSink 传入 NewAuditObserver 完全可行 (audit_local_test.go 已证). 再抽一层无实际收益
- [x] 🟡
audit_observer.go:173 - operationFromTool switch 违反开闭原则:工具注册时声明 Operation(P2)- 2026-04-13 完成: 加 Metadata.AuditOperation 字段 (同 PermissionClass 模式, 工具自描述跨切面语义), AuditObserver 查询 Registry.MetadataFor 优先, fallbackOperationFromTool 作为零回归兜底.22 文件改动: tool.go 新字段 + registry.go MetadataFor + audit_observer.go SetToolRegistry + engine.go wire 注入 + 4 测试 (registry 命中 / 未声明 fallback / nil registry / 未注册工具 fallback) + 20 builtin 工具各自声明 AuditOperation. 详见 handoff §10.12
已修复
- [x]
dream.go:587 - periodicDone 并发 Close() 导致 double-close panic:mu 保护 + 置 nil
- [x]
token_budget.go:517 - minInt 死函数(Go 1.18+ 内置 min):已删除
待修复
- [x] 🟡
dream.go - context.Background() 作为 periodic goroutine 的 rootCtx(parentEngine==nil 时):改为带 cancel 的 ctx(P2)- 2026-04-13 audit: 已修. dream.go:142-143 periodicCancel context.CancelFunc, line 487 rootCtx, de.periodicCancel = context.WithCancel(context.Background()), line 598-599 Close 时调用 cancel. 正是 TODO 要求的"带 cancel 的 ctx"
- [x] 🟡
plan_store.go:107 - FilePlanStore WritePlan/ReadPlan 无 I/O 锁,与 MemoryPlanStore 行为不一致(P2)- 2026-04-13 修复: audit 纠正 TODO 方向--加 sync.Mutex 只能保护同进程, 解决不了跨进程 (daemon 读 + CLI 写) 场景. 真正的修复是 tmp+rename 原子写入, 同时让 L106 撒谎的 "(原子写入)" 注释变真. POSIX 保证同文件系统 rename 原子, Read 永远看到 "某次完整 Write 的结果", 跨进程安全. 新增 TestFilePlanStore_AtomicWrite (200 写 + 1000 读并发) + TestFilePlanStore_NoTmpLeak. commit b974e1c.
- [x] 🟢
plan_command_server.go:121 - socket 路径硬编码 /tmp,多用户机器有安全隐患:改用 os.UserCacheDir(P2) — 2026-04-15 已修复: resolvePlanSockPath 两阶段策略 (UserCacheDir -> os.TempDir), 对齐 inbox/uds_server.go 已有模式. 同时修复 uds_server.go fallback 从硬编码 /tmp 改为 os.TempDir().
- [x] 🟢
dream.go:548 - saveSessionCountLocked 失败 fail-open:连续失败时应降级或报警(P3)✅审计后关闭(2026-04-15: L576-581 WriteFile 失败已有 observer.Error 报警, marshal 失败实际不可能发生)
- [x] 🟢
plan.go:273 - IsActive() 检查与 active 写入之间有无锁窗口(P3)✅审计后关闭(2026-04-15: 理论竞态但 benign, 最坏结果是返回 "not active" error, 修需持锁执行文件 I/O 代价过高)
- [x] 🟢
dream_lock.go:93 - _ = os.Chtimes(...) 静默忽略 mtime 更新失败(P3)✅已修复(2026-04-15: L94 已有日志, L121 Rollback 补 stderr 日志对齐)
- [x] 🟡
dream.go:415 - SpawnSubAgent 强制 .(* Engine) 类型断言:EngineRef 接口太窄(P2,大改动)✅已修复(G9-A:ForkSubAgent 接口)
- [x] 🟢
dream.go:317 - float64 * time.Duration 精度风险(已有 LEGACY 注释)(P3)✅审计后关闭(2026-04-15: L346-350 已改为 time.Duration(hours3600)time.Second 整秒换算, 有 ELEVATED 注释)
- [x] 🟢
plan_command_server.go:118 - sessionID 截断魔数 40 → const maxSessionIDSuffix(P3)✅审计后关闭(2026-04-15: L152 已是 const maxSessionIDSuffix = 40, TODO 行号漂移)
- [x] 🟢
dream_task.go:121 - AddFileTouched O(n) 线性扫描 → map[string]struct{}(P3)✅审计后关闭(2026-04-15: L129-136 已有 filesTouchedSet map O(1) 去重)
- [x] 🟢
dream_prompt.go - 多处魔法字符串/数字 → 命名常量(P3)✅审计后关闭(2026-04-15: "数字"全在 prompt 模板文本里(~150 chars, tail -50), 是自然语言近似值不是代码逻辑, 真常量 MaxIndexLines=200 已提取)
- [x] 🟢
token_budget.go - 10+ 魔法数字散布 → 集中常量(P3)✅审计后关闭(2026-04-15: 9 个数字全部已在 const 块里命名, TODO 描述与现状不符)
- [x] 🟢
plan_store.go:236 - validatePlanSlug 已定义但从未调用(P3)✅已修复(2026-04-15: WritePlan 写路径加 defense-in-depth 调用, 防范未来 slug 来源变更时路径遍历)
- [x] 🟢
plan_queue.go:437 - recovered 变量丢弃 _ = recovered:返回值或日志(P3)✅审计后关闭(2026-04-15: L437 已有 fmt.Fprintf(os.Stderr, "recovered %d pending plans"), TODO 描述与代码不符)
- [x] 🟡
plan_queue.go - FilePlanQueue 承担 5 种职责(队列/执行/持久化/取消/清理):拆分(P2)- 2026-04-13 audit 否决: PlanQueue interface (plan_queue.go:195) 已是顶层抽象, 注释明确说 "可实现 RedisPlanQueue (多副本共享), MemoryPlanQueue (测试)".消费者要换实现是重新实现 PlanQueue interface, 不是复用 FilePlanQueue 内部.把 FilePlanQueue 拆成 4 个类只是"把耦合搬进 4 个文件",语义上依然是"文件持久化的异步 plan 队列"一个整体.同 L1185 cargo cult SRP 模式, 否决不做
- [x] 🟡
plan.go - PlanModeManager God Object:权限模式相关逻辑拆出(P2)- 2026-04-13 audit 否决 (framing 错): PlanModeManager 实际 ~130 行, 5 字段, 6 方法 - 不是 God Object, TODO 用词夸大.permission 集成只有 2 字段 (prePlanMode + perms) 和 2 处调用 (Enter 时 perms.SetMode(ModePlan), Exit 时还原).plan mode 和 permission mode 语义耦合 - 进入 plan mode 必须切换 permission mode, 这是业务规则不是代码耦合.拆开会产生重复的"谁管 prePlanMode"状态机.否决不做
- [x] 🟡
dream.go - DreamEngine 通过 parentEngine 间接访问 Activity/Observer:直接注入字段(P2)- 2026-04-14 audit 关闭: 代码已在 2026-04-13 完成, 复选框漏勾. 证据: (1) dream.go:110-119 struct 已独立持 observer/activity/rootCtx 三字段, parentEngine 降级为"仅 ForkSubAgent"; (2) NewDreamEngine 构造器收 cfg.Observer/Activity/RootCtx 并 NoopObserver 兜底 (L231-242); (3) engine.go:711 buildDreamEngine(mem, reg, observer, activity, rootCtx) 签名已改, post-fill 已删, 构造顺序下移到 observer/activity/rootCtx 就绪之后; (4) grep de.parentEngine != nil && de.parentEngine. 代码残留 = 0 处 (L108 仅历史注释提到旧状态); (5) de.(observer|activity|rootCtx) 直接字段调用 = 17 处; (6) dream_test.go:609-690 已补 "nil observer fallback" 与 "injected observer" 两个 L1224 红利测试. handoff task 基于过时快照, 属任务队列时态错位
- [x] 🟢
context/compact.go:188 AutoCompactThreshold - 10 行包级函数与 engine.TokenBudgetManager 语义重复, 唯一 call site engine.go:3456 做 +-compactReserveTokens 抵消 roundtrip 实意 "拿物理窗口", 且内部 contextWindowProvider 的 setter 全代码零注入, model 参数永远被忽略返回 DefaultContextWindow - 2026-04-14 删除: engine.go:3456 直连 e.cfg.ModelRegistry().ContextWindow(model) (与 engine.go:2556 一致), 删除 compact.go:184 函数 + compact_test.go:32 测试 + engine.go:4002 unused const. contextWindowProvider 全局变量保留 (Compressor.effectiveContextWindow 仍依赖其作为实例级 contextWindowFn 为 nil 时兜底, 另一笔独立清理账)
- [x] 🟢
token_budget.go - 523 行 6 大功能合体:拆分 token_estimator + token_window + token_warnings(P3)- 2026-04-14 audit false positive: 现为 535 行活模块 (engine.go:91/702/1261/2360/2975 多处 call site).反向思考后结论: 6 个功能组共享 modelRegistry + observer 两个依赖,它们是同一抽象 TokenBudgetManager 的 6 个入口而非 6 个独立抽象.拆分无法降低扩展/修改成本 (IDE 跳转 O(1), 和文件行数无关), 反而引入"新方法放哪个文件"决策负担 + 跨文件跳转 (warnings 严重依赖 window)."535 行太长"是美学焦虑非工程痛点.维护说明已写入 token_budget.go 文件头.真抽象债沿"修改跨多个概念/新增场景绕开结构"识别, 而非行数代理指标.否决不做
已修复
- [x]
result_store.go:81 - 文件权限 0644 → 0600
- [x]
result_store.go:159 - truncateString 按字节截断 → 按 rune 截断
- [x]
team.go:163-169 - XML 注入:WorkerID/summary 未转义 → xmlEscape
- [x]
reminders.go - 四个魔法数字 → 命名常量
- [x]
filecache.go:119 - SHA256 截断条件恒真(死代码)→ 直接截取
- [x]
agent_loader.go - max_turns 手写整数解析 → strconv.Atoi
- [x]
skill_def.go - SkillRegistry 持有 *Engine 循环依赖 → SkillSpawner 接口
- [x]
reminders.go - FileStateCache 具体类型 → FileChangeChecker 接口
待修复
- [x] 🟡
input.go:328 - resolvePath 无目录边界校验,@../../etc/passwd 可读任意文件(P2)→ 已在 Phase 2 E1 修复
- [x] 🟡
skill_loader.go:306 - LoadSkillFile path 参数无路径穿越校验(P2)→ 已在 Phase 2 E2 修复
- [x] 🟢
skill_loader.go:261 - ScanSkillsDir 未校验符号链接,可跳出允许目录(P3)✅审计后关闭(2026-04-15: skills 目录是用户自控的 ~/.flyto/skills/, 攻击者能放 symlink = 已有 home dir 写权限, 安全收益近零)
- [x] 🟢
operation_log.go:37 - Input json.RawMessage 记录原始工具参数(含 API key 等敏感信息),未脱敏(P3)✅审计后关闭(2026-04-15: in-memory only 不持久化, observer 层已有 sanitizeSecrets, 工具输入通常是 file_path/content 不含 API key)
- [x] 🟡
tool_summary.go:74 - GenerateSummaryAsync goroutine 复用外部 ctx,ctx 取消后 goroutine 可能永久阻塞(P2)
- [x] 🟡
context_calibrator.go:95 - RecordFailure 持锁调用 save()(文件 I/O)→ JSON 序列化在锁内,文件 I/O 移到锁外(P2)
- [x] 🟡
context_calibrator.go:68 - NewContextWindowCalibrator 并发调用:load() 无锁,records map 并发读写(P2)- 2026-04-13 audit: 已在架构重组 commit c4a2273 修复 (load() line 157-159 写 records 时持有 c.mu.Lock), 测试 go test ./pkg/engine -race -run ContextWindow 全过. 与 L1251 是姐妹修复, 同次提交里完成但 TODO 当时只勾了 L1251
- [x] 🟡
subagent_registry.go:126 - watchCompletion goroutine 泄漏 → 加 stopCh + Close() 方法(P2)
- [x] 🟡
subagent_registry.go:131 - 回调在 mu 已释放后执行,访问注册表状态时有 data race(P2)- 2026-04-13 audit: 已在架构重组 commit c4a2273 修复 (watchCompletion line 158-164 CLEVER 注释明确说 "在锁内复制 callbacks slice, 锁外执行回调" 是 race-aware deliberate 设计), 测试 go test ./pkg/engine -race -run 'SubAgent|Registry' 全过. 与 L1253 是姐妹修复, 同次提交里完成但 TODO 当时只勾了 L1253. 行号 ":131" 在当前文件里是 Count() 的 defer RUnlock, 跟 callback 无关 - 文件被重构过, 行号 stale
- [x] 🟡
agent_loader.go:147 - LoadAgentDefFile os.ReadFile 无大小限制 → io.LimitReader(1MB)(P2)
- [x] 🟢
agent_executor.go:177 - defer cleanup() 返回值用 _ = 丢弃,panic 被静默忽略(P3)✅审计后关闭(2026-04-15: L180 已有 if err := cleanup(); err != nil { fmt.Fprintf(stderr) })
- [x] 🟢
filecache.go:214 - IsModified TOCTOU:RLock→读 ModTime→Unlock→os.Stat→Lock 窗口期文件可变(P3)✅审计后关闭(2026-04-15: 文件系统操作固有 TOCTOU, 持锁做 os.Stat 会降低并发性能, 保守策略 modified→re-read 已正确处理)
- [x] 🟢
file_scratchpad.go:121 - os.Rename 失败静默忽略(P3)✅审计后关闭(2026-04-15: L122 已有 fmt.Fprintf(os.Stderr, "rename failed"), TODO 描述与代码不符)
- [x] 🟢
context_calibrator.go:183 - save() 失败 fail-open,无降级或报警(P3)✅已修复(2026-04-15: persistData WriteFile/Rename 失败补 stderr 日志, 与 dream_lock/file_scratchpad 对齐)
- [x] 🟢
activity.go:371 - 手写 itoa 函数 → strconv.Itoa(P3)
- [x] 🟢
team.go:258 - enqueuTeamNotification 拼写错误(少一个 e)(P3)
- [x] 🟢
operation_log.go:41,65 - "success"/"failed"/"rolled_back" 散布字符串 → 常量(P3)
- [x] 🟢
fallback.go - FallbackConfig 只支持单层降级 → 改为 []string fallback 列表(P3)✅审计后关闭(2026-04-15: 单层是有意设计, MaxFallbacks=1 默认值印证. 多层降级是 feature request 非 bug, 当前 A→B 覆盖 90%+ 场景, 等产品需求再扩展)
已修复
- [x]
permission/permission.go - Engine 接口与 flyto.Engine 同名 → 改为 Checker
- [x]
inbox/uds_server.go - socket 权限依赖 OS 默认 → 显式 os.Chmod(0600)
- [x]
engine/migrate.go + memory/migrate.go - 迁移中途失败无回滚提示 → 加 LEGACY 注释
待修复
- [x] 🟡
memory/memory.go - WithXxx 构造注入 vs InjectXxx 后置注入两套模式 (P2, 原 P3 framing 错) - 2026-04-14 部分接线 + 重复退役 (commit ecd3c1f): 原 TODO framing "文档化两套模式" 是 P3, audit 后发现 (a) "循环依赖" 叙事破产 - "mem 先于 observer/fileHistory" 是偶然演化不是技术约束, (b) 9+ 个 With* 公共 API 仅 2 个 (SecretGuard/Freshness) 在生产接线. 第二轮 grep 纠正前一轮 audit 失误后确认 InjectBackupper/InjectMemorySelector 有真生产 call site (engine.go:683/766, 非死代码). 执行: 重排 engine.go 构造顺序 → buildMemory 5 参签名 → 接线 WithObserver/WithFileHistory/WithMemorySelector → 删 3 重复 API (InjectBackupper/InjectMemorySelector/NewFileStoreWithScorer) + 对应测试. 剩 4 条能力未接线, 作为独立 TODO 见下
- [x] 🟡
memory/memory.go WithScorer - RelevanceScorer 公共选项零 call site (P3) - 2026-04-16 接线: Config.MemoryScorer + buildMemory 传递. 外部消费者可通过引擎 API 注入自定义评分器 (嵌入向量/BM25)
- [x] 🟡
memory/memory.go WithTypeRegistry - MemoryTypeRegistry 公共选项零 call site (P2) - 2026-04-16 接线: Config.MemoryTypeRegistry + buildMemory 传递. 多租户/行业特化场景可注册自定义记忆类型
- [x] 🟡
memory/memory.go WithStrictSymlink - 符号链接严格模式零 call site (P2) - 2026-04-16 接线: Config.MemoryStrictSymlink bool + buildMemory 传递. 服务端部署可启用严格拒绝
- [x] 🟡
memory/memory.go WithSyncAdapter - Git/HTTP 远程同步零 call site (P2) - 2026-04-16 接线: Config.MemorySyncAdapter + MemorySyncConfig + buildMemory 传递. GitSyncAdapter 已完整实现 (483行), 外部消费者可直接使用
- [x] 🟢 ~~
memory/memory.go - FindRelevant AI 失败时 fallback 调用包级 SelectRelevant 函数,绕过 Store 接口抽象~~(P3)- 2026-04-14 修复: AI selector 失败时 fallback 传 nil scorer, 静默忽略 WithScorer 配置. 反转 Select 错误契约 + fallback 搬到 Store 层 + 加回归测试.
- [x] 🟢 ~~
flyto/observer.go - 三个 Observer 接口(Event/Metric/Trace)分散,消费者需同时注册三个~~(P3)- 2026-04-14 审核结论: 伪问题. 路径订正: 真实路径 core/pkg/flyto/observer.go. 原 TODO 描述"需同时注册三个"与代码注释 L5-7 明确矛盾, 实际机制是 EventObserver 必须项 + Metric/Trace 通过 type assertion 可选接入. 核实证据 (LSP findReferences): 引擎主路径 engine.go:2955/3458/3953 和 CompositeObserver 分发层 (observer.go:180-209) 共 6 处全部为 if m, ok := obs.(MetricObserver); ok 模式, e.observer 字段类型只是 EventObserver. 实现数不对称证实按需接入有效: EventObserver 20 实现 / MetricObserver 7 / TraceObserver 1. 这是 Interface Segregation Principle 教科书案例, 合并反而劣化 (13 个实现被迫写空 Metric, 19 个被迫写空 Span), 且会加剧 L1292 的结构化耦合. 同类"伪问题措辞"已在 L1287/L1289/L1290 出现.
- [x] 🟢 ~~
hooks/handler.go - HookHandler 接口只有一个方法,可简化为函数类型~~(P3)- 2026-04-14 审核结论: 伪问题. 路径订正: 真实路径 core/pkg/hooks/handler.go (TODO 原文漏 pkg/ 前缀). LSP findReferences + goToImplementation 核实: 接口已有 3 个实现 (HookHandlerFunc/ShellHandler/CallbackHandler), 其中 HookHandlerFunc (handler.go:42) 正是"简化为函数类型"诉求的既有解 (http.HandlerFunc 模式). 接口必须保留的唯一硬依赖: types.go:100 HookDef.Handler HookHandler 字段需要联合承载 struct handler (ShellHandler 的 executor/timeout 状态, CallbackHandler 的超时保护逻辑 handler.go:129-150) 与 func handler. 去掉 interface 反而强制 call site 手写 HookHandlerFunc(h.ExecuteHook) 包装, 劣化. 同类"伪问题措辞"已在 L1287/L1290 出现, 参见 feedback_todo_audit_first.
- [x] 🟢 ~~
inbox/uds_server.go:87 - socket 路径 /tmp 硬编码,多用户机器有冲突风险 → os.UserCacheDir~~(P3)- 2026-04-14 完成: 订正: 不是"冲突" (sessionID 已唯一), 真问题是 /tmp 对所有用户可读导致 session 元数据泄露, 且与 session_persist.go / session_snapshot.go 路径策略不一致. 实施: 抽出 resolveSockPath() 两阶段策略, 优先 os.UserCacheDir/flyto/, macOS 路径越界 104 字节内核限制时 fallback /tmp 保功能正确性. 新增 TestUDSServer_SockPathLocation 验证策略. L1206 plan_command_server.go 同类待迁移可复用此 helper.
- [x] 🟢 ~~
permission/classifier_ai.go + memory/selector_ai.go - AIClassifier 和 AIMemorySelector 相似模式(AI 决策 + 文本 fallback)但未共享基础设施~~(P3)- 2026-04-14 审核结论: 伪问题 (LSP 核实). 路径订正: core/pkg/{permission,memory}/*_ai.go. 表面相似, 内部机制差两个数量级, 未来方向相反: AIClassifier 是两阶段 (Stage1 RoleFast + Stage2 RoleMain) + 内置 5min TTL cache + 自消费流式 TextDeltaEvent/UsageEvent + stopReason 感知 (max_tokens 降级 DecisionAsk, 安全核心设计) + 直接依赖 flyto.ModelProvider, 由 permission.classifier_factory.go 提供 provider-specific 工厂 (NewAnthropicClassifier→XML, NewOpenAIClassifier→未来 JSON mode, NewGoogleClassifier→未来 Gemini+grounding), 演进方向 = 按 provider 分化; AIMemorySelector 是单阶段 + 无 cache + 用 ModelQueryFunc 闭包 (selector_ai.go:23-31 明文"打破 memory→engine 循环依赖"的故意设计) + 无流式无 usage, 由 engine.go:690 单一构造点传入 makeMemoryQueryFn 闭包抹平 provider 差异, 演进方向 = 跨 provider 统一. TODO 描述的"文本 fallback"本身也已不准确: AIClassifier 无文本 fallback, AIMemorySelector 的 fallback 已在 L1287 修复时反转到 Store 层. 共享基础设施的代价: 选项 A (抽 helper 包) 会强制 memory import flyto 破坏 ModelQueryFunc 设计决策, 选项 B (降级 AIClassifier 到 ModelQueryFunc) 丢掉 cache+stopReason 使安全分类器退化, 选项 C (保持分化) 才能兼容 classifier factory 未来按 provider 分化的方向. LSP 证据: NewAIClassifier 4 个 call site 全在 permission.classifier_factory; NewAIMemorySelector 1 个 call site 在 engine.go:690; MemorySelector interface 只有 AIMemorySelector 一个实现. 同类"伪问题措辞"已在 L1287/L1288/L1289/L1290 出现 - 此为本交接第 5 例.
- [x] 🟢 ~~
memory/memory.go:38 ↔ flyto/observer.go:16 - MemoryObserver 与 EventObserver 方法集逐字相同 (Event+Error), 隐性 coupling.~~ (P3) - 2026-04-16 完成 (第五棒): 第四棒 "搬包归位" 判断被推翻, 用更小动作解决. 关键核实: flyto 是零外部依赖纯契约层 (pkg/flyto/doc.go L3), 项目内 29 个包 import flyto 且 flyto 零反向 import 任何 SDK 扩展包, 所以 memory → flyto 单向依赖完全安全不构成循环, 原注释 L49 "替代方案 1: 直接依赖 engine.EventObserver - 否决, 循环依赖" 把 engine (会循环) 错误推广到了 flyto (不循环). 执行: memory.go 删 MemoryObserver 接口体 + 5 处类型引用改 flyto.EventObserver + 保留 noopMemoryObserver 作为 memory 包私有兜底 (它自然满足 flyto.EventObserver, 不引入 engine 依赖), engine.go:904 CLEVER 注释从"鸭子类型"改为"直接透传别名", scanner.go:121 注释同步. 验证 1042 测试全过 + go vet 零告 + go build 全通. 衍生登记: L1329b hooks/context/evolve 同模式 + L1329c websocket → bridge 接线.
- [x] 🟢 ~~
hooks/hooks.go + context/compact.go + evolve/evolve.go - HookObserver/CompactObserver/EvolveObserver 与 flyto.EventObserver 方法集逐字相同, 同 L1326 方案可批量清理 (删接口体 + 改用 flyto.EventObserver). 本次 L1326 只处理 memory 以限定 commit 范围, 三包留作独立决策.~~ (P3) - 2026-04-16 完成 (L1326 衍生 a): 三包同步执行 L1326 范式. 每包: 删本地 XxxObserver 接口体 + 字段/SetObserver/getObserver/Config.Observer 类型改 flyto.EventObserver + 保留本地 noopXxxObserver 私有兜底 (不引 engine.NoopObserver 避免循环). 注释从 "与 engine.EventObserver 解耦避免循环依赖" 改为 "与 L1326 memory 包同决策: flyto 是零外部依赖契约层单向依赖完全安全". docs/architecture.md 四处同步 (1005/1029/1040/1063-1068). 验证: go build + go test ./pkg/hooks/... ./pkg/context/... ./pkg/evolve/... 全通.
- [x] 🟡 ~~
pkg/websocket/websocket.go (825 行完整 RFC 6455 实现) 未接入 pkg/bridge.BridgeTransport 接口 - bridge.go 注释声称有 SSE/WS/LongPoll 三实现, 实际只有 transport/sse.go. websocket 包零 core 消费者的原因是接线断裂而非死代码. 需加 transport/websocket.go 作为 BridgeTransport 实现, 或改 websocket 包主动暴露 bridge 适配层.~~ (P2) - 2026-04-16 完成 (L1326 衍生 b): 产品经理挑战"现在没客户明天没有?"推翻了"不做假设性需求"初判 — Flyto 是 SaaS 产品 WS 是基本盘非 hypothetical. 走路径 A: 新建 bridge/transport/websocket.go (~290 行), 复用 pkg/websocket.UpgradeHTTP + WebSocketConn 底层 RFC 6455, bridge 层只做 SessionConn 契约适配 (readLoop + pingLoop + text frame JSON 双向). WebSocket 包自包含性保留 (未反向 import bridge). 四个测试: Roundtrip / MissingSessionID / TransportClose / Reconnect_ReplaceOldConn 全绿. LongPoll 评估后不做: 只覆盖极端老企业内网场景, 现代客户不需要, 200 行真来了再补. bridge.go + sse.go package doc 同步砍 "三实现" 承诺为 "两实现 + LongPoll 待驱动". ResumeWindow 未实现 (WS 无原生 Last-Event-ID 机制, 需另设计 ack 协议, 留独立 TODO).
- [x] 🟡 ~~
engine/team.go + inbox/ - 接线 Agent Teams 模式 (对齐 Anthropic Claude Code Agent Teams v2.1.32, 2026-02-05 发布).~~ 2026-04-16 MVP 完成 (5 commits): C1 f7f75a3 耳朵+嘴巴 (Engine.incomingInbox + send_message 工具 + Team 布线) / C2 2928316 TaskList 业务层+MemoryStore+4 工具 / C3 b573019 MarkdownStore + Anthropic 兼容测试 / C4 1318b37 跨行业 example 三例 / C5 文档 + 对账.主动拒绝的 Anthropic 特性 (决策记录于 core/docs/agent_teams.md): (a) FLYTO_EXPERIMENTAL_AGENT_TEAMS=1 env flag — 默认启用, 不假设运维场景. (b) 三个独立 shell hook (TeammateIdle/TaskCreated/TaskCompleted) — 复用 flyto.EventObserver 统一事件总线, 不回退 L1326 收敛. (c) no nested teams / lead fixed / no session resumption 三个硬限制 — 研究阶段保守, 不照抄 (供应链/组织架构天然嵌套). (d) 文件系统专用 shared task list — 升华为 tasklist.Store 接口 + 多实现 (MemoryStore/MarkdownStore/CustomStore), 跨行业 (HIPAA/金融审计/WMS) 可自选存储. 升华: send_message 作内置工具 (Anthropic 是让模型写文件到 mailbox 目录) + Markdown 双向互操作 (Anthropic Claude Code 和 Flyto 可读写同一 tasks.md). 跨行业 example: core/examples/agent_teams/ 三个示例覆盖 debate / markdown / custom 三种接入模式.
已修复
- [x]
webfetch.go - SSRF:无私网 IP 拦截 → safeDialContext + privateIPNets + 重定向限制
- [x]
filewrite.go:174 - 无 filepath.Clean + 权限 0644 → Clean + 0600
- [x]
bash_background.go - 无任务上限 + 输出无限累积 → maxBackgroundTasks(100) + maxOutputBytes(10MB) + Remove()
待修复
- [x] 🟡
filewrite.go - 非原子写入(直接 os.WriteFile,进程崩溃文件截断)→ temp file + os.Rename(P2)
- [x] 🟡
fileread.go:858 - HEIC/TIFF 文件无预检大小限制,io.ReadAll 全量读入内存(P2)→ 已在 Phase 2 E3 修复
- [x] 🟢
filewrite.go:125 - SecretGuard ErrContentTooLarge 时放行而非拒绝(现有 LEGACY 注释)(P3)✅审计后关闭(2026-04-15: L137-142 LEGACY 注释说明权衡, 超大文件通常不含手工 key, 拒绝会阻碍合法场景, 未来改进方向已记录 OnContentTooLarge callback)
- [x] 🟡
bash_background.go - 已完成任务永不自动清理(Remove() 已实现但无 GC 机制)→ ✅已修复(2026-04-11:sweep-on-Add,defaultBackgroundTaskRetention=10m,WithRetention 可注入;5 个新测试覆盖 stale/running/respect/disabled/batch)
- [x] 🟢
task.go:105 - TaskStore.Update 不校验状态转换合法性(done → in_progress 等非法转换被允许)(P3)✅审计后关闭(2026-04-15: Task 是 LLM 便利工具不是状态机, done→in_progress 是合法场景(重开任务), 工具层 L404-417 已校验状态值合法性)
- [x] 🟢
fileread.go:667 - 读取小范围行时 scanner 仍扫描全文件(超大文件性能陷阱)(P3) - 2026-04-16 优化: collectFull 路径改用 io.ReadAll 批量读取 + 字节级行扫描, allocs 从 ~10K 降至 253 (10K 行文件). scanner 路径 offset>0 已有 break 无需改
- [x] 🟢
bash.go:334,114,122,137 - 超时 600s/120s/120s/30s 魔法数字,多处重复 → 命名常量(P3)
- [x] 🟢
webfetch.go:113,122 - 超时 30s/120s 魔法数字 → 命名常量(P3)
已修复
- [x]
internal/transport/client.go:372,379 - error body + streaming body 加 io.LimitReader(1MB/100MB)
- [x]
internal/wire/openai.go:314,379 - error body + consumeSSE scanner 加 io.LimitReader
- [x]
internal/wire/gemini.go:321 - scanner 加 io.LimitReader(resp.Body, 100MB)
待修复
- [x] 全部 7 provider Config - 新增
GoString() 遮蔽 APIKey 前4位,防 %#v 明文泄露(P1)
- [x] 🟡
internal/transport/client.go + internal/wire/openai.go - maxErrorBodyBytes/maxStreamingBodyBytes 分别定义(两个包),考虑提取到共享位置(P2)- 2026-04-13 完成: 提取到 internal/wire/limits.go 导出为 wire.MaxErrorBodyBytes / wire.MaxStreamingBodyBytes. 方向选择: transport→wire, 因为 transport 已依赖 wire.ParseAnthropicStream, 反向会破包依赖. commit 6d8a754.
- [x] 🟡 全部 7 provider Config - 无
Timeout 字段,consumer 必须注入带 timeout 的 HTTPClient(P1)- 2026-04-13 完成: 加 Timeout time.Duration 字段, 通过 http.Transport.ResponseHeaderTimeout 实现 (不影响 SSE 流), 云端默认 60s, 本地 (ollama/lmstudio) 默认 300s, 详见 handoff §10.8
- [x] 🟡
openai/wire/openai.go:394 + gemini/wire/gemini.go - consumeSSE 无显式 ctx 取消检查 → 每轮加 ctx.Err() 检查(P1)
- [x] 🟢
gemini/wire/gemini.go:240 - HTTP 错误 body 读取 + 附加到错误消息(P2)
已修复
- [x]
internal/mcp/resource_cache.go:Set - 新增 maxResourceCacheEntries=1000,超限时拒绝新条目写入
- [x]
internal/mcp/http_transport.go:NewHTTPTransport - 新增 mcpSafeDialContext SSRF 防护,屏蔽私网/metadata 地址
- [x]
internal/mcp/client.go:56,117 - pending map 改为 string key + maxPendingRequests=10000 容量限制
- [x]
internal/mcp/jsonrpc.go + client.go - ID 改为 json.RawMessage,dispatchSingle 提取,batch 响应处理
- [x]
internal/syslib/bash/extract.go - 新增 decodeANSIC,resolveWordValue 对 $'...' 完整解码 ANSI-C 转义
- [x]
internal/cache/ttl.go - 新增 sweepExpired GC goroutine + Close() sync.Once 保护
- [x]
internal/logger/logger.go - sanitizeSecrets + Sensitive 字段 + 脱敏应用到底层写入
- [x]
internal/mcp/sse_transport.go - sseReadLoop 增加 ctx.Err() 检查
- [x]
internal/mcp/stdio_transport.go - handleStderr() goroutine 转发 stderr 到 logger
- [x] ~~
internal/server/server.go:699~~ - Bearer token 改用 subtle.ConstantTimeCompare 防时序攻击(internal/server 已删除)
- [x]
internal/transport/stream_guard.go:306 - timer2.Reset 语义澄清 + 移除冗余 if remaining > 0
- [x]
internal/syslib/diff/myers.go:11 - 注释修正("行数差异" → "行数之和")
- [x]
internal/syslib/diff/patch.go - StructuredPatch 新增 maxContextLines=100 上限
待修复
- [x] 🔴
internal/syslib/bash/extract.go:264 - NodeCommandSubstitution 直接返回原始文本,$(rm -rf /) / 反引号内命令无法被 IsDangerousCommand 检测(P0)✅已修复(commit 3fba5f5:递归检测 + bash.ExtractAllCommands)
- [x] 🔴
internal/transport/retry/ - Retryer + 6 层策略未接入生产,所有 provider 零重试保护(P0)✅已修复(commit 4a73d75:集成到 CreateMessageStream)
- [x] 🟡
internal/syslib/bash/extract.go:299 - ExtractCommandName 前缀跳过列表缺 doas/expect/python -c/perl -e 等提权命令(P1)✅已修复(commit 55bde65:dangerousPatterns + doas/expect/python/perl 等)
- [x] 🟡
internal/mcp/stdio_transport.go:302 - handleStderr goroutine 无 done channel,进程崩溃时永久阻塞(P1)✅已修复(commit 55bde65:scanDone + select done)
- [x] 🟡
internal/mcp/sse_transport.go:139 - sseReadLoop 传入 context.Background(),ctx 取消检查是死代码(P1)✅已修复(commit 55bde65:sseCtx+sseCancel,Close() 调用 sseCancel())
- [x] 🟢
internal/syslib/diff/patch.go:91-96 - splitLines 不保留 trailing newline,diff 检测有偏差(P2)✅已修复(2026-04-09:保留 \n,末尾空串丢弃)
- [x] 🟢
internal/syslib/diff/patch.go - 只有 patch 生成,无 ApplyPatch 函数(P3) - 2026-04-16 完成: 实现 ApplyPatch(oldText, hunks) + ReverseHunks(hunks) 双向 patch. 上下文行校验防误用, 乱序 hunk 自动排序, 重叠检测. 34 个新测试含 22 场景 round-trip + 双向 undo 验证
- [x] 🟢
internal/mcp/client.go - ElicitationCreateParams.Message 无长度验证(P2)✅已修复(2026-04-15: maxElicitationMessageLen=64KB defense-in-depth, 工具名/资源 URI 已被 maxResponseSize 1MB 兜底无需额外校验)
- [x] 🟢
internal/mcp/http_transport.go:89 - recvCh 容量 32,积压时静默丢弃无感知(P2)✅审计后关闭(2026-04-15: 非 bug, select 无 default 分支是阻塞背压不是静默丢弃, 与 StdioTransport 设计一致, 已补 CLEVER 注释对齐)
- [x] 🟢
internal/tokenizer/tokenizer.go - CJK 用 1.5 tokens/char 近似,非真实 tokenizer(P3) - 2026-04-16 修正: 1.5x→2x 保守估算. BPE 对 CJK UTF-8 (3字节) 常用字=1token 生僻字=2-3token, 2x 取安全上界. token budget 宁高勿低, 精确计数由 API usage.input_tokens 负责
- [x] ⚪
internal/transport/retry/backoff.go:16,115 - jitter 用 math/rand,应用 crypto/rand(P3)✅已修复(2026-04-09:cryptoFloat64() + crypto/rand 8字节归一化)
- [x] ⚪
internal/syslib/git/git.go:162-196 - 手写 itoa 死代码,应用 strconv.Itoa(P3)✅已修复(2026-04-09:strconv.Itoa)
G9 系列:引擎层 Anthropic 解耦
- [x] G9:P1 工具结果排序(engine.go sort.Slice 按原始 toolCalls 顺序重排)
- [x] G9:P1 WebSocket 背压改"丢旧保新"(websocket.go receiveCh 满时丢最旧 + stderr 日志)
- [x] G9-A:EngineBackend 接口(替代主循环 Provider==nil 判断)
- [x] G9-A:SubAgentSpawner 接口(dream.go 消除 *Engine 类型断言)
- [x] G9-B:消除 Provider==nil 双路径(flyto.Request 扩展 FastMode/Effort/SystemBlocks)
- [x] G9-B 修复:System fallback(buildFlytoRequest 保留 System 字段供非 Anthropic Provider 使用)
- [x] G9-B 修复:JSONSchema 结构化输出补齐(anthropic.Provider 设置 ResponseFormat + beta.StructuredOutput)
- [x] G9-B 修复:apiClient 缓存(BuildAndStream 复用 New() 创建的 client)
- [x] G9-C:Provider + Model 必填(删除 Config.APIKey/BaseURL/BearerAuth,New() 校验)
- [x] G9-C:subagent.go 删除 legacy api.Client 路径(-60 行)
- [x] G9-C:DefaultRoles 清空(引擎不预设角色映射)
- [x] G9-C:testutil_test.go 新增 noopProvider + testConfig() 辅助函数
G10 系列:Prompt/Context/Config 层解耦
- [x] G10 P0:compact.go 删除 generateSummary HTTP 直连(-190 行)+ DefaultCompactModel + CompactHTTPClient + AnthropicAPIVersion
- [x] G10 P0:EstimateCost 未知模型返回 0(不再 fallback 到 claude-sonnet-4-6 定价)
- [x] G10 P0:ModelForRole 统一 fallback 到 Config.Model
- [x] G10:DefaultModels 清空(模型定价由框架层运行时获取,引擎不硬编码)
G11 系列:Permission/Query/Wire 层
- [x] G11 P0:Usage CacheTokens 抽象(CacheReadInputTokens/CacheCreationInputTokens → CacheTokens{Read, Written})
- [x] G11 P0 误报:classifier_ai.go import flyto 是合理的(flyto 是公共契约包非 Anthropic 封装)
G12 系列:工具类包
- [x] G12 P0:ThinkingSignature → ProviderMetadata map[string]string(公共类型去 Anthropic 泄漏)
- [x] G12 P1:tokenizer.go ModelPricing 清空(又一份重复的 Anthropic 定价表)
- [x] G12 P1:flyto/ 注释清理(~16 处 Anthropic 引用改为通用描述)
G8 P2 补充修复
- [x] providers/shared/mask.go:GoString APIKey 脱敏提取为共享函数(5 个 provider 统一引用)
- [x] norm_error_content_strip.go:消除不可达 else 分支
- [x] normalizer.go:sorted 标记按需排序,Run 热路径零 copy+sort 开销
- [x] memory.go:fileStore 加 sync.RWMutex(Save/Delete 写锁,List/FindRelevant 读锁,UpdateIndex TOCTOU 修复)
Phase 2 架构清理(2026-04-09)
- [x] A:
internal/api 重命名为 internal/transport(通用 SSE 流式 HTTP 客户端定位)
- [x] B:
api.Client 去 Anthropic 默认值(所有配置通过 ClientOption 注入:WithMessagePath/WithAPIVersion/WithRetryPolicy/WithClassifier)
- [x] C:
ModelConfig / flyto.ModelInfo 统一(config.ModelConfig = flyto.ModelInfo 类型别名,消除类型分裂)
- [x] D:
checker.go fallback 移除(PermissionClass 动态注册表,工具通过 Metadata.PermissionClass 声明,无硬编码 fallback)
- [x] E1:
input.go:328 - resolvePath 目录边界校验(P2 安全)
- [x] E2:
skill_loader.go:306 - LoadSkillFile 路径穿越校验(P2 安全)
- [x] E3:
fileread.go:858 - HEIC/TIFF 预检大小限制(P2 安全)
- [x] DNS 测试修复(14 个测试:预热/幂等/失败静默/共享池/DNS缓存/TTL过期)
代码风格 / Lint
- [x] ⚪ ~~P3+ 存量中文标点修复~~: 2026-04-14 完成 (commit 2457cc5, 534 文件, 53972 次替换) - 全仓库 Go 注释 + Markdown 文档的中文全角标点 (
,。!?:;()【】""''——…、) 批量替换为英文半角 (, . ! ? : ; ( ) [ ] " ' - ...). 规则已在 feedback_punctuation.md 记忆里落地, 新增代码/文档今后直接用英文标点, 本条仅针对存量修复.
- 正确做法: 写一个
core/tools/punct-fix/main.go 工具, 用 go/parser parse Go 文件只改 comment 节点 (不动 BasicLit 字符串字面量), 用 Markdown 状态机跳过 code fence 和 inline code. 禁止用 grep/sed 裸替换 (会破坏字符串字面量内容). 禁止委托 MiniMax (同字符多语境是它的弱区, handoff §4.7).
- 规模估算: ~20 个 provider/engine 相关 .go 文件注释 + ~30 个 .md 文档 + CLAUDE.md 等. 工具跑一遍 ~10 秒, review + commit ~30 分钟.
- 优先级: 非常低. 新代码从 2026-04-11 起已遵守新规则, 存量修复纯属整洁性改进, 不阻塞任何功能.
- 自举 marker: 这一项本身就用英文标点写, 作为新规则生效日期 (2026-04-11) 的时间 marker. 存量扫描工具跑起来后可以跳过这一段 (它已经合规).
- 2026-04-14 实际执行: 工具语言产品经理决策选 Python (一次性临时, 用完丢弃, 不引入 Go 工具链), 主线程 flag 了三个顾虑 (零外部依赖原则 / TODO 原文指定 go/parser / string literal 误改风险), 用严格 state machine (Go 六状态: CODE/LINE_CMT/BLK_CMT/STR/RAW_STR/RUNE, Markdown 跳过 code fence + inline code) 兑现正确性. 规模远超原估算 (~20 .go + ~30 .md → 实际 430 .go + 104 .md), 工具脚本 /tmp/punct_fix.py 本次 commit 后丢弃.
- 2026-04-14 正确性双重验证: (1) 理论: 脚本 state machine 对 engine.go 去注释后与早期方案逐字节相同 78567 bytes (2) 实证: go build ./core/... 534 文件改完后编译通过零错误. 这是数学级 + 工程级双重保证, 证明没有字符串字面量被误改.
- 2026-04-14 PUNCT_MAP 踩坑留痕: 第一版用中文全角字面量写 dict, Write 工具归一化了 fullwidth ASCII-equivalents (U+FF0C → U+002C), 导致
,?:;()""'' 8 个映射 dead (只有无 ASCII 等价物的 !。【】——…、 幸存, 第一版 dry-run 只捞到 24139 次替换). 修复: 全部用 \uXXXX escape, 第二版 dry-run 捞到 53972 次 (翻倍). 这个坑对应 feedback_rtk_usage 的 "工具链归一化陷阱", 应登记到 feedback_todo_audit_first 的 "检查方法本身可能错" 子规则.