todo(核心)
该仓库是一个由已存在的文件构建的闭环状态机。不存储其他状态。
| 层级 | 文件 | 角色 | 写入策略 |
|---|---|---|---|
| 需求 | README.md, docs/*.md | 真实来源:合同、规范、设置、长期计划 | 主要只读。在 todo-continue 的“文档漂移”下编辑;计划文档 (docs/plans/) 由 todo-init/todo-plan/todo-add/todo-update 编辑;README 中的 Tests: 行由 todo-init 在确认后编辑;可选的 RTM (reference/rtm.md) 仅当其已存在时,由 todo-plan/todo-add/todo-continue 编辑;docs/status.html 由 todo-report 编辑 |
| 活动队列 | TODO.md | 一个任务树。工作项的追踪器,非设计场所 | 仅由 todo-* 技能编辑 |
| 验证 | 源代码 + 测试套件 | “完成”的真实来源 | 仅针对当前任务更改 |
| 存档 | CHANGELOG.md | 永久,仅追加(遵循 Keep a Changelog + Conventional Commits) | 仅由根 drain 和已确认的 /todo-release 触及 ## [Unreleased](发布时:重命名为版本号,其上方新增一个空的 ## [Unreleased],并更新末尾的链接行);todo-init 创建文件或添加缺失的 [Unreleased] |
| 检查(可选) | .todo/check.sh, .todo/todo-lint.sh, CI 作业 | 由 CI、pre-push 钩子和 /todo-continue 运行的 lint + 测试 | check.sh 由 /todo-init --ci 在确认后写入;todo-lint.sh 由用户复制;由用户提交 |
| 版本存储 | git | 每个任务对应一个分支或工作树。未经验证的内容不得进入基础分支 | 参见“Git 模式” |
文件编辑优先使用原生文件工具(Read/Edit/Write);无论使用何种方式编辑,只更改目标行并保持文件末尾换行。仅对 git、测试和 Done when 命令、可选脚本以及技能指定的文件命令(cp, chmod, mkdir -p, date, grep, cmp, mv 以及技能自身临时文件的 rm)使用 shell。
待办文件包括 TODO.md、CHANGELOG.md、docs/plans/PROJECT.md、docs/plans/<ROOT-ID>-*.md(由 Ref: 命名)、以及由 todo-* 技能写入的 README 中的 Tests: 和 Base: 行。README.md 仅当 git diff README.md 完全添加了这些行(可选地在空行之后)时,才由待办提交暂存;节点在“文档漂移”下对 README 的编辑属于该节点的文件,并与其一起提交;任何其他对 README 的更改属于用户:它们计入干净树检查,并且从不被待办提交暂存。待办编辑保持未提交状态,直到技能在 todo/ 分支上进行的下一次提交(通常是树声明),该提交会通过显式路径暂存它们。新的根节点以及未声明树的任何节点更改,仅在基础分支上进行;在 todo/ 分支上,技能会拒绝这些更改并指定基础分支。干净树检查(reference/git-flow.md)会忽略它们。
直接使用
单独的 /todo 是只读的:列出命令,如果存在则对 TODO.md 运行不变性检查,并报告。不更改任何内容。
| 命令 | 目的 |
|---|---|
/todo-init | 引导文件,检测测试命令和 git,可选的预规划和 CI 设置,迁移旧格式 |
/todo-plan | 将工作规划到通道和树中;随时修改计划 |
/todo-add | 添加任务,或将请求分解成树 |
/todo-continue | 执行下一个任务节点,验证它,并将其合并到其父节点 |
/todo-update | 手动状态和字段更改 |
/todo-status | 只读树、通道、历史和搜索 |
/todo-report | 将交互式 HTML 状态页面写入到 docs/status.html |
/todo-release | 用户确认后,将 [Unreleased] 转换为一个版本 |
每个技能只运行其自己的命令,并且不会在未经提示的情况下链式调用其他命令。
Git 模式
在每个命令开始时运行 git rev-parse --is-inside-work-tree。
- Git 仓库: 每个节点在其自己的分支或工作树上工作(
reference/git-flow.md)。基础分支是用户指定的分支,否则是 README 中的Base:行,否则是main,master,trunk,develop中存在的第一个;如果都不存在,询问一次。当用户指定的基础分支与该默认值不同时,主动提供将Base: <branch>添加到 README(确认后写入;待办文件,如Tests:行)。每个检出或合并到基础分支的步骤(树声明、声明释放、基础合并、发布)在主检出(git worktree list的第一项)中运行:如果从链接的工作树启动,请先cd到那里;如果主检出在另一个有更改的分支上,则停止并打印命令。 - 不是 git 仓库: 对每个命令警告一次:“这不是 git 仓库。todo 技能仍然工作,但没有分支、工作树、提交或合并,因此没有隔离和历史记录。运行
git init以启用它们。”然后使用无 git 模式:跳过所有 git 步骤,在State:中跟踪进度(包括files:),仍然更新标记并 drain 到 CHANGELOG.md。验证保持不变。
切勿打标签、强制推送、硬重置或清理。唯一允许的强制分支删除是针对仅包含技能自身未发布的声明提交的分支(reference/git-flow.md,树声明的第 2 和 5 步)。技能自身的 git 命令永不触及网络: 无获取、拉取或推送。共享提交是 git 和你的工作。测试命令可能下载依赖项;如果因缺少网络而失败,则用该原因阻止,而不是重试。
TODO.md 模式
# Active Backlog
## 🌳 Task Tree
正好是这个标题和一个部分。没有“进行中”、“就绪”或“阻塞”部分:每个任务上的标记表示其状态。空的树是指这两个标题且没有任务。
一个任务有 2 到 4 行:做什么、如何验证、可选的链接和状态。
- [ ] **FEAT-002** · P1 · mid · XL · Add token refresh to the auth service
- Done when: old token rejected after refresh; `npx jest tests/auth`; regression: `npm test`
- Links: Ref: docs/auth.md#refresh · Depends: TASK-001 · From: FIX-001
- State: branch todo/FEAT-002-add-token-refresh-auth from main; next: build children
- [ ] **FEAT-002.1** · mid · Refresh endpoint
- Done when: endpoint covered by an integration test; `npx jest tests/auth/refresh.test.ts`
- [/] **FEAT-002.1.1** · high · Token store interface
- Done when: store round-trips a token; `npx jest tests/auth/store.test.ts`
- State: attempts 1/3; baseline 42 passing; branch todo/FEAT-002.1.1-token-store-interface from todo/FEAT-002.1-refresh-endpoint; next: build
| 部分 | 规则 |
|---|---|
| 标题 | - [m] **ID** · [P0-P2,仅根节点 ·] 复杂度 [· 大小] · 做什么,一句话:复杂度 low, mid 或 high(必需);大小 S, M, L 或 XL(可选) |
Done when: | 必需。 标准,然后是 ; 和反引号中的至少一个精确命令。可选的 ; regression: \cmd``;如果没有,则回归测试是项目的完整套件 |
Links: | 可选。Ref: <path#anchor>, Depends: <IDs>, From: <ID>, Lane: <name>(仅根节点),用 · 分隔 |
State: | 可选,在上下文重置后保留。项目用 ; 分隔,每个项目最多一次,按此顺序:attempts n/3, baseline <n> passing(或 none,或 red: <failing test ids, comma-separated>), `tier: <low |
Blocked: | 在 [!] 任务上,替代 State 并位于其位置(最后一个字段行):<reason> → <command in backticks> <error, at most 5 lines joined with " / "> → <action required>。手动阻塞写入 n/a (manual) 在中间 |
仅有这四个字段存在;一个任务不能同时拥有 State 和 Blocked。超过 4 行是设计散文,属于 docs/(用 Ref: 链接)。如果任务需要更多,请拆分。
标记:[ ] 开放,[/] 活动(文件中最多一个),[!] 阻塞,[x] 已验证并合并(或正在合并)到其父节点,等待其根节点 drain。
树、层级和复杂度
层级 L 上的节点,其标题缩进 2L 个空格,其字段缩进 2L+2。最多 3 层。
范围规则。 每个叶子节点是一个适合一个上下文窗口且最多涉及 5 个文件的更改;更大的工作应拆分为子节点(/todo-add)。还无法拆分的节点保持 State: next: decompose,直到 /todo-continue 将其分解。
大小(可选,放在复杂度之后)在有助于读者时记录范围;如果存在,它必须与树匹配,添加或删除节点会更新每个祖先的大小:
| 大小 | 含义 | 位置 |
|---|---|---|
S | 一个更改,最多 2 个文件 | 叶子节点 |
M | 一个上下文窗口,最多 5 个文件 | 叶子节点 |
L | 有子节点,全部为叶子节点 | 父节点 |
XL | 有孙节点 | 父节点 |
带有 State: next: decompose 的叶子节点可以携带 L/XL。
复杂度表示推理的难度,而不是更改的大小,因此代理可以选择合适的模型:
| 复杂度 | 含义 | 示例 |
|---|---|---|
low | 机械性:原因和更改已知,无需调查 | 重命名、文档、配置、防护或小函数(任务行已说明修复方法) |
mid | 需要一些调查或设计的普通工作(默认) | 新的端点、必须找到原因的 bug |
high | 困难推理或高风险 | 并发、安全、数据迁移、不明确的规范、跨模块设计、棘手的算法 |
父节点仅由其集成步骤(通常为 low 或 mid)来评定;其子节点携带自己的级别。根节点携带优先级和通道;子节点继承两者。
模型路由(可选)
项目通过 README 中的一行将级别映射到模型,例如 Models: low=haiku, mid=sonnet, high=opus(代理理解的任意名称)。如果没有该行,或者没有子代理工具,复杂度仅作为提示,工作在当前模型中运行。如果两者都存在:
/todo-continue --delegate和通道运行会为每个节点(或通道根节点,以其最高复杂度为准)分配一个子代理,运行在映射的模型上。- 审查者运行在节点级别或更高。
- 升级: 验证尝试失败后,下一次尝试将运行高一个级别(
low→mid→high),记录在State:中为tier:。错误的low标签仅消耗一次廉价尝试,而不是阻塞节点。
IDs
- 根节点
<PREFIX>-<NNN>,一个全局序列,3 位数字。子节点附加.n:FEAT-002.1,FEAT-002.1.2。点号等于层级。 - ID 永不重用。下一个根节点编号 = 在
TODO.md、CHANGELOG.md(包括Includes:后的 ID)、docs/plans/*.md以及,在 git 模式下,提交主题(git log --all --format=%s,其中包含被丢弃的 ID)中找到的最高 FEAT-/FIX-/TASK- 编号,加上 1。下一个子节点编号 = 在相同位置找到的该父节点下最高子节点编号,加上 1。
| 前缀 | 含义 | CHANGELOG 标题 | 提交类型 |
|---|---|---|---|
FEAT | 新能力 | ### Added | feat |
FIX | Bug 或回归 | ### Fixed | fix |
TASK | 重构、杂务、文档、测试、性能、构建 | ### Changed | 视情况而定 |
子节点使用其根节点的前缀。
下一个节点是哪个
当节点满足以下条件时,它是可工作的:它是 [ ];每个子节点都是 [x];每个 Depends: ID 都满足(在 CHANGELOG.md 中作为条目 ID 或跟在 Includes: 后,在 git 模式下也在 git show <base>:CHANGELOG.md 中,或者在 TODO.md 中是 [x];当仅基础分支的 CHANGELOG 满足条件时,/todo-continue 会在开始处理节点之前(步骤 3)将基础分支合并到节点将启动的分支;只读命令会报告“等待基础合并”);没有祖先是 [!];并且,在 git 模式下,其根节点未被其他分支声明。当根节点在基础分支的 TODO.md 中的 State:(git show <base>:TODO.md)命名了一个分支时,它被声明;其树仅能从该家族的分支(todo/<ROOT-ID>-… 或 todo/<ROOT-ID>.…)工作。被跳过的已声明树报告为 claimed by <branch>;当该分支在此克隆中存在时,添加“如果是你的工作:git switch <branch>(或 cd 到其工作树)并重新运行以继续”。此克隆中的分支可能属于另一个会话,因此永远不会从基础分支自动恢复。如果在基础分支上待办文件包含未提交的编辑,则 git switch 到该分支会失败:说明情况并提供 git stash push -- <backlog paths> 以及稍后在基础分支上使用 git stash pop。
中断的工作优先(git 模式)。 在树分支(任何 todo/ 分支)上,列出树的分支(git branch --list 'todo/<ROOT-ID>-*' 'todo/<ROOT-ID>.*')并读取每个分支上节点的 git show <branch>:TODO.md。在尚未合并到其父分支(git merge-base --is-ancestor <branch> <parent branch> 失败)的分支上是 [x] 的节点在合并前停止:完成该合并,不做其他操作(/todo-continue 步骤 2)。[/] 的节点被恢复,最深者优先。在任何树分支上是 [!] 的节点在任何地方都计为 [!](不可工作;其祖先等待),即使另一个分支仍显示其为 [ ];其节点不再存在于其父分支的 TODO.md 中(已丢弃)的分支被忽略;已合并到其父分支的分支用 git branch -d 删除。在基础分支上跳过此步骤。
通道
通道是一个命名的根树轨迹,由一个代理上下文单独按顺序工作。根节点上的 Lane: <name>(一个小写字母,后跟小写字母、数字或破折号)将其整个树放入该通道;子节点继承它,没有通道的根节点位于通道 main(因此 --lane main 选择那些根节点)。通道是上下文,而不是任务:一个通道可能包含一个或多个任务,通道彼此并行运行(/todo-continue)。