Valhalla静态工程审阅|Planning with Files 深度评测:用持久化文件解决 AI 长任务的上下文与恢复问题【Agent Skill 特辑 #020】
评测对象:
planning-with-files
仓库:https://github.com/OthmanAdi/planning-with-files
固定提交:9b7d0a007946ae7694216642fd5be78c2f13b6db
评测类型:证据驱动的只读静态工程审阅
评测边界:本文仅依据固定源码快照进行分析,未执行项目代码、测试、依赖扫描或运行时安全验证。 作者:Valhalla Matrix治理实验室
摘要
AI 编码代理在处理短任务时通常表现良好,但面对跨文件修改、长时间调试、复杂重构和多阶段交付时,容易遇到几个共性问题:
- 上下文窗口不足;
- 中途压缩后丢失任务状态;
- 长任务执行顺序不稳定;
- 任务重启后无法恢复进度;
- Agent 重复执行已经完成的步骤;
- 用户难以了解当前任务处于什么阶段。
planning-with-files 采用一种相对直接、但具有工程实用性的思路:将任务计划、阶段状态、执行记录和相关元数据持久化到 Markdown 或其他文件中,让 AI Agent 通过文件系统维护长任务状态。
基于固定提交的静态证据,本项目包含:
- 274 个受支持源文件;
- 181 个 Shell 文件;
- 85 个 Python 文件;
- 8 个 TypeScript 文件;
- 18 个 Skill 条目;
- 17 个一级目录或模块入口;
- 52 个测试文件线索;
- 3 个 CI 工作流线索;
- 47 条静态风险命中。
从工程结构看,它不是一个大型运行时平台,而是一个围绕“文件化规划、状态恢复和多 Agent 平台适配”构建的专项工具。项目的优点是目标聚焦、实现路径清晰、测试文件较多;主要挑战则集中在 Shell 脚本复杂度、路径边界、跨平台兼容性、Hook 行为一致性和状态文件的可靠性上。
核心结论:
planning-with-files具备较明确的工程价值,适合用于验证 AI 长任务的计划持久化和断点恢复方法。但在实际使用前,应重点验证路径隔离、脚本参数处理、Hook 生命周期、并发访问和异常恢复行为。
一、项目解决了什么问题?
1. AI 长任务为什么容易失控?
一个典型的 AI 编码任务可能包含以下步骤:
理解需求
↓
扫描项目
↓
制定方案
↓
修改多个文件
↓
执行测试
↓
修复失败
↓
重新验证
↓
整理交付结果
如果这些状态只存在于模型上下文中,就会面临几个问题:
- 上下文被压缩后,早期计划可能丢失;
- Agent 重启后,不知道之前完成了哪些工作;
- 长任务中途失败时,无法准确恢复;
- 多轮执行后,计划、实际修改和测试结果可能不一致;
- 用户无法快速判断当前任务进展。
planning-with-files 的基本思路是将这些信息写入项目目录中的持久化文件:
任务计划文件
执行状态文件
阶段记录文件
检查结果
恢复信息
于是,Agent 的任务状态从:
仅存在于上下文
变成:
上下文 + 文件系统中的持久化状态
这是一种“外部化任务记忆”的工程实践。
二、静态资产概览
2.1 源码规模
本次扫描识别出 274 个受支持源文件:
| 语言 | 文件数量 | 可能承担的职责 |
|---|---|---|
| Shell | 181 | Hook、安装、状态管理、文件操作和自动化流程 |
| Python | 85 | 测试、辅助逻辑和验证脚本 |
| TypeScript | 8 | 特定 Agent 平台或扩展入口 |
| 合计 | 274 |
Shell 文件占比约为三分之二。这与项目的设计目标相符,因为文件化规划通常需要完成:
- 创建任务目录;
- 初始化计划文件;
- 解析任务状态;
- 检查阶段完成情况;
- 写入或读取 Markdown;
- 安装 Hook;
- 适配不同 Agent 平台;
- 计算文件摘要;
- 调用外部命令。
不过,Shell 占比较高也意味着项目的稳定性高度依赖:
- Shell 解释器差异;
- 命令行工具可用性;
- 环境变量;
- 路径格式;
- 文件编码;
- 子进程退出码;
- 错误处理方式。
2.2 Skill 表面
项目识别出 18 个 SKILL.md 或技能条目,并分布在多个 Agent 平台适配目录中,包括:
.agents
.codebuddy
.codex
.continue
.cursor
.factory
.gemini
.hermes
.kiro
.mastracode
.opencode
.pi
这说明项目并非只服务于一个固定客户端,而是尝试将同一套规划方法适配到多个 Agent 工具或开发环境。
多平台适配的价值在于:
- 降低重复配置成本;
- 保持规划方法的一致性;
- 便于不同团队采用;
- 能够根据平台提供相应 Hook 和扩展入口。
与此同时,也需要确认不同平台的行为是否一致:
- 计划文件是否写入相同位置;
- 当前任务状态是否使用同一格式;
- Hook 是否全部安装成功;
- 中断和恢复事件是否等价;
- 路径解析是否存在平台差异;
- 某些平台是否会绕过计划检查。
三、核心架构:文件化规划与状态恢复
根据源码目录和抽样文件,可以将项目的工作流程抽象为以下结构:
flowchart TD
A[用户任务] --> B[初始化任务会话]
B --> C[生成计划文件]
C --> D[拆分阶段与步骤]
D --> E[执行当前阶段]
E --> F[写入执行记录]
F --> G{阶段是否完成}
G -- 否 --> E
G -- 是 --> H[进入下一阶段]
H --> I{任务是否完成}
I -- 否 --> E
I -- 是 --> J[生成完成报告]
E --> K[任务中断或上下文压缩]
K --> L[读取计划与状态]
L --> E
3.1 初始化阶段
init-session.sh 是重要的静态阅读入口。报告提取到的函数包括:
slugify
short_uuid
gen_nonce
apply_v3_mode
write_default_task_plan
从命名可以推断,初始化阶段可能涉及:
- 将任务名称转换为安全标识;
- 生成短 ID;
- 生成随机标记;
- 应用不同版本的规划模式;
- 写入默认任务计划。
需要重点验证:
- 任务名称是否可能影响路径;
- 生成的目录是否存在冲突;
- 随机 ID 是否仅用于标识,还是承担安全认证作用;
- 初始化失败时是否留下半成品文件;
- 重复初始化是否会覆盖既有计划;
- 计划文件是否使用安全的原子写入方式。
3.2 计划注入阶段
inject-plan.sh 是抽样文件中结构最复杂的入口之一,报告提取到的函数包括:
slug_is_valid
norm_slashes
canonicalize
is_within_root
smart_plan_extract
这些函数名显示出项目对路径规范化和工作区边界有所关注。
其中,is_within_root 这类逻辑尤其重要。文件化规划系统需要避免出现以下问题:
计划根目录
↓
用户输入路径
↓
路径穿越
↓
访问计划目录之外的文件
理想的路径处理过程应包括:
- 统一路径分隔符;
- 处理相对路径;
- 规范化
.和..; - 解析符号链接;
- 获取真实路径;
- 检查是否仍位于允许根目录内;
- 再执行文件读写。
仅依赖字符串前缀判断并不足够。例如,以下判断可能存在边界问题:
[[ "$target" == "$root"* ]]
因为同名前缀目录、符号链接和未规范化路径都可能造成误判。
3.3 完成检查阶段
check-complete.sh 中出现了:
advisory_report
ledger_line_count
json_escape
first_in_progress_phase
这表明项目可能通过计划文件和日志记录判断:
- 当前任务是否完成;
- 是否还有进行中的阶段;
- 记录条数是否符合预期;
- 输出是否需要 JSON 转义;
- 是否需要生成提醒或报告。
这里有一个重要的工程问题:
“计划上标记为完成”是否等于“实际工作已经完成”?
例如,Agent 可能在修改文件后直接更新状态,却没有成功执行测试。因此,完成检查最好同时验证:
计划状态
+ 实际文件变更
+ 测试结果
+ 必需产物
+ 未完成事项
否则,状态文件可能成为“看起来已完成”的来源,而不是实际执行结果的可信证明。
3.4 停止门控阶段
gate-stop.sh 可能承担停止前检查或任务结束门控职责。
这类机制通常用于防止 Agent 在以下情况下过早结束:
- 仍有未完成阶段;
- 存在未处理错误;
- 测试尚未执行;
- 计划文件未更新;
- 还有未提交的任务结果;
- 需要用户确认的操作没有完成。
但停止门控不能仅依赖自然语言状态。建议将其设计为结构化条件,例如:
completion:
plan_status: complete
pending_phases: 0
required_checks:
tests: passed
lint: passed
user_approval: not_required
side_effects: recorded
四、项目的主要工程优势
4.1 目标聚焦,边界相对清晰
与同时包含模型服务、前端、数据库和多个运行时的大型 Agent 平台相比,planning-with-files 的目标比较集中:
长任务规划
+ 文件化状态
+ Hook 触发
+ 多平台适配
这种聚焦带来的好处是:
- 容易理解;
- 便于部署;
- 适合个人和小团队试用;
- 更容易围绕核心流程编写测试;
- 能够快速验证“持久化计划是否改善 Agent 执行”。
4.2 测试覆盖方向具有针对性
报告列出的测试文件包括:
tests/test_planning_disabled_optout.py
tests/test_check_complete_resolver.py
tests/test_nested_plan_isolation.py
tests/test_v238_command_files.py
tests/test_hook_resolver_integration.py
tests/test_precompact_hook.py
tests/test_resolver_plan_root_pin.py
tests/test_hook_body_v240.py
tests/test_path_fix.py
tests/test_injection_determinism.py
tests/test_session_catchup.py
tests/test_ledger_utf8.py
tests/test_ps1_windows_encoding.py
tests/test_cursor_nested_root_isolation.py
tests/test_resolver_parity.py
tests/test_plan_attestation.py
tests/test_codex_nested_root_isolation.py
tests/test_stop_hook_dispatch.py
tests/test_gate.py
tests/test_line_endings.py
tests/test_containment.py
tests/test_canonical_script_sync.py
从测试名称来看,项目已经关注了一些实际问题:
- 规划功能关闭或退出;
- 嵌套目录隔离;
- Hook 集成;
- 计划根目录固定;
- 注入稳定性;
- 会话恢复;
- UTF-8 和 Windows 编码;
- 不同平台行为一致;
- 停止门控;
- 路径包含关系;
- 脚本同步。
这比只测试“能否创建一个计划文件”更加接近真实工程场景。
4.3 存在计划完整性校验思路
attest-plan.sh 中出现:
resolve_plan_file
attestation_path_for
compute_hash
从静态命名看,项目可能尝试为计划文件建立摘要或证明文件。
如果该机制确实用于运行时校验,它可以帮助发现:
- 计划文件被外部修改;
- Agent 在执行过程中覆盖计划;
- 多个任务误用了同一计划;
- 恢复时读取了错误版本;
- 计划与执行记录不一致。
不过,文件哈希只能证明内容变化,不能证明内容本身可信。它还需要配合:
- 哈希生成者身份;
- 版本信息;
- 任务 ID;
- 时间戳;
- 文件位置;
- 写入权限;
- 失败处理;
- 回滚机制。
五、需要重点关注的风险
5.1 Shell 脚本规模较大
181 个 Shell 文件构成了本项目最重要的工程风险面。
Shell 适合处理文件、Hook 和命令行环境,但其复杂度容易被低估。常见风险包括:
- 未加引号的变量展开;
- 空格和换行导致参数拆分;
eval或间接命令执行;- 管道中间步骤失败未被发现;
set -e行为与预期不一致;- 子 Shell 环境变量丢失;
- 命令不存在时错误被吞掉;
- Windows PowerShell 与 Unix Shell 行为不一致;
- 临时文件权限过宽;
- 并发执行造成状态覆盖。
建议对生产可达脚本统一检查:
set -Eeuo pipefail
并根据实际环境补充:
- 参数白名单;
--参数终止符;- 明确的临时目录;
trap清理逻辑;- 命令存在性检查;
- 退出码传递;
- 日志脱敏;
- 文件锁;
- 超时控制。
5.2 静态风险命中大多出现在测试文件
报告列出的前 30 条风险样例中,大量路径属于:
tests/
例如:
tests/test_hook_resolver_integration.py
tests/test_injection_determinism.py
tests/test_plan_attestation.py
tests/test_gate.py
tests/test_containment.py
这会显著影响风险解读。
测试文件中出现 Shell 调用通常是合理的,因为测试需要:
- 调用被测脚本;
- 创建临时目录;
- 模拟 Hook;
- 检查退出码;
- 验证跨平台行为;
- 构造路径边界场景。
因此,47 条静态命中不能直接作为生产风险数量。应将命中按以下维度分类:
| 分类 | 处理方式 |
|---|---|
| 测试代码 | 检查测试隔离和夹具安全 |
| 示例代码 | 确认不会进入生产包 |
| 安装脚本 | 高优先级复核 |
| Hook 实现 | 高优先级复核 |
| 核心运行脚本 | 最高优先级复核 |
| 文档片段 | 检查是否会被自动执行 |
| 第三方或生成文件 | 单独确认来源 |
5.3 计划文件可能遭遇污染
文件化规划的优势是持久化,风险也在于持久化。
如果 Agent 读取项目中的计划文件、日志或历史记录,需要防范:
- 计划文件被恶意修改;
- 外部输入通过计划文件注入指令;
- 一个项目的计划被另一个任务读取;
- 嵌套项目共享错误的计划根目录;
- 计划状态被提前改为完成;
- 旧任务状态影响新任务;
- 多个 Agent 并发修改同一文件。
建议为计划文件增加:
任务 ID
工作区根目录
创建时间
最后修改时间
当前版本
文件摘要
写入者
状态变更记录
并确保恢复时同时检查:
计划文件
+ 工作区路径
+ 当前任务 ID
+ 执行日志
+ 文件摘要
5.4 多平台 Hook 适配容易产生行为差异
项目同时支持多个 Agent 或开发平台。不同平台的 Hook 机制可能存在差异:
- 触发事件名称不同;
- 输入格式不同;
- 环境变量不同;
- 当前工作目录不同;
- 退出码语义不同;
- 是否允许修改上下文不同;
- 是否支持阻止后续执行不同。
因此,需要建立跨平台一致性测试矩阵:
| 能力 | 平台 A | 平台 B | 平台 C | 平台 D |
|---|---|---|---|---|
| 初始化计划 | 通过/失败 | 通过/失败 | 通过/失败 | 通过/失败 |
| 写入状态 | 通过/失败 | 通过/失败 | 通过/失败 | 通过/失败 |
| 中断恢复 | 通过/失败 | 通过/失败 | 通过/失败 | 通过/失败 |
| 停止门控 | 通过/失败 | 通过/失败 | 通过/失败 | 通过/失败 |
| 嵌套目录隔离 | 通过/失败 | 通过/失败 | 通过/失败 | 通过/失败 |
六、如何评价这份原始评测报告的数据质量?
这份原始报告的证据组织总体较好,但还存在几个需要改进的地方。
6.1 做得较好的地方
固定提交明确
报告提供了完整 Commit SHA:
9b7d0a007946ae7694216642fd5be78c2f13b6db
这使读者能够复核同一版本,避免因为仓库持续变化导致结论漂移。
对静态证据边界有明确说明
报告多次强调:
- 未执行代码;
- 未执行测试;
- 未进行依赖扫描;
- 静态命中需要人工复核;
- 测试文件存在不等于测试通过。
这是比较重要的研究规范,能够减少过度解读。
风险样例可定位
报告给出了具体文件路径,而不是只提供抽象风险数量。例如:
.agents/skills/planning-with-files/scripts/inject-plan.sh
.agents/skills/planning-with-files/scripts/init-session.sh
tests/test_containment.py
tests/test_plan_attestation.py
文件级证据便于后续安排人工审阅和验证。
测试命名能够反映实际问题
从测试名称可以看出,项目关注了嵌套根目录、路径修复、Hook、编码、状态恢复和计划完整性等问题。这些方向与项目定位较匹配。
6.2 数据解释需要更加克制
文件数量不是质量评分
274 个源文件和 181 个 Shell 文件只能说明工程规模和语言构成,不能单独说明:
- 代码质量;
- 维护性;
- 安全性;
- 性能;
- 架构先进程度。
AST 结构计数不能等同于复杂度
报告中的:
声明 89
分支 408
循环 141
异常路径 32
适合作为源码导航指标,不应被直接解释为:
- 圈复杂度;
- 缺陷概率;
- 维护成本;
- 性能瓶颈。
尤其是 Shell 语法经过词法或结构提取后,分支计数可能受到命令替换、条件表达式和脚本风格影响。
风险命中应区分测试与生产
当前风险样例大部分位于 tests/ 目录。报告虽然写明需要人工复核,但最好进一步增加:
生产可达命中
测试专用命中
构建和安装命中
示例命中
文档命中
这样更有利于技术负责人判断风险优先级。
“模块表面 broad”不等于模块耦合复杂
17 个一级模块根表示项目有多个顶层目录,但不能直接证明:
- 模块职责清晰;
- 内部依赖合理;
- 组件可以独立部署;
- 跨平台实现一致。
报告中已经写了“由一级模块根数量推导,不评价内部耦合”,这一点是正确的,建议在文章正文中继续保持。
七、建议的验证清单
1. 构建验证
记录以下信息:
- 操作系统和版本;
- Shell 类型和版本;
- Python 版本;
- Node.js 版本;
- 包管理器版本;
- 官方安装命令;
- 官方测试命令;
- 构建结果;
- 失败日志;
- 生成文件和临时文件。
2. 功能验证
至少覆盖:
- 初始化任务;
- 创建计划文件;
- 读取当前计划;
- 更新阶段状态;
- 任务中断;
- 任务恢复;
- 嵌套项目隔离;
- 计划完成检查;
- 计划文件摘要;
- 停止门控;
- 关闭规划功能;
- 多平台 Hook 行为。
3. 安全验证
建议测试:
| 测试场景 | 预期结果 |
|---|---|
使用 ../ 访问计划根目录之外的文件 | 被拒绝 |
| 使用符号链接绕过目录限制 | 被拒绝 |
| 任务名包含空格和特殊字符 | 正确处理 |
| 两个任务同时初始化 | 不互相覆盖 |
| 两个 Agent 同时写入状态 | 有锁或明确冲突处理 |
| 计划文件被外部修改 | 能够发现或记录 |
| Shell 参数包含特殊字符 | 不产生额外命令 |
| 中断任务 | 子进程和临时文件被清理 |
| Windows 换行和编码 | 结果一致 |
| 空计划、损坏计划 | 返回可解释错误 |
4. 质量验证
补充确认:
- 测试实际通过率;
- 测试覆盖率;
- 跳过测试的原因;
- CI 是否执行所有关键路径;
- 发布包是否包含测试和示例脚本;
- 不同平台的脚本是否保持同步;
- 依赖是否锁定;
- 许可证文件是否完整;
- 文档描述是否与当前实现一致。
八、最终评价
planning-with-files 是一个目标清晰、工程边界明确的 Agent 辅助项目。它没有试图解决所有模型能力问题,而是聚焦于一个非常具体的痛点:
如何让 AI Agent 在长任务中记住计划、保持进度,并在中断后继续工作。
从静态证据看,项目的优势主要包括:
- 文件化规划思路简单易理解;
- 具备多平台 Skill 适配;
- 设计了计划初始化、注入、检查和停止门控流程;
- 测试文件覆盖了多个真实边界场景;
- 关注嵌套目录隔离、路径处理、编码和状态恢复;
- 存在计划摘要或完整性校验相关逻辑;
- 具备基础 CI 自动化。
主要风险和挑战包括:
- Shell 文件数量较多;
- 复杂控制流集中在关键脚本中;
- 路径处理属于高敏感边界;
- 多平台 Hook 可能出现行为差异;
- 计划文件存在被污染或状态失真的可能;
- 静态风险命中需要区分测试代码和实际执行路径;
- 当前报告未提供测试通过率、覆盖率和运行时结果。
综合来看,可以将该项目评价为:
一个面向 AI 长任务的轻量级持久化规划工具,具有较强的实践价值和较清晰的技术方向;适合在隔离环境中开展功能、兼容性和安全验证,但当前静态证据不足以支持对生产稳定性和安全性的确定性判断。
最值得继续验证的不是“计划文件能否生成”,而是以下完整闭环:
任务初始化
→ 计划生成
→ 阶段执行
→ 状态写入
→ 测试记录
→ 中断恢复
→ 完成校验
→ 清理和审计
如果这条链路能够在不同平台、不同目录结构和异常场景下保持一致,planning-with-files 才真正具备作为 AI 编码代理长任务辅助组件的工程基础。