f72f2e5(base) → e0a6558(head)本版本是一条围绕「Agent 报告完整性治理」的修复线,核心目标:把"有任意一路报告就算成功"的宽松语义,收紧为"四路齐全才算可用,部分产出显式降级并隔离"。
变更包含 4 组改动:
| # | 主题 | 关键文件 |
|---|---|---|
| 1 | 单路 Agent 报告最多尝试 3 次,独立任务 ID 重试;4/4 才 available,部分为 partial,全失败为 empty | internal/engine/orchestrator.go |
| 2 | 历史缺路 / partial 报告不再参与全量去重与增量合并基线选择 | internal/data/task_queue_repo.go |
| 3 | 合并部分失败时把已原地污染的基线标记为 partial 隔离;pr_merge 要求完整基线并把 merge=false 传播为任务失败 | internal/engine/orchestrator.go |
| 4 | 新增配置 agent_max_retries(硬上限 3),并在管道状态中新增 partial 语义 | configs/*、internal/biz/repo_pipeline_status.go、internal/data/models.go |
变更文件清单:CHANGELOG.md、configs/{config,config-sit,config-ent3,config.example}.yaml、configs/config.go、internal/biz/repo_pipeline_status.go(+test)、internal/data/models.go、internal/data/task_queue_repo.go(+test)、internal/engine/orchestrator.go、internal/engine/orchestrator_agent_retry_test.go(新)、internal/engine/orchestrator_report_status_test.go(新)、internal/engine/review_regression_test.go。
结论先行:这 26 条"修复"绝大多数不是真修复,而是同一批存量问题因代码整体上移/函数重命名后行号漂移,在 head 中消失、随后又在新行号以"新增"形式出现(见第三节的成对north映射)。真正被消除的只有因函数拆分带来的少量结构性改善。
按类型分布:
① 重复代码块(3 条)
internal/biz/repo_pipeline_status.go:59 — 重复分支块internal/data/task_queue_repo.go:203 — 重复查询块internal/engine/orchestrator.go:239 — 重复块(3 处)② 圈复杂度过高(6 条)
internal/data/task_queue_repo.go:380internal/engine/orchestrator.go:547、:737、:852、:917、:1104③ 嵌套深度过高(2 条):orchestrator.go:547、:917
④ 函数过长(2 条):orchestrator.go:917、:1104
⑤ 高耦合(3 条):orchestrator.go:737、:917、:1104
⑥ 缺少默认分支(2 条,Low/Bug):orchestrator.go:577、:637
⑦ 参数过多(2 条):data/task_queue_repo.go:421、data/task_queue_repo_status_test.go:128
⑧ 未使用参数(3 条):data/task_queue_repo.go:421、orchestrator.go:852、:1078
⑨ 行过长(2 条):data/task_queue_repo_status_test.go:42、orchestrator.go:263
行号漂移对照("修复"实为同一问题搬家):
| 修复(base 行号) | 新增(head 行号) | 说明 |
|---|---|---|
repo_pipeline_status.go:59 | :65 | 新增 partial 分支后再现重复块 |
task_queue_repo.go:203 | :176 | 查询改写后行号上移 |
task_queue_repo.go:380 | :385 | 复杂度 11 未变,仅位移 |
task_queue_repo.go:421 | :426 | ListByUserRepos 参数/未使用参数原样保留 |
task_queue_repo_status_test.go:42/128 | :54/209 | 测试辅助函数缺陷原样保留 |
orchestrator.go:239/263/547/577/637 | :243/267/557/587/647 | 整体位移,问题性质未变 |
全部为 Low / Medium,无 High/Critical。分三类:
| 严重度 | 位置 | 问题 | 归因 |
|---|---|---|---|
| Medium | internal/engine/orchestrator.go:136 | 上帝类:Orchestrator 方法数达 21(>20) | 本次新增 agentMaxAttempts、effectiveAgentTimeout、runSingleAgentWithRetry、runSingleAgentAttempt、aggregateAgentResults、completeBaselineManifest、markBaselineMergePartial 等,把类推过阈值 |
| Medium | internal/engine/orchestrator.go:882 | runSingleAgentAttempt:复杂度 23、函数 91 行、直接依赖 16 个函数 | 由原 runSingleAgent 拆出并追加 manifest 空值校验、终态判断 |
| Medium | internal/engine/orchestrator.go:676 | runAgentBatch 直接依赖 11 个函数(>10) | 新增聚合路径 |
| Medium |
internal/biz/repo_pipeline_status.go:65 — 新增的 partial 分支与 completed 分支重复 4 行(可抽 completedStatus() 辅助)internal/data/task_queue_repo.go:176 — 查询块重复internal/engine/orchestrator.go:243 — 重复块 3 处internal/data/task_queue_repo_status_test.go:55、internal/engine/orchestrator_agent_retry_test.go:21、internal/engine/orchestrator_report_status_test.go:28 — 测试重复块task_queue_repo.go:{385,426}、task_queue_repo_status_test.go:{36,54,209}、orchestrator.go:{267,557,587,647,717,1010,1236} 等,与第二节"修复"项一一对应,属存量问题随行号迁移,并非新引入。
runSingleAgentWithRetry 用 context.WithTimeout(ctx, effectiveAgentTimeout()) 建立共享超时预算,第 2/3 次重试用 {id}_{config}_retry_N 独立任务 ID,避免复用已失败任务;agentMaxAttempts() 以 min(cfg,3) 硬顶上限,测试 TestAgentMaxAttemptsCapsAtThree 覆盖了越界配置。✅failed/cancelled/canceled/timeout 均视为终止失败并触发重试,修掉了"timeout 终态死等"的隐患。✅S3Manifest[config] 为空即判失败(runSingleAgentAttempt 内 strings.TrimSpace 校验),避免"ok 但无产物"被计入成功。✅aggregateAgentResults 用 available / partial / empty 三态,且 available 时清空 ReportError;对应汇总口径 anySuccessful()(是否失败)与 allSuccessful()(是否可发卡片)。✅RunMergePhase 在 okCount != 4 时调用 markBaselineMergePartial 把基线置 partial 并写 ReportError,返回 ,避免"被原地覆盖过的基线继续宣称完整"。✅R1(中)基线选择口径不统一——读取路径仍用宽松查询。
GetLatestCompleteScan(4 路齐全)只被 pr_merge 与 RunMergePhase 使用;而 GetLatestScan(仅 state='completed',不看 report_status/manifest 完整性)仍被以下路径调用:
internal/biz/wiki.go:152/183/204/220(Wiki 树/文档/报告 manifest/报告正文)internal/mcp/resolve.go:66(MCP 数据基线)internal/biz/repo.go:122(仓库详情最新全量任务)后果:当某仓库最新全量任务是 partial/empty 时,Wiki 与 MCP 仍会把缺路 manifest当作基线对外提供。建议这些读取路径同样改为"优先取完整记录、否则回退并显式标注"。
R2(中)未配置 Agent 时全量去重失效。
FindCompletedByHeadSHA 新增 report_status='available' 过滤。若部署未配置 Agent(o.agent == nil → runAgentBatch 直接返回空结果、ReportStatus 非 available),则任何全量任务的去重都失效,同 commit 会反复重扫。上线前需确认 Agent 为必备组件,或对 agent==nil 保留旧的宽松去重。
R3(中)新增状态值 partial 的前端契约。
deriveRepoPipelineStatus 开始输出 report_generation_status="partial"。仓库内无枚举校验,openapi.yaml 亦未定义该字段(见调研),实际识别取决于前端。需与前端确认新取值渲染(否则可能显示为"未知/失败")。
R4(低)列表/详情口径与去重口径可能"闪跳"。
GetLatestScansByRepoIDs(仓库列表/我的仓库)仍取最新完成全量任务,不看完整性。若最新任务是 partial、而更早存在完整基线,列表会显示 report_generation_status=failed(hasAvailableReport 为 false 且非 partial),与"存在可用基线"的事实不一致。
R5(低)移除 code_overview 兼容键的影响面。
RunMergePhase 删除了 baseKey=="" && rt=="code_summary" 时用 code_overview 兜底的兼容逻辑,改由 completeBaselineManifest 严格要求 4 键齐全。这会让历史旧格式基线(用 code_overview 命名)彻底不可用于增量合并——与"隔离历史缺路基线"的设计一致,但需在运维侧确认:这类仓库会被强制走一次新的全量扫描。
R6(低)重试会新增 Agent 侧任务与超时预算紧张。
以 120m 为单路共享预算、每路最多 3 次尝试,4 路并发下最坏情况对 Agent 侧的压力×3;共享预算意味着首次尝试若已接近 120m,后续重试基本无预算可用(timeoutCtx.Err()!=nil 直接 break),实际重试机会取决于单次耗时。建议对单次尝试设置更短的分段预算或补充可观测指标。
agent_max_retries: 3 已同步到 config.yaml、config-sit.yaml、config-ent3.yaml、config.example.yaml,并在 configs/config.go 补充 AgentMaxRetries 字段(JSON/yaml 双 tag)。未见遗漏。✅
本次改动测试补充充分,且针对性强:
internal/engine/orchestrator_report_status_test.go(新):TestAggregateAgentResults 覆盖 4/4、3/4、0/4 三态与 ReportError 内容、available 清空错误;TestCompleteBaselineManifest 覆盖 partial/缺路拒绝;TestMarkBaselineMergePartial 验证隔离。internal/engine/orchestrator_agent_retry_test.go(新):覆盖"第三次成功即停止且任务 ID 序列正确""连续失败严格停在 3 次""timeout 终态立即重试""配置越界仍硬顶 3 次"。internal/data/task_queue_repo_status_test.go:新增 TestFindCompletedByHeadSHARequiresAvailableReport、TestGetLatestCompleteScanSkipsPartialAndLegacyIncomplete,直接验证 partial/历史 3/4 记录不再阻止补扫与基线选择。internal/biz/repo_pipeline_status_test.go:把"partial 全量报告"的期望从 failed 改为 partial。internal/engine/review_regression_test.go:新增源码级回归断言,锁定 GetLatestCompleteScan、pr_merge 失败传播、以及"模板读取不得用 context.Background()"。缺口:未见针对 R1(Wiki/MCP 读取路径完整性)、R2(未配置 Agent 去重)、RunMergePhase 部分失败时基线被落库为 partial 的端到端集成测试;也未覆盖 GetLatestScansByRepoIDs 的完整性口径。
总体评价:本次是有明确缺陷驱动的质量改进,方向和实现均正确,测试覆盖扎实,未引入 High/Critical 问题。主要代价是 orchestrator.go 复杂度进一步上升(单文件新增 7 个方法,触发"上帝类"阈值)。
优先级建议:
GetLatestScan 的读取方(wiki.go、resolve.go、repo.go)也能感知 partial,消除 R1/R4 的口径分裂。agent==nil 场景保留宽松去重(R2)。report_generation_status="partial" 契约(R3)。RunMergePhase/runSingleAgentAttempt 拆分(复杂度 12/23、90+ 行),并补充合并部分失败的落库集成测试(第三节 3.1)。hasAvailableReport/hasCompleteFullReport/completeBaselineManifest 三处重复的"四路齐全"判定为共享函数,消除重复代码块与二义性。关于"修复 26 条"的提醒:请勿将其理解为 26 个缺陷已解决;其中约 20 条为行号漂移后的同源问题(对照见第二节),真实净变化是"存量复杂度问题 + 新增类膨胀",建议按第四节建议逐步治理。
orchestrator.go:737 |
:717 |
| 位移 |
orchestrator.go:852 | :882 | runSingleAgent → runSingleAgentAttempt,复杂度反而从同量级升到 23 |
orchestrator.go:917 | :1010 / :1236 | 位移 |
orchestrator.go:1078 | :1236 | 位移 |
orchestrator.go:1104 | :1075 | writebackIssuesSummary,复杂度 29 |
internal/engine/orchestrator.go:1075writebackIssuesSummary 复杂度 29、115 行、依赖 16 函数 |
| 存量函数,本次位置暴露(非新增逻辑) |
| Medium | internal/engine/orchestrator.go:1289 | RunMergePhase 90 行、依赖 16 函数 | 追加"部分失败隔离基线"分支 |
| Medium | internal/engine/orchestrator_report_status_test.go:12 | 测试嵌套深度过高 | 新增测试 |
| Medium | internal/engine/orchestrator_agent_retry_test.go:127 | 测试圈复杂度 11 | 新增测试 |
| Medium/Security | internal/engine/orchestrator_agent_retry_test.go:165 | "错误详情泄露" | 判为误报:位于 httptest 桩服务,仅回显测试 JSON |
falsefinalState 由 mergeOK 决定,false 时任务落 failed。✅