ECC 项目 dart-build-resolver:面向 Agent 的 Dart/Flutter 构建错误最小化修复指南
本指南围绕 ECC(The agent harness performance optimization system)仓库中
agents/dart-build-resolver.md所定义的专业解析 Agent 展开,系统讲解如何在 Agent 工作流中以“最小、外科手术式修改”修复dart analyze/flutter analyze错误、Flutter 编译失败、pub 依赖冲突与build_runner代码生成故障。读完本文,你将掌握一套完整的“先诊断—再修复—后验证”错误闭环方法、常见错误对照速查表,以及它与仓库内/flutter-build、/flutter-test、/flutter-review命令协作时的真实定位。
一、背景:为什么需要专门的构建错误解析器
在现代 AI 辅助编码流程中,“代码写错了、构建崩了”是最常见也最消耗上下文的环节。普通代码生成 Agent 擅长补写功能,却往往在一次 dart analyze 报出几十条错误时陷入三种陷阱:过度重构(为了修一行错误重写整个文件)、压制症状(随手加 // ignore: 或把类型改成 dynamic)、无验证循环(改完不复跑分析器)。
dart-build-resolver 正是为此设计的单一职责 Agent。从其在 ECC 仓库中的 frontmatter 定义可以看出它的能力边界:
name: dart-build-resolver
description: Dart/Flutter build, analysis, and dependency error resolution specialist. Fixes `dart analyze` errors, Flutter compilation failures, pub dependency conflicts, and build_runner issues with minimal, surgical changes. Use when Dart/Flutter builds fail.
tools: Read, Write, Edit, Bash, Grep, Glob
model: sonnet
它被授予 Read / Write / Edit / Bash / Grep / Glob 六类工具,即具备读写文件、执行命令、搜索代码的完整能力,但它的使命被严格限定为:修复 Dart 分析器错误、Flutter 编译问题、pub 依赖冲突、build_runner 失败。在仓库的工作流编排中,该 Agent 由 /flutter-build 命令调用,其定位逻辑在 agent.yaml 与 COMMANDS-QUICK-REF.md 中有明确登记(命令 flutter-build 对应此 Agent)。
在 ECC 工作流中的实际位置
在 ECC 的 Flutter 研发闭环中,三个命令形成一条流水线,dart-build-resolver 是第一道关口:
/flutter-build→ 调用dart-build-resolverAgent,先让代码“能编译、零错误”;/flutter-test→ 确认构建通过后回归测试,防止修复引入行为变化;/flutter-review→ 构建与测试都通过后,再由 flutter-reviewer 做代码质量审查。
commands/flutter-build.md 的 Prerequisites 一节也反向印证了这一顺序:/flutter-review 要求“先 /flutter-build,对坏代码做 review 是不完整的”。因此理解 dart-build-resolver,本质上是理解 ECC 里“先让工程可用,再谈代码质量”的工程纪律。
二、核心职责与错误分类
该 Agent 的五大核心职责(见 agents/dart-build-resolver.md):
- 诊断
dart analyze与flutter analyze错误; - 修复 Dart 类型错误、空安全违规与缺失导入;
- 解决
pubspec.yaml依赖冲突与版本约束问题; - 修复
build_runner代码生成失败; - 处理 Flutter 平台特有构建错误(Android Gradle、iOS CocoaPods、Web)。
这五类职责覆盖了 Flutter 工程从“静态分析”到“产物打包”的全部失败点:分析期(analyzer)、依赖解析期(pub)、代码生成期(build_runner)、平台构建期(Android/iOS/Web)。
三、诊断命令:按序执行的故障定位第一步
Agent 面对“构建挂了”的第一动作不是改代码,而是先跑诊断,按以下顺序收集证据(命令均可在 agents/dart-build-resolver.md 与 commands/flutter-build.md 中找到):
# 1. 检查 Dart/Flutter 分析错误(纯 Dart 项目用 dart analyze)
flutter analyze 2>&1
# 或
dart analyze 2>&1
# 2. 检查 pub 依赖解析是否成功
flutter pub get 2>&1
# 3. 检查代码生成产物是否过期(使用 build_runner 的项目)
dart run build_runner build --delete-conflicting-outputs 2>&1
# 4. 按目标平台执行 Flutter 构建
flutter build apk 2>&1 # Android
flutter build ipa --no-codesign 2>&1 # iOS(CI 无签名场景)
flutter build web 2>&1 # Web
几点执行要点值得展开:
2>&1:把 stderr 合并到 stdout,确保 Agent 通过 Bash 工具能一次性捕获完整报错文本,避免只拿到半截输出就臆断错误。- 诊断优先级:永远先跑
flutter analyze。分析器错误是“代码层”问题,成本最低;只有分析通过后才需要怀疑依赖解析与平台构建。 - 平台命令分而治之:Android 用
flutter build apk,CI 环境下打 iOS 包用--no-codesign跳过签名,Web 用flutter build web。原文注释提示:iOS 在无签名 CI 上必须加--no-codesign,否则会卡在签名环节报误导性错误。 --delete-conflicting-outputs参数会在检测到输出文件与输入不一致时强制删除冲突产物再重建,是解决“陈旧生成文件”类问题的关键开关。
在仓库的 commands/flutter-build.md 中,诊断环节的执行顺序被固化为固定工作流:先 flutter analyze,再 flutter pub get,然后按需 dart run build_runner build,最后做平台构建。
四、五步解析工作流:修复—验证的闭环
dart-build-resolver 的修复方法论被提炼为一段循环流程:
1. flutter analyze -> 解析错误信息
2. 读取受影响文件 -> 理解上下文
3. 施加最小修复 -> 只改需要的
4. flutter analyze -> 验证修复
5. flutter test -> 确保没有破坏任何东西
这个循环的三个设计要点:
- 一次一个错误:不是一次性吞下所有错误,而是逐个修复、逐个验证。对应
commands/flutter-build.md中的策略——“Analysis errors first / One fix at a time / Verify each change”。 - 改完必验:第 4 步确保新改动没有引入新错误(即“修复引入的错误不能比解决的更多”);第 5 步用测试兜底,防止看似正确的最小修改悄悄改变了运行时行为。
- 证据驱动的终止:若同一错误在 3 次尝试后仍存在,或修复需要架构级改动,Agent 必须停止并上报(详见“停止条件”一节),而不是无限重试消耗预算。
五、常见错误模式速查表:症状 → 根因 → 修复
原文给出了一张高度可复用的错误对照表,这是该 Agent 的核心知识库,逐行解读如下:
| 错误 | 根因 | 修复 |
|---|---|---|
The name 'X' isn't defined |
缺失 import 或拼写错误 | 添加正确的 import 或修正名称 |
A value of type 'X?' can't be assigned to type 'X' |
空安全——可空值未被处理 | 加 !、?? 默认值 或空值检查 |
The argument type 'X' can't be assigned to 'Y' |
类型不匹配 | 修正类型、加显式转换或改用正确 API |
Non-nullable instance field 'x' must be initialized |
缺少初始化器 | 添加初始化、标 late 或改为可空 |
The method 'X' isn't defined for type 'Y' |
类型不对或 import 错误 | 检查类型与 import |
'await' applied to non-Future |
对非异步值使用了 await | 移除 await 或把函数改为 async |
Missing concrete implementation of 'X' |
抽象接口未完整实现 | 补齐缺失的方法实现 |
The class 'X' doesn't implement 'Y' |
缺少 implements 或缺失方法 |
添加方法或修正类签名 |
Because X depends on Y >=A and Z depends on Y <B, version solving failed |
pub 版本冲突 | 调整版本约束或加 dependency_overrides |
Could not find a file named "pubspec.yaml" |
工作目录错误 | 回到项目根目录执行 |
build_runner: No actions were run |
build_runner 输入无变化 | 用 --delete-conflicting-outputs 强制重建 |
Part of directive found, but 'X' expected |
陈旧生成文件 | 删除 .g.dart 后重新运行 build_runner |
修复优先级的心智模型
这 12 类错误还可进一步归并为四层,Agent 按层处理(参考 commands/flutter-build.md 的 Fix Strategy):
- 分析错误优先(前八行)——代码必须零错误;
- 警告二次分诊——修复可能引发运行时 bug 的警告;
- pub 冲突第三——解决依赖解析;
- 每次只改一处并验证。
值得特别强调的是后两类平台类错误在代码中的反直觉表现:The method 'X' isn't defined for type 'Y' 常常不是真的缺方法,而是把不可变列表当可变列表用,或把 BLoC 事件 API 与 Cubit 命名方法混用。仓库的 /flutter-build 示例中记录了这样一个真实修复案例:
// 错误场景:state.items 是不可变列表,.add 不存在
state.items.add(item);
// 正确做法:状态变更走状态管理器的命名方法
context.read<CartCubit>().addItem(item);
// 注意:Cubit 暴露的是命名方法(addItem/removeItem);
// .add(event) 是 BLoC 的事件 API —— 两者不要混用。
六、Pub 依赖排障:版本冲突的完整工具箱
依赖冲突(version solving failed)是 CI 中最折磨人的错误之一。原文给出了从“看”到“治”的完整命令链:
# 显示完整依赖树
flutter pub deps
# 查看某个包为何选择了该版本(压缩模式 + grep 过滤)
flutter pub deps --style=compact | grep <package>
# 将所有包升级到最新的兼容版本
flutter pub upgrade
# 只升级指定包
flutter pub upgrade <package_name>
# pub 缓存元数据损坏时修复缓存
flutter pub cache repair
# 校验 pubspec.lock 是否一致(CI 强制锁文件场景)
flutter pub get --enforce-lockfile
每个命令的适用场景:
flutter pub deps是排障的第一步——先看清楚谁依赖了谁、各版本从哪条传递链来;--style=compact+grep把全量树压缩后只过滤目标包,快速定位“为什么 pub 选中了这个版本”;pub upgrade与upgrade <pkg>是主动升级手段,后者只升级单个包,属于更“外科手术式”的操作;pub cache repair专门针对缓存元数据损坏的偶发问题;--enforce-lockfile在 CI 中尤为重要——当pubspec.lock已提交时,强制 pub 严格按锁文件解析,防止漂移导致的“本地能过、CI 报错”。
解决 version solving failed 的根本手段则在 pubspec.yaml 中:要么放宽某条依赖的版本上/下限,要么引入 dependency_overrides。注意原文与仓库评审规则都强调:dependency_overrides 只适合临时修复,必须附带注释与 issue 链接,不应残留在生产 pubspec.yaml(对应 skills/flutter-dart-code-review 中 “No dependency overrides in production” 的检查项)。
七、空安全修复模式:拒绝强解包,拥抱模式匹配
Dart 2.12 之后的强空安全(sound null safety)让 String? 与 String 成为截然不同的类型。原文用一段代码给出了“坏—好”对照:
// 错误场景:A value of type 'String?' can't be assigned to type 'String'
// BAD —— 直接强解包(bang operator),name 为 null 时运行时崩溃
final name = user.name!;
// GOOD —— 提供回退默认值
final name = user.name ?? 'Unknown';
// GOOD —— 空值守卫 + 提前返回,之后的 ! 是安全的
if (user.name == null) return;
final name = user.name!; // 空值检查后使用是安全的
// GOOD —— Dart 3 模式匹配
final name = switch (user.name) {
final n? => n, // 匹配非空值并绑定为 n
null => 'Unknown', // 匹配 null
};
为什么原文档的 Key Principles 明确要求“优先使用空安全模式而非 !”?
!把错误推迟到运行时:编译期无法发现,一旦值为 null 直接抛Null check operator used on a null value;??提供确定性回退:把“缺失数据”显式建模为业务默认值;- Dart 3 的 switch 表达式 + 空值模式
final n?是官方推荐的“穷尽匹配”写法——null 分支与非空分支都被强制处理,杜绝遗漏; - 对应 rules/dart 与 skills/flutter-dart-code-review 中反复出现的评审项:Excessive
!bang overuse——评审器会专门检查!滥用,建议优先用?.、??、case var v?或requireNotNull。
八、类型错误修复模式:让集合类型显式化
类型不匹配中最典型的是 List<dynamic> 混入强类型列表:
// 错误场景:The argument type 'List<dynamic>' can't be assigned to 'List<String>'
// BAD —— 从 JSON 解析出的列表被推断为 List<dynamic>
final ids = jsonList;
// GOOD —— 显式构造类型化列表
final ids = List<String>.from(jsonList);
// 或 —— 先断言为 List 再 cast
final ids = (jsonList as List).cast<String>();
这条模式的本质是打破 dynamic 的类型泄露:从 json.decode 或动态 API 拿到的集合,其元素类型在编译期未知,直接赋值会沿类型系统一路“污染”下游。修复的方式是让转换发生在边界处(List<String>.from(...) 会逐个元素做运行时类型检查,.cast<String>() 则是惰性视图,访问时才检查)。这与该 Agent “Never use dynamic to silence type errors”的原则一脉相承——用 dynamic 掩盖错误只是把问题留给运行时,分析器的 strict-casts / strict-raw-types(rules/dart 中强调的严格分析配置)正是用来阻止这类隐式 dynamic 的。
九、build_runner 排障:生成代码的陈与新
使用 json_serializable、freezed、riverpod_generator 等代码生成库的项目,最常见的两类 build_runner 故障是“产物陈旧”与“冲突输出”。原文的处置如下:
# 清空并重新生成所有文件
dart run build_runner clean
dart run build_runner build --delete-conflicting-outputs
# 开发期监听模式(文件变更自动重新生成)
dart run build_runner watch --delete-conflicting-outputs
# 检查 pubspec.yaml 中是否缺失 build_runner 相关依赖
# 必配项:build_runner、json_serializable / freezed / riverpod_generator(都放 dev_dependencies)
三个要点:
clean之后再build是“终极重建”组合,用于生成图彻底损坏时;- watch 模式适用于开发迭代,省去手动反复触发;
- 依赖必须声明在
dev_dependencies而非dependencies——生成器只需要在开发与构建期可用,打进了运行时依赖会无谓扩大产物体积与攻击面。这个“生成器只入 dev”的约束在 skills/flutter-dart-code-review 的项目健康检查中也有对应项:生成的.g.dart/.freezed.dart产物要么保持最新,要么加入.gitignore。
陈旧产物(stale artifact)陷阱
错误表里的 Part of directive found, but 'X' expected 值得单独说明:Dart 的 part of 机制要求生成文件与主文件严格配对。当 .g.dart 是基于旧版本主文件生成的,两者对不上就会报此错。原文给出的处理非常干脆——删除 .g.dart 文件并重新运行 build_runner,让生成器基于当前源码从头产出。这是“症状 vs 根因”关系的经典案例:与其手工修补生成文件(会被下次生成覆盖),不如让生成器重新跑一遍。
十、平台构建排障:Android 与 iOS
Android(Gradle / JDK 兼容性)
# 清理 Android 构建缓存
cd android && ./gradlew clean && cd ..
# 使 Flutter 工具缓存失效
flutter clean
# 重新构建
flutter pub get && flutter build apk
# 检查 Gradle/JDK 版本兼容性
cd android && ./gradlew --version
Android 侧最常见的失败来源是本地构建缓存与 Flutter 缓存的状态不一致。gradlew clean 清的是 Gradle 增量产物,flutter clean 清的是 Flutter 的 .dart_tool、构建缓存等,两者必须搭配使用。flutter clean 之后必须重新 flutter pub get(因为它会删除 .dart_tool/package_config.json)。最后用 gradlew --version 核对 Gradle 与 JDK 版本——AGP(Android Gradle Plugin)对 JDK 版本有硬性要求,JDK 过新或过旧都会抛出难懂的构建错误。
iOS(CocoaPods / Podfile)
# 更新 CocoaPods 仓库索引并重新安装
cd ios && pod install --repo-update && cd ..
# 深度清理 iOS 构建
flutter clean && cd ios && pod deintegrate && pod install && cd ..
# 检查 Podfile 中的平台版本
# 确保 ios platform 版本 >= 所有 pod 要求的最低版本
iOS 侧的关键词是 CocoaPods 状态漂移:pod install 偶尔会因为本地 Spec 仓库过旧而解析不到最新版本,--repo-update 先刷新索引再安装;pod deintegrate 会移除当前 Pods 集成状态,配合 flutter clean 实现“从零再来”。Podfile 里声明的 platform :ios 版本若低于任一 pod 的最低要求,安装会直接失败——原文特别提示要先核对这个版本门槛。
十一、关键原则与停止条件:解析器的“职业操守”
Key Principles
原文用加粗形式立下了这个 Agent 不可逾越的纪律:
- Surgical fixes only —— 只修错误,不做重构。把“修复”与“改进”严格分开,是控制风险的核心;
- Never 未经批准添加
// ignore:抑制注释 —— 静默掉错误等于放弃分析器的保护; - Never 用
dynamic来掩盖类型错误 —— 把问题从编译期挪到运行期是最差的选择; - Always 每次修复后运行
flutter analyze验证 —— 修复必须可被证明; - 修复根因而非压制症状;
- 优先空安全模式,而非
!强解包。
这些纪律与 flutter-reviewer 的检查项形成了有趣的对偶关系:dart-build-resolver 负责“建得快、改得准”,而评审器负责“查得严”——reviewer 的规则里明确列出 “Unjustified lint suppressions(// ignore: 无说明注释)”与“Missing type annotations / implicit dynamic”作为扣分项,正是对 resolver 三条“Never”规则的呼应。
Stop Conditions:何时必须停下上报
Agent 在以下情况必须停止并把问题交还用户,而不是继续烧预算:
- 同一错误在 3 次修复尝试后依旧存在;
- 修复引入的错误比解决的多;
- 需要架构级改动或会改变行为的包升级;
- 存在冲突的平台约束,需要用户拍板。
十二、标准输出格式:可审计的修复报告
为了让每一次修复可追溯、可合并验证,原文规定了严格的输出格式:
[FIXED] lib/features/cart/data/cart_repository_impl.dart:42
Error: A value of type 'String?' can't be assigned to type 'String'
Fix: Changed `final id = response.id` to `final id = response.id ?? ''`
Remaining errors: 2
[FIXED] pubspec.yaml
Error: Version solving failed — http >=0.13.0 required by dio and <0.13.0 required by retrofit
Fix: Upgraded dio to ^5.3.0 which allows http >=0.13.0
Remaining errors: 0
报告由“文件定位(file:line)+ 原始错误 + 具体修改 + 剩余错误计数”组成,最后输出一行总决算:
Final: Build Status: SUCCESS/FAILED | Errors Fixed: N | Files Modified: list
这种格式的价值在于可审计性:每个修复都有文件级锚点、有原始报错、有 diff 说明、有剩余错误计数,人类开发者或后续 Agent 可以逐条确认改动合理性,也可以直接转成 commit message 或 PR 描述。仓库 commands/flutter-build.md 的示例会话则展示了更高层的汇报形式——用表格汇总“Analysis errors fixed / Files modified / Remaining issues”并给出 Build Status: PASS。
十三、与仓库配套生态的联动关系
dart-build-resolver 不是孤立文件,而是 ECC Agent 体系中的一个节点。理解这些联动有助于你在自己的工程中复用它:
- 命令入口:
/flutter-build是它的调用命令,定义了“What This Command Does / When to Use / Stop Conditions”等调用侧契约; - 配套评审:
flutter-reviewer负责构建通过后的代码质量审查,两者共享同一套 Dart/Flutter 知识底座; - 规则库:
rules/dart/下的coding-style.md、patterns.md、security.md、testing.md等为该 Agent 的修复决策提供了风格与安全约束; - 技能库:文中零散的编码模式在
skills/flutter-dart-code-review/SKILL.md中有系统化沉淀,涉及空安全、dynamic禁用、严格分析配置、依赖卫生等 15 个维度的清单; - 测试入口:
/flutter-test承担修复后的回归验证; - 编排链路:
workflows/orch-review.workflow.js与 agent.yaml 中登记了各语言 Build/Test/Review 命令的分工,flutter-build即代表“先修复到可编译”的阶段; - 多语言文档:本 Agent 的说明已随文档体系同步翻译到
docs/zh-CN/agents/dart-build-resolver.md、docs/ja-JP/agents/dart-build-resolver.md等目录,说明其是 ECC 面向多语言社区的标准交付物。
结语:把“构建错误”变成“可治理的工程流程”
dart-build-resolver 给团队的最大启示,是把最容易失控的“报错—修 bug”环节重构成了一套可重复、可审计、有边界的流程:先跑诊断命令收集证据,按错误表匹配根因,以最小改动修复,每次修复后用 flutter analyze 与 flutter test 验证,超限即停并输出结构化报告。这套方法论的完整落点就在 ECC 仓库:Agent 定义见 agents/dart-build-resolver.md,命令契约见 commands/flutter-build.md,代码细节可在 skills/flutter-dart-code-review 与 rules/dart 中进一步查阅。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00