diff --git a/assets/agents/dev-lifecycle.md b/assets/agents/dev-lifecycle.md index b8852ea..6d3ad4c 100644 --- a/assets/agents/dev-lifecycle.md +++ b/assets/agents/dev-lifecycle.md @@ -47,6 +47,10 @@ permission: ## 全局约束 +### 阶段契约(单一来源) +- 阶段推进遵循 `stage-contract`(assets/prompts/stage-contract.md):阶段完成 = Flow Record body checklist `- [x] `,门禁与顺序以该契约为准 +- 阶段推进调用 `flow_control{op:"stage-start"|"stage-complete"}`(requirements 完成需用户确认;高风险 Flow 的 design→tasks 需确认) + ### 文档目录 - PRD → `docs/prd/` - ADR → `docs/adr/` diff --git a/assets/commands/code.md b/assets/commands/code.md index ef4866f..20fa88e 100644 --- a/assets/commands/code.md +++ b/assets/commands/code.md @@ -1,4 +1,5 @@ --- -description: 编码实现 — 分支 → 编码 → 单测 → PR +description: 编码实现 — TDD 编码 + 单测 → PR --- -请加载 flow-code 技能,按其中定义的流程进行编码实现阶段。 +请加载 flow-code 技能,按其中定义的流程进行编码实现阶段(阶段契约见 stage-contract)。 + diff --git a/assets/commands/design.md b/assets/commands/design.md index aa68b16..a34f476 100644 --- a/assets/commands/design.md +++ b/assets/commands/design.md @@ -1,4 +1,5 @@ --- -description: 技术设计 — 方案设计 → ADR +description: 技术设计 — 方案设计 → ADR → Planning PR --- -请加载 flow-design 技能,按其中定义的流程进行技术设计阶段。 +请加载 flow-design 技能,按其中定义的流程进行技术设计阶段(阶段契约见 stage-contract)。 + diff --git a/assets/commands/handoff.md b/assets/commands/handoff.md deleted file mode 100644 index 4c980ca..0000000 --- a/assets/commands/handoff.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -description: Handoff — 打包上下文,跨会话传递进度 ---- -请加载 flow-handoff 技能,按其中定义的流程打包当前进度。 diff --git a/assets/commands/release.md b/assets/commands/release.md index 1824059..bf6f0ab 100644 --- a/assets/commands/release.md +++ b/assets/commands/release.md @@ -1,4 +1,5 @@ --- -description: 发布 — 版本 → Changelog → Release → npm publish +description: 发布 — 版本 → Release PR → GitHub Actions 发布(仅人工) --- -请加载 flow-release 技能,按其中定义的流程进行发布阶段。 +请加载 flow-release 技能,按其中定义的流程进行发布阶段(阶段契约见 stage-contract)。 + diff --git a/assets/commands/requirements.md b/assets/commands/requirements.md index 27777a0..7d01788 100644 --- a/assets/commands/requirements.md +++ b/assets/commands/requirements.md @@ -1,4 +1,5 @@ --- -description: 需求分析 — 访谈澄清 → PRD → GitHub Issue +description: 需求分析 — 访谈澄清 → PRD → Flow Record --- -请加载 flow-requirements 技能,按其中定义的流程进行需求分析阶段。 +请加载 flow-requirements 技能,按其中定义的流程进行需求分析阶段(阶段契约见 stage-contract)。 + diff --git a/assets/commands/review.md b/assets/commands/review.md index dbd7340..6c54c5d 100644 --- a/assets/commands/review.md +++ b/assets/commands/review.md @@ -1,4 +1,5 @@ --- -description: 代码审查 — AI 审查 → 自动合并 +description: 代码审查 — 双轴审查 → 自动合并 --- -请加载 flow-review 技能,按其中定义的流程进行代码审查阶段。 +请加载 flow-review 技能,按其中定义的流程进行代码审查阶段(阶段契约见 stage-contract)。 + diff --git a/assets/commands/setup.md b/assets/commands/setup.md index d66c97e..c785636 100644 --- a/assets/commands/setup.md +++ b/assets/commands/setup.md @@ -1,4 +1,5 @@ --- -description: 初始化 — 检测 gh CLI → 配置 GitHub → 创建 docs/ 目录 +description: 初始化 — 探测环境 → 生成 workflow → 确认 Profile --- -请加载 flow-setup 技能,按其中定义的流程进行初始化。 +请加载 flow-setup 技能,按其中定义的流程进行初始化(阶段契约见 stage-contract)。 + diff --git a/assets/commands/tasks.md b/assets/commands/tasks.md index b7c3a4d..74de90e 100644 --- a/assets/commands/tasks.md +++ b/assets/commands/tasks.md @@ -1,4 +1,5 @@ --- description: 任务分解 — DAG 拆分 → Sub Issues --- -请加载 flow-tasks 技能,按其中定义的流程进行任务分解阶段。 +请加载 flow-tasks 技能,按其中定义的流程进行任务分解阶段(阶段契约见 stage-contract)。 + diff --git a/assets/commands/test.md b/assets/commands/test.md deleted file mode 100644 index b71d101..0000000 --- a/assets/commands/test.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -description: E2E 测试 — 触发 CI → 监控 → 汇报 ---- -请加载 flow-test 技能,按其中定义的流程进行端到端测试阶段。 diff --git a/assets/prompts/bootstrap.md b/assets/prompts/bootstrap.md index 805b4ef..156bf3c 100644 --- a/assets/prompts/bootstrap.md +++ b/assets/prompts/bootstrap.md @@ -2,18 +2,19 @@ 全流程开发工作流已启用。 可用命令: -- /setup — 首次初始化(gh CLI + GitHub 远程 + docs/ 目录) -- /requirements — 需求访谈 → PRD → GitHub Issue -- /design — 技术方案 + ADR +- /setup — 初始化(探测环境 → 生成 workflow → 确认 Profile) +- /requirements — 需求澄清 → PRD → Flow Record +- /design — 技术方案 + ADR → Planning PR - /tasks — DAG 任务拆解 → Sub Issues -- /code — 分支 → 编码 + 单测 → PR -- /test — 触发 CI → 监控 → 汇报 +- /code — 编码 + TDD(flow-tdd 协议)→ PR - /review — 双轴审查(规范+规格)→ 自动合并 -- /release — ⚠️ 手动阶段:版本 → Changelog → Release → npm publish -- /handoff — 打包上下文,跨会话传递 +- /release — ⚠️ 手动阶段:版本 → Release PR → GitHub Actions 发布 推荐流程(顺序执行): -setup → requirements → design → tasks → code → test → review → merge +setup → requirements → design → tasks → code → review → release + +阶段契约:每个 stage 的完成权威是 Flow Record body checklist(`- [x] `), +阶段推进由 `flow_control{op:"stage-start"|"stage-complete"}` 维护,详见 stage-contract。 ⚡ 自动化模式:需求确认后输入 @dev-lifecycle 自动执行流程(终点为自动合并,不包含 release) diff --git a/assets/prompts/stage-contract.md b/assets/prompts/stage-contract.md new file mode 100644 index 0000000..c5ee121 --- /dev/null +++ b/assets/prompts/stage-contract.md @@ -0,0 +1,55 @@ +# Stage Contract + +Flow 阶段契约的单一来源。Primary(`dev-lifecycle`)与所有 stage 命令(`/setup /requirements /design /tasks /code /review /release`)共享本契约。 + +## 阶段模型 + +| Stage | 阶段名(flow_control) | 产出 | 工具 | +|-------|----------------------|------|------| +| setup | —(前置初始化) | Profile 确认、workflow 就绪 | `setup_control` | +| requirements | `requirements` | PRD、Decision Map、Flow Record、CONTEXT.md 术语 | `flow_control{create-flow}` | +| design | `design` | 技术方案 + ADR(Planning Baseline) | `flow_control{planning-start, planning-pr}` | +| tasks | `tasks` | DAG 任务定义、Sub Issues | `task_control{create-task}` | +| code | `code` | 实现 + TDD evidence + PR | `task_control{start-task, submit-task}` | +| review | `review` | 审查报告、合并 | `task_control{submit-review, merge-task}` | +| release | —(手动) | 版本、Release PR、GitHub Actions 发布 | `release_control` | + +## 完成权威 + +- **阶段完成 = Flow Record(Parent Issue)body checklist 中的 `- [x] `** +- 阶段推进由 `flow_control{op:"stage-start"|"stage-complete"}` 维护:start 打 `cabbage:stage:` label,complete 标记 checklist +- 已完成阶段重复 start/complete → 幂等通过 + +## 门禁规则 + +| 场景 | 要求 | +|------|------| +| stage-start X | 前置阶段须已 completed(requirements 无前置) | +| stage-complete X | 前置阶段须已 completed;**requirements 完成需用户确认一次** | +| stage-start tasks(高风险 Flow,label `cabbage:risk:high`) | 需用户确认(`user_confirmed: true`) | +| 依赖 Task 启动 | 前置依赖 Task 已 merged、并行数 < 5 | + +## 阶段顺序与基线 + +``` +setup → requirements → design → tasks → code → review → (release 手动) +``` + +- **Planning Baseline**:CONTEXT.md + PRD + Design + ADR 经单个 Planning PR 合入后,才允许创建 Task Record +- **requirements 基线**:需求阶段结束须用户确认一次 +- **goal 最小化**:`goal({op:"create", parent_issue_number:})`,目标/验收从 Flow Record 读取 + +## 各阶段关键约束 + +- **requirements**:渐进式澄清(Decision Map),一次只问一个问题;领域术语写入根 CONTEXT.md +- **design**:architect 在 planning worktree 产出文档;方案遵循工程原则(KISS/YAGNI/DRY/SRP) +- **tasks**:垂直切片、可独立验证、无循环依赖;标题用英文功能标题(内核派生 slug) +- **code**:TDD 协议唯一来源是 `flow-tdd` skill;证据经 `tdd_checkpoint` 提交,缺证据 PR 被拒 +- **review**:双轴审查(规范 + 规格)+ TDD criterion coverage;reviewer 只读,写操作经 `task_control` +- **release**:人工流程;按 Profile 版本规则 + 项目 GitHub Actions release workflow(技术栈无关) + +## Prohibited Actions + +- 不跳过前置门禁直接 stage-start/complete +- 不手工修改 Flow Record 的 stage checklist(由 flow_control 维护) +- 不绕过工具直接执行高风险 git/gh 写操作(push/PR/merge/worktree) diff --git a/assets/skills/flow-code/SKILL.md b/assets/skills/flow-code/SKILL.md index 0b5d324..3debcb8 100644 --- a/assets/skills/flow-code/SKILL.md +++ b/assets/skills/flow-code/SKILL.md @@ -1,140 +1,38 @@ --- name: flow-code -description: 分支 → 编码 + 单测 → PR 提交(Worktree 模式) +description: 编码实现 — task_control 创建 worktree + TDD(flow-tdd 协议) --- # flow-code -认领 Sub Issue,创建 worktree,实现代码+单测,提交 PR。 +认领 Task Record(Sub Issue),在 worktree 内按 TDD 实现代码与单测。 +worktree 创建、PR 提交由内核工具 `task_control` 完成,本 skill 不复制 shell 实现。 -## Prerequisites -- `/tasks` 已完成 → Sub Issues 就绪 -- 阅读 `docs/adr/` 确保实现与 ADR 兼容 -- 确认 task 的 `worktree_root` 字段(来自 task 文件 frontmatter) - -## Workflow - -### 1. 选择任务 -选择一个可执行的 Sub Issue(前置依赖已满足)。 - -### 2. 检查 ADR 约束 -阅读相关 ADR,确保实现方案不违反已有架构决策。 - -### 3. 创建/复用 Worktree -```bash -# 探测默认分支 -BASE=$(gh repo view --json defaultBranchRef --jq '.defaultBranchRef.name' 2>/dev/null || git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@' || echo "main") - -# 检查和创建 worktree -WORKTREE=".worktree/" -BRANCH="feat/" - -if [ -d "$WORKTREE" ]; then - # Worktree 目录已存在 → 验证一致性 - EXISTING=$(git -C "$WORKTREE" rev-parse --abbrev-ref HEAD 2>/dev/null) - if [ "$EXISTING" != "$BRANCH" ]; then - echo "ERROR: $WORKTREE exists but tracks branch '$EXISTING' (expected '$BRANCH')" - exit 1 - fi - echo "Worktree 已存在,复用:$WORKTREE" -else - # 检查分支是否已存在 - if git show-ref --verify "refs/heads/$BRANCH" >/dev/null 2>&1; then - # 分支已存在,检查是否已合并 - if git branch --merged "$BASE" | grep -q "$BRANCH"; then - git branch -D "$BRANCH" - else - echo "ERROR: 分支 '$BRANCH' 已存在,请先处理" - exit 1 - fi - fi - # 创建 worktree + 分支 - git worktree add -b "$BRANCH" "$WORKTREE" "$BASE" -fi - -# 进入 worktree -cd "$WORKTREE" -``` -### 4. 安装依赖 - -```bash -npm install -``` - -### 5. 编码 + 单测 -```bash -# 实现代码 + 单元测试 -npm test -``` - -#### TDD Advisory Protocol - -编码阶段必须遵循 `flow-tdd` skill 定义的 TDD 流程。Agent 在编写任何功能代码前加载 `flow-tdd` skill。 - -**TDD 检查点(commit 前自检):** -- [ ] 每个功能的代码变更都有对应的测试 -- [ ] 测试在修改前是 RED(失败),修改后是 GREEN(通过) -- [ ] 已完成 `final-regression`(全部测试通过) -- [ ] 已完成 `final-verification`(对照 acceptance_criteria) -- [ ] self-report 已写入 commit message 或 PR body - -> 引用:`flow-tdd` skill 是 TDD 协议的唯一来源,包含完整的 RED→GREEN cycle 状态机、abandon-cycle 规则和 final-regression/verification 流程。 - -### 6. 文档同步检查 -完成编码后,逐项检查以下文档是否需要同步更新: - -``` -## 文档同步检查清单 -□ guides/quickstart.md — 安装方式或前置条件有变化吗? -□ guides/configuration.md — 新增/修改了配置项吗? -□ guides/usage.md — 命令或行为有变化吗? -□ guides/architecture.md — 架构或流程有变化吗? -□ docs/dev/guides/contributing.md — 开发流程有变化吗? -``` - -- 逐项评估,无需修改的跳过 -- 需要修改的文档随代码一起提交到同一个 PR -- PR body 中列出已同步的文档 - -### 7. 提交 PR -```bash -# 在 worktree 内执行 - -# 根据 task 预期文件和 git status 确定待暂存文件 -# Task 文件通常包含 frontmatter expected_files 字段 -git status --short - -# 显式暂存任务相关文件(不是 git add .) -# 示例:git add src/plugin/goal.ts test/goal.test.ts assets/agents/team/reviewer.md -git add ... - -# 提交前检查:确保无密钥、无超大文件、无任务范围外文件 -git diff --cached --name-only - -git commit -m "feat(): " -git push origin feat/<task-slug> -mkdir -p docs/dev/handoff -echo "Closes #<issue-num>" > docs/dev/handoff/pr-body.md -gh pr create --title "<title>" --body-file docs/dev/handoff/pr-body.md -``` - -### 8. 更新开发文档 -如涉及 API 变更 → 更新 `docs/dev/api/` -如涉及数据模型变更 → 更新 `docs/dev/db/` +## 核心:调用 task_control + flow-tdd -## Output -- Worktree 已创建/复用(`.worktree/<task-slug>/`) -- 代码已推送 -- PR 已创建并关联 Sub Issue(PR body 含 `Closes #<issue-num>`) -- 文档同步 checklist 已完成 -- 已同步的文档随 PR 提交 -- dev docs 已更新 +1. `task_control{op:"start-task", task_id:"<slug>", ...}` — 创建 worktree + 记录基线 + 冻结 TDD policy +2. 加载 `flow-tdd` skill,按 Task 的 `tdd` 配置执行 RED→GREEN cycle +3. 每个 stage 通过 `tdd_checkpoint` 提交证据(唯一证据源,缺证据的 PR 会被拒绝) +4. 本地 `git add` + `git commit`(多次提交) +5. **不 push、不创建 PR** — 由 primary 的 `task_control{op:"submit-task"}` 完成 + +## 编码约束 + +- 只改 Task 相关的文件;遵循项目现有代码规范与分层结构 +- 遵循工程原则(KISS/YAGNI/DRY/SRP/最小变更,单份引用仓库 AGENTS.md) +- 文档同步:涉及 guides/api/db 变更时随代码同 PR 更新 -## 上下文管理 -如果上下文窗口压力大,使用 `../flow-handoff` 打包进度。 +## TDD 引用 + +> `flow-tdd` skill 是 TDD 协议的唯一来源,包含完整的 RED→GREEN cycle 状态机、 +> abandon-cycle 规则和 final-regression/verification 流程,以及 `tdd_checkpoint` 各 op 的调用方式。 + +## Output +- Worktree 已创建(task_control) +- TDD evidence 已提交(tdd_checkpoint) +- 本地 commit 就绪(分支推送与 PR 由 task_control 完成) ## 后续 -- **/test** — 触发 CI E2E 测试 - **/review** — 审查 PR ## Contract @@ -143,33 +41,33 @@ gh pr create --title "<title>" --body-file docs/dev/handoff/pr-body.md 由 `/code` 命令或 `@dev-lifecycle` Phase 3 触发。 ### Inputs -- Sub Issue 编号(来源:`/tasks` 产出) -- task 文件 frontmatter 中的 `worktree_root`(来源:task 文件) +- Task Record(Sub Issue 编号与标题,来源:`/tasks` 产出) ### Preconditions - `/tasks` 已完成 → Sub Issues 就绪 -- 前置依赖任务已合并 +- Planning Baseline 已合入;前置依赖任务已合并(task_control 门禁) ### Procedure -1. 选择依赖已满足的 Sub Issue -2. 创建或复用 Worktree(`git worktree add -b`) -3. 安装依赖 → 编码 + 单测 → commit + push -4. 编排器创建 PR +1. 调用 `task_control{op:"start-task"}` 创建/复用 worktree +2. 加载 `flow-tdd`,按 Task 的 tdd 配置执行 RED→GREEN cycle +3. 通过 `tdd_checkpoint` 提交证据 +4. 本地 git add + commit(不 push) +5. 交回 primary 调用 `task_control{op:"submit-task"}` 创建 PR ### Outputs - Worktree 已创建(`.worktree/<task-slug>/`) -- 代码已推送 -- PR 已创建(Orchestrator 执行) +- TDD evidence(Task Record 单评论) +- 本地 commit ### Failure -- Worktree 创建失败 → 检查分支冲突或目录残留 -- 测试失败 → 修复后重试 +- worktree 创建失败 → 检查分支冲突或目录残留(task_control 报错) +- 测试失败 → 修复后重试(不跳过 RED) ### Idempotency -- Worktree 已存在 → 复用 +- worktree 已存在 → task_control 校验分支一致性后复用 - 代码已提交 → 追加提交 ### Prohibited Actions -- Worker 不创建 PR、不操作 Issue +- 不 push、不创建 PR、不操作 Issue(由 task_control 完成) - 不使用 `git add .` -- 不直接 push 到默认分支 +- 不绕过 task_control 直接执行 worktree 创建 / PR 创建 / 分支推送 diff --git a/assets/skills/flow-design/SKILL.md b/assets/skills/flow-design/SKILL.md index 1e0ba9d..c20e2c2 100644 --- a/assets/skills/flow-design/SKILL.md +++ b/assets/skills/flow-design/SKILL.md @@ -1,26 +1,22 @@ --- name: flow-design -description: 技术方案设计 → 方案文档 + ADR 输出 +description: 技术方案设计 → 方案文档 + ADR(planning worktree + Planning PR) --- # flow-design -基于 PRD 进行技术方案设计,输出方案文档和 ADR。 +基于 PRD 进行技术方案设计,architect 在 planning worktree 产出 CONTEXT.md + PRD + Design + ADR, +并由 `flow_control` 通过 Planning PR 合入形成 Planning Baseline。 -## Prerequisites -- `/requirements` 已完成 → `docs/prd/<title>.md` + Parent Issue 存在 -- 阅读 `../_context/CONTEXT.md` 了解领域术语 +## 核心:调用 flow_control -## Workflow +1. `flow_control{op:"planning-start"}` — 创建 planning worktree `.worktree/planning-<slug>`(architect 在此写文档) +2. 在 planning worktree 内产出:根 `CONTEXT.md` 术语更新 + `docs/prd/<title>.md` + `docs/dev/specs/<title>.md` + `docs/adr/<date>-<slug>.md` +3. `flow_control{op:"planning-pr"}` — 对 planning worktree 变更创建 Planning PR(合并后 = Planning Baseline) -### 1. 阅读上下文 -- 阅读 PRD:`docs/prd/<title>.md` -- 阅读已有 ADR:`docs/adr/`(确保设计不与已有架构决策冲突) -- 阅读 `docs/dev/out-of-scope.md`(了解已排除范围) +worktree 创建与 Planning PR 创建均由内核工具完成,创建动作全部收敛到工具内。 -### 2. 技术方案设计 - -#### 简单性约束 +## 设计约束 技术方案必须遵循以下原则: @@ -29,65 +25,16 @@ description: 技术方案设计 → 方案文档 + ADR 输出 3. **方案自检** — 每个设计决策必须能回答"为什么不用更简单的替代方案?" 4. **ADR 记录简化决策** — 如果选择复杂方案,必须在 ADR 中说明为何简单方案不够 -#### 方案内容 -- 技术选型与理由 -- 架构与数据流 -- API / 数据模型 -- 与已有的 ADR 的兼容性检查 - -### 3. 记录 ADR -为每个关键决策记录 ADR 到 `docs/adr/<YYYY-MM-DD>-<slug>.md`。 -ADR 格式参考 `_prompts/adr-format` 中的格式要求。 - -ADR 至少记录: -- 技术选型决策 -- 架构模式决策 -- 任何可能有争议的方案选择 - -### 4. 附到 GitHub Issue -```bash -mkdir -p docs/dev/handoff -cat > docs/dev/handoff/design-comment.md << 'EOF' -## 技术方案 - -...(摘要) - -## ADR - -...(ADR 列表) - -完整文档:docs/dev/specs/<title>.md -EOF -gh issue comment <issue-number> --body-file docs/dev/handoff/design-comment.md -``` +## CONTEXT.md 术语 -### 5. 提交文档(Planning PR) -设计文档和 ADR 通过 Planning PR 合入,确保分支保护下也能正常工作: - -```bash -# 探测默认分支 -BASE=$(gh repo view --json defaultBranchRef --jq '.defaultBranchRef.name' 2>/dev/null || echo "main") - -# 创建 planning 分支 -git checkout -b chore/plan-<slug> $BASE -git add docs/dev/specs/<title>.md docs/adr/<date>-<slug>.md -git commit -m "docs: <title> — 技术方案 + ADR" -git push origin chore/plan-<slug> - -# 创建 PR -gh pr create --title "docs: <title> — 技术方案 + ADR" \ - --body "Planning PR:完整技术方案和架构决策记录" \ - --base $BASE - -# 合并 PR 后切回 $BASE -git checkout $BASE && git pull origin $BASE -``` +阅读项目根 CONTEXT.md 与 skills 内 `_context/CONTEXT.md` 了解领域术语; +设计中发现的新术语经用户确认后写入根 CONTEXT.md(术语权威)。 ## Output - `docs/dev/specs/<title>.md` — 技术方案 - `docs/adr/<date>-<slug>.md` — ADR 决议 -- Parent Issue 收到设计评论 -- 文档通过 Planning PR 合入默认分支 +- 根 CONTEXT.md 术语更新(如发现新术语) +- Planning PR(由 flow_control 创建) ## 后续 - **/tasks** — 基于设计方案拆解为 DAG 任务 @@ -99,31 +46,33 @@ git checkout $BASE && git pull origin $BASE ### Inputs - `docs/prd/<title>.md` — 上游 PRD(来源:requirements 阶段产出) -- Parent Issue 编号(来源:Goal metadata 或 `/requirements` 阶段产出) +- Flow Record 编号(来源:requirements 阶段产出) ### Preconditions -- `/requirements` 已完成 → `docs/prd/<title>.md` 存在 -- Parent Issue 存在 +- `/requirements` 已完成 → PRD 与 Flow Record 存在 +- requirements 基线已用户确认 ### Procedure -1. 阅读 PRD、已有 ADR、Out of Scope -2. 输出技术方案到 `docs/dev/specs/<title>.md` -3. 记录 ADR 到 `docs/adr/<YYYY-MM-DD>-<slug>.md` -4. 附设计评论到 Parent Issue -5. 通过 Planning PR 提交文档 +1. 调用 `flow_control{op:"planning-start"}` 创建 planning worktree +2. 阅读 PRD、已有 ADR、根 CONTEXT.md 术语 +3. 输出技术方案到 `docs/dev/specs/<title>.md` +4. 记录 ADR 到 `docs/adr/<YYYY-MM-DD>-<slug>.md` +5. 调用 `flow_control{op:"planning-pr"}` 创建 Planning PR ### Outputs -- `docs/dev/specs/<title>.md` — 技术方案 -- `docs/adr/<date>-<slug>.md` — ADR 决议 +- 技术方案 + ADR(planning worktree 内) +- Planning PR(Planning Baseline 的一部分) ### Failure - ADR 与已有决策冲突 → 记录冲突并标注 -- Planning PR 合并失败 → 通知用户手动处理 +- Planning PR 创建失败 → 通知用户手动处理 ### Idempotency - 技术方案已存在 → 读取并更新 - ADR 已存在 → 不重复创建 +- planning worktree 已存在 → 复用(工具校验分支一致性) ### Prohibited Actions -- 不直接 push 到默认分支 +- 不绕过 flow_control 手工创建 worktree / Planning PR(由内核工具完成) - 不跳过 ADR 兼容性检查 +- 不直接 push 到默认分支 diff --git a/assets/skills/flow-handoff/SKILL.md b/assets/skills/flow-handoff/SKILL.md deleted file mode 100644 index b137daf..0000000 --- a/assets/skills/flow-handoff/SKILL.md +++ /dev/null @@ -1,76 +0,0 @@ ---- -name: flow-handoff -description: 打包上下文,跨会话传递进度 ---- - -# flow-handoff - -当上下文窗口压力过大或需要跨会话传递进度时使用。 - -## 何时使用 -- 上下文窗口接近极限(约 80k+ tokens) -- 需要切换到另一个任务再回来 -- 当前阶段等待外部输入无法继续 - -## Workflow - -### 1. 打包当前状态 -创建 handoff 文档,记录: - -```markdown -# Handoff: <流程/阶段名> - -## 上下文 -- 项目: <项目名> -- 当前阶段: <stage> -- 已完成的工作: <列表> -- 未完成的决策: <列表> - -## 关键产出 -- <已产出的文件/Issue/PR 列表> - -## 下一步 -1. <下一步行动> -2. <需要用户输入的问题> - -## 待办 -- [ ] <待完成任务> -``` - -### 2. 保存 Handoff 文件 -保存到 `docs/dev/handoff-<YYYY-MM-DD>.md`。 - -### 3. 指示用户 -告知用户:在下次会话中可以直接引用该 handoff 文件以恢复上下文。 - -## Output -- `docs/dev/handoff-<date>.md` - -## Contract - -### Trigger -由 `/handoff` 命令触发。上下文窗口接近极限或需跨会话传递进度时使用。 - -### Inputs -- 当前 Goal 状态(来源:Goal metadata) - -### Preconditions -- 存在 Active Goal - -### Procedure -1. 打包当前流程状态(阶段、任务、进度) -2. 列出已完成和待完成项 -3. 确定下一步骤 -4. 保存到 handoff 文件 - -### Outputs -- `docs/dev/handoff-<date>.md` — 交接文件 - -### Failure -- 无 Goal → 仅输出当前会话摘要 - -### Idempotency -- 同一天多次执行 → 覆盖前一次 handoff - -### Prohibited Actions -- 不修改代码或文档 diff --git a/assets/skills/flow-release/SKILL.md b/assets/skills/flow-release/SKILL.md index 25a914f..9916235 100644 --- a/assets/skills/flow-release/SKILL.md +++ b/assets/skills/flow-release/SKILL.md @@ -1,54 +1,34 @@ --- name: flow-release -description: 版本 → Changelog → Release → npm publish(仅人工) +description: 版本 → Release PR → GitHub Actions 发布(release_control,仅人工) --- # flow-release -版本号更新 → tag 推送 → Release 草稿 → 人工审核发布 → 自动 npm publish。 +通过 `release_control` 完成版本提议、Release PR、合并发布与发布监控。 +发布流程技术栈无关:按 Profile 的版本规则(version file / tag format / release workflow)执行, +最终发布走项目自己的 GitHub Actions release workflow(不假设 npm 或任何特定生态)。 -## Prerequisites -- main 分支最新,所有 PR 已合并 -- CI 通过 +## 核心:调用 release_control -## Workflow +1. `release_control{op:"propose-version"}` — 聚合上一 tag 后全部 commit,按 Profile 版本规则分类,输出提议版本 +2. `release_control{op:"open-release-pr"}` — 固定顺序:release branch → 按 Profile 写规则更新版本 → Release PR(人工确认后) +3. `release_control{op:"merge-release-pr"}` — CI + 人工批准 → merge → SHA 校验 → 打 tag push +4. `release_control{op:"monitor"}` — 轮询 GitHub Actions release workflow 至成功;瞬时失败 rerun;代码/配置缺陷 → Corrective Flow -### 1. 确认版本号 -从 conventional commits 自动确定 semver bump: -```bash -git log $(git describe --tags --abbrev=0)..HEAD --oneline -``` -- `fix:` → patch | `feat:` → minor | `BREAKING:` → major +## 版本规则(Profile) -### 2. 更新版本号 → 打 tag → 推送 -```bash -npm version <major|minor|patch> --no-git-tag-version -# 更新 CHANGELOG.md -git commit -m "chore(release): v<version>" -BASE=$(gh repo view --json defaultBranchRef --jq '.defaultBranchRef.name') -git tag v<version> -git push origin $BASE --tags -``` - -### 3. Release 草稿(自动) -tag 推送触发 `.github/workflows/release-draft.yml`: -- 构建 + 测试 -- 生成 Release 草稿(含自动 release notes) - -### 4. 人工审核发布 -GitHub Releases 页面 → 检查 Release 草稿 → 点击 **Publish release**。 - -发布触发 `.github/workflows/release-publish.yml`: -- 自动 `npm publish`(需要 `NPM_TOKEN` 仓库 Secret) +版本分类依赖 AGENTS.md `## Project Profile` 的 `version bump rule`: +`breaking→major, feature→minor, fix→patch`(0.x breaking 也 major)。 +未分类 commit → release_control 要求用户分类后重试。 ## Output -- 版本已更新 -- tag 已推送 -- Release 草稿已创建(待人工发布) -- 发布后自动推送 npm +- 提议版本 +- Release PR(合并 + tag push) +- GitHub Actions release workflow 运行结果 ## 后续 -无需后续阶段。Flow 完成。 +- 发布完成后 Flow 结束(goal-verify 独立验证 → complete-flow) ## Contract @@ -56,31 +36,31 @@ GitHub Releases 页面 → 检查 Release 草稿 → 点击 **Publish release** 由 `/release` 命令触发。**仅手动触发**,不包含在自动流程中。 ### Inputs -- 版本号类型(patch/minor/major,来源:用户指定) +- 用户对提议版本/Release 的确认 ### Preconditions - 所有 PR 已合并 -- 默认分支最新 -- 所有测试通过 +- 默认分支最新且 CI 通过 +- Release Profile 已确认(AGENTS.md Project Profile) ### Procedure -1. 确认版本号 -2. 更新版本(npm version) -3. 生成 Changelog -4. 创建 git tag → push → 触发 Release 草稿 -5. 人工审核发布 → npm publish +1. 调用 `release_control{op:"propose-version"}` 确定版本 +2. 调用 `release_control{op:"open-release-pr"}` 创建 Release PR(人工确认) +3. 调用 `release_control{op:"merge-release-pr"}` 合并 + 打 tag +4. 调用 `release_control{op:"monitor"}` 监控发布 workflow ### Outputs -- GitHub Release -- npm 包已发布 +- GitHub Release(经项目 release workflow) +- tag 已推送 ### Failure -- npm publish 失败 → 检查 npm 认证 +- 发布 workflow 失败 → monitor 识别瞬时失败并 rerun;代码/配置缺陷 → Corrective Flow + 新版本 - tag 已存在 → 提示版本冲突 ### Idempotency -- 相同版本号 → 阻止 +- 相同版本号 → 阻止重复发布 ### Prohibited Actions -- 不在自动模式中执行 -- 不跳过测试直接发布 +- 不在自动模式中执行(release 为人工流程) +- 不假设 npm 或其他特定技术生态(按 Profile 规则 + 项目 GitHub Actions release workflow) +- 不跳过测试/CI 直接发布 diff --git a/assets/skills/flow-requirements/SKILL.md b/assets/skills/flow-requirements/SKILL.md index 49592c3..4ed140a 100644 --- a/assets/skills/flow-requirements/SKILL.md +++ b/assets/skills/flow-requirements/SKILL.md @@ -1,249 +1,84 @@ --- name: flow-requirements -description: 渐进式需求澄清 → PRD 输出 → GitHub Issue 创建 +description: 渐进式需求澄清 → PRD → Flow Record(flow_control) --- # flow-requirements -通过 Decision Map 驱动的渐进式需求澄清,将松散想法逐步凝固为 PRD 并创建 GitHub Issue。 +通过渐进式需求澄清将松散想法凝固为 PRD,并用 `flow_control{op:"create-flow"}` 创建 Flow Record(Parent Issue)。 ## 核心理念:战争迷雾(Fog of War) -需求澄清不是一次性穷举——你无法在动手前预测所有问题。正确的方式是: - -- 只定义**前沿**(当前可见的待决策问题) -- 逐个解决前沿问题,每解决一个,迷雾推开一层,新问题浮现 -- 直到前沿推至足够远,所有关键决策已定,PRD 自然成形 - -## Prerequisites - -- `/setup` 已完成(gh CLI 可用,docs 目录就绪) - ---- +需求澄清不是一次性穷举——只定义**前沿**(当前可见的待决策问题),逐个解决,每解决一个迷雾推开一层,直到所有关键决策已定,PRD 自然成形。 ## Phase A:创建决策映射 -**目标**:把用户的松散想法转化为结构化的待决策问题清单。 - -### A1. 锚定核心问题 - -先问第一个问题,锁定需求的核心: - -> "这个需求最终要解决什么问题?不做会怎样?" - -不要同时问多个问题。得到回答后再继续。 - -### A2. 构建 Decision Map - -根据回答,在 `docs/dev/decision-map.md` 创建决策映射。映射是一个紧凑的 Markdown 文件,用于追踪所有待决策问题。 - -**映射结构:** - -```markdown -# Decision Map: <需求标题> - -## <ticket-slug>: <问题简述> - -Blocked by: <slug>, <slug> (无依赖则省略此行) -Status: open -Type: Grilling | Research | Prototype - -### Question -<一句话描述需要决策的问题> +在 `docs/dev/decision-map.md` 创建映射,追踪所有待决策问题(Ticket 含 slug / Blocked by / Status / Type / Question / Answer)。初始只创建 2–4 个前沿 Ticket,不试图穷举。 -### Answer -<解决后填写> -``` - -**规则:** -- `slug` 是 dash-case 短标识符,在映射内唯一,读起来像微型标题(如 `core-scenario`、`data-model`、`auth-strategy`) -- 初始只创建 2–4 个**前沿 Ticket**——即当前能直接开始解决的,不要试图穷举 -- 前沿之外的问题暂时不写,等迷雾推开后再添加 -- 有依赖关系的 Ticket,用 `Blocked by` 声明前置条件 - -**Ticket 类型:** - -| Type | 用途 | 执行方式 | -|------|------|----------| -| **Grilling**(默认) | 需要和用户对话澄清的决策 | 见 Phase B - Grilling 方法 | -| **Research** | 需要查阅文档、API、代码库 | 调研 → 输出摘要 → 记录答案 | -| **Prototype** | 需要低保真原型来验证方案 | 创建原型 → 确认 → 记录答案 | - ---- +Ticket 类型:Grilling(与用户对话澄清)/ Research(查文档)/ Prototype(低保真验证)。 ## Phase B:渐进式解决 -**规则:** -- 每次只处理**一个**已解除阻塞的 Ticket(`Status: open` 且所有 `Blocked by` 的 Ticket 已 `resolved`) -- 处理前先 **Claim**:将 `Status` 设为 `in-progress`,保存映射 -- 根据 Ticket 的 `Type` 选择执行方式 -- 解决后将 `Status` 设为 `resolved`,在 `Answer` 中记录结论 -- 每解决一个 Ticket,检查是否有新问题浮现 → 插入新 Ticket(标注 `Blocked by`) +- 每次只处理**一个**已解除阻塞的 Ticket;处理前 Claim(`Status: in-progress`) +- Grilling 型:**一次只问一个问题**,每个问题给出推荐答案,沿决策树分支深入 +- 追问技巧:"然后呢?"(第 3 层才触及本质)、"如果不做这个会怎样?"(验证优先级)、"谁来判断做对了?"(明确验收) +- 解决后 `Status: resolved` 并记录 Answer;检查是否有新问题浮现 → 追加 Ticket - 循环直到所有前沿 Ticket 已 resolved -### B1. Grilling 型 Ticket 的执行方法 - -对 Grilling 型 Ticket,采用逐层拷问方法: - -**核心原则:** -- **一次只问一个问题**。同时问多个问题会让人困惑。 -- 沿决策树的每条分支逐一深入,逐个解决决策之间的依赖关系。 -- **每个问题必须给出推荐答案**,不能只提问不表态。 -- 得到用户反馈后再继续下一个问题。 - -**追问技巧:** -1. **"然后呢?"** — 前 2 个回答通常只是表面,追问到第 3 层才触及本质 -2. **"如果不做这个会怎样?"** — 验证真实优先级 -3. **"谁来判断这个做对了?"** — 明确决策者和验收标准 -4. **"和现有功能的关系?"** — 发现隐藏的依赖和冲突 -5. **"这个简单"或"这个以后再说"** — 标记为 out-of-scope,避免遗漏 - -**覆盖维度:** -每个 Grilling Ticket 在拷问过程中应覆盖以下维度(按需,不是每个都要问完): -- 用户故事与业务价值 -- 边界条件与异常场景 -- 验收标准(追问到可验证的程度) -- 技术约束(语言/框架、部署环境、性能要求) -- 优先级:P0(必须)/ P1(重要)/ P2(锦上添花) - -**结束条件:** -当用户对某个决策分支的回答不再引出新问题时,该分支已澄清完毕。标记 `Status: resolved`,填写 `Answer`。 - -### B2. Research 型 Ticket 的执行方法 - -1. 明确调研目标:需要回答什么问题? -2. 查阅相关文档、API 参考、代码库 -3. 输出简短摘要到 `Answer` -4. 标记 `Status: resolved` - -### B3. Prototype 型 Ticket 的执行方法 - -1. 明确验证目标:这个原型要证明什么? -2. 创建低保真原型(提纲、草图、桩代码) -3. 和用户确认方案是否符合预期 -4. 结论写入 `Answer`,标记 `Status: resolved` - -### B4. 迷雾检查 - -每解决一个 Ticket 后,检查是否推开迷雾: - -- **有新问题浮现?** → 在映射末尾追加新 Ticket,标注正确的 `Blocked by` 边 -- **之前的决策因此变更?** → 更新或标记相关 Ticket -- **前沿是否已推至足够远?** → 所有关键决策已定 → 进入 Phase C - ---- - ## Phase C:凝固为 PRD -当所有前沿 Ticket 均已 `resolved`,且没有新的关键问题浮现时,将 Decision Map 凝固为 PRD。 - -### PRD 生成规则 - -不是凭空编写,而是从 Decision Map 的 `Answer` 中提炼: - -1. 将每个 Ticket 的 `Question` + `Answer` 映射为 PRD 的对应章节 -2. 补充:背景、非功能性需求、风险 -3. 从 Grilling 过程中标记的 out-of-scope 项,记录到 `docs/dev/out-of-scope.md` - -### PRD 文档路径 - -保存到 `docs/prd/<title>.md`。 - -### Out-of-Scope 记录 - -将在澄清过程中明确排除的需求记入 `docs/dev/out-of-scope.md`: -- 说明排除原因 -- 便于后续回顾,避免重复讨论 - ---- - -## Phase D:创建 GitHub Issue - -```bash -mkdir -p tmp -gh issue create \ - --title "<PRD Title>" \ - --label "enhancement" \ - --body-file docs/prd/<title>.md -``` - ---- - -## 多会话协作 +从 Decision Map 的 Answer 提炼为 PRD,保存到 `docs/prd/<title>.md`。 -如果需求澄清跨越多个会话,Decision Map 已持久化在 `docs/dev/decision-map.md`。后续会话: +## Phase D:创建 Flow Record -1. 加载整个映射作为上下文 -2. 找到第一个 `Status: open` 且已解除阻塞的 Ticket -3. Claim → 解决 → 迷雾检查 → 重复 -4. 每个会话最多解决一个 Ticket,结束时输出下一步指令 +调用 `flow_control{op:"create-flow", title:"<英文功能标题>"}` 创建 Draft Parent Issue(Flow Record)。 +内核负责 slug 派生与校验、Profile 门禁、双 session 检查、legacy 检测——创建动作全部收敛到工具内。 -**会话结束时的 Handoff 格式:** +## CONTEXT.md 术语发现 -> **Next steps** — N 个 Ticket 已解除阻塞: `<slug>`, `<slug>` -> -> 继续解决下一个已解除阻塞的 Ticket: -> ``` -> /requirements ,继续推进 Decision Map(docs/dev/decision-map.md) -> ``` - ---- +需求澄清中发现的领域术语写入根 `CONTEXT.md`(术语权威)。发现新术语或冲突时暂停提问,用户确认后由 `flow_control` 更新。 ## Output - -- `docs/dev/decision-map.md` — 决策映射(需求阶段临时文件,PRD 生成后可删除) -- `docs/prd/<title>.md` — PRD 文档 -- `docs/dev/out-of-scope.md` — 排除范围记录 -- GitHub Parent Issue +- `docs/dev/decision-map.md` — 决策映射(PRD 生成后可删除) +- `docs/prd/<title>.md` — PRD +- Flow Record(Parent Issue,由 flow_control 创建) ## 下一阶段 - -- **/design** — 基于此 PRD 进行技术设计 - ---- +- **/design** — 基于 PRD 进行技术设计 ## Contract ### Trigger - -由 `/requirements` 命令或 `@dev-lifecycle` Phase 0 触发。 +由 `/requirements` 命令或 `@dev-lifecycle` 触发。 ### Inputs - - 用户提供的功能描述(来自消息文本) ### Preconditions - -- `/setup` 已完成(gh CLI 可用,docs 目录就绪) +- `/setup` 已完成(Profile 已确认,gh CLI 可用) ### Procedure - 1. 锚定核心问题,创建 Decision Map 2. 渐进式解决所有前沿 Ticket(Grilling / Research / Prototype) 3. 迷雾推至足够远 → 凝固为 PRD -4. 记录 Out of Scope -5. 创建 Parent GitHub Issue +4. 记录领域术语到 CONTEXT.md +5. 调用 `flow_control{op:"create-flow"}` 创建 Flow Record ### Outputs - -- `docs/dev/decision-map.md` — 决策映射 -- `docs/prd/<title>.md` — PRD 文档 -- `docs/dev/out-of-scope.md` — 排除范围记录 -- GitHub Parent Issue +- `docs/dev/decision-map.md` +- `docs/prd/<title>.md` +- Flow Record(Parent Issue) ### Failure - -- gh CLI 不可用 → 提示先执行 /setup +- Profile 未确认 → flow_control 返回 `SETUP_REQUIRED`,提示先执行 `/setup` - Issue 创建失败 → 记录错误,不阻塞 PRD 写入 ### Idempotency - -- 如果 Decision Map 已存在 → 从中断点继续,不重新创建 -- 如果 PRD 文件已存在 → 更新而非覆盖 -- 如果 Parent Issue 已创建 → 追加评论而非重复创建 +- Decision Map 已存在 → 从中断点继续 +- PRD 文件已存在 → 更新而非覆盖 +- Flow Record 已创建 → 绑定已有 Issue(`parent_issue_number`)而非重复创建 ### Prohibited Actions - - 不跳过 Phase A 和 Phase B 直接输出 PRD - 不一次问多个问题 -- 不省略 Out of Scope 记录 +- 不绕过 flow_control 手工创建 Issue(由 create-flow 完成) diff --git a/assets/skills/flow-review/SKILL.md b/assets/skills/flow-review/SKILL.md index f311c69..bd6c09e 100644 --- a/assets/skills/flow-review/SKILL.md +++ b/assets/skills/flow-review/SKILL.md @@ -1,140 +1,44 @@ --- name: flow-review -description: 双轴审查(规范 + 规格)→ 自动合并 +description: 双轴审查(规范 + 规格)→ task_control submit-review / merge-task --- # flow-review 沿两条轴线审查 PR 代码:规范(是否符合编码标准)和规格(是否实现了原始需求)。 +审查结果由 primary 通过 `task_control` 提交与合并,reviewer 本身只读。 -## Prerequisites -- PR 已创建 -- 可选的 `/test` 已完成 +## 核心:调用 task_control -## Workflow +1. `task_control{op:"submit-review", pr_number, verdict:"approve"|"request-changes", ...}` — 发布审查结果 +2. `task_control{op:"merge-task", pr_number, ...}` — 合并(CI + 分支保护 + 风险分级 + 高风险人类 approval) +3. `task_control{op:"status-task", task_id}` — 查看 Task/PR 状态 -### 1. 获取审查材料 +reviewer 只读:不直接 `gh pr review` / `gh pr merge`(写操作由 primary 经 task_control 执行)。 -**代码审查必须在本地分支/worktree 中进行,禁止通过 WebFetch 或浏览器访问远程 PR 页面获取代码。** - -```bash -# 切换到对应分支(如非 worktree 模式) -git checkout feat/<task-slug> - -# 或在 worktree 模式下,直接进入 worktree 目录 -cd .worktree/<task-slug> - -# 获取 PR diff 和元数据 -gh pr diff <pr-number> -gh pr view <pr-number> --json title,body,files -``` - -### 2. 阅读约束来源 -- 检查相关 ADR(`docs/adr/`)— 确认代码是否遵循架构决策 -- 确认规格来源:commit 消息中的 Issue 引用或 `docs/prd/` / `docs/dev/specs/` - -### 3. 文档同步确认 -确认 PR 中是否包含文档同步: -- 检查 PR body 是否列出已同步的文档 -- 检查 `docs/guides/` 和 `docs/dev/guides/` 是否有相应变更 -- 如涉及配置/API 变更但文档未同步 → 标记为阻断性问题 - -### 4. 双轴审查 -并行检查两个维度: +## 双轴审查 **规范轴(Normative)** — 代码是否符合文档化的编码标准? 参考 `references/smell-baseline.md` 中的代码气味基线。 **规格轴(Specification)** — 代码是否忠实实现了需求? 对照 PRD(`docs/prd/`)和设计方案(`docs/dev/specs/`)验证。 -- 如发现实现与设计偏差 → 追加到 `docs/dev/changelog/<YYYY-MM-DD-NNN-slug>.md` +- 如发现实现与设计偏差 → 追加到 changelog **TDD Criterion Coverage** — 检查 Task 的 `acceptance_criteria` 是否被 PR 覆盖: - 对照 Task frontmatter 中的 `acceptance` 数组,逐条核查 - 检查 PR 是否包含每条 criterion 对应的测试或验证 - 检查 `tdd` 配置块中的 `mode` 和 `min_cycles` 是否被遵循 -- **明确不检查 commit order** — TDD cycle 的 RED→GREEN 顺序是编码过程约束, - reviewer 不审查 commit 历史是否呈现 RED-first 模式 - -### 5. 合并前检查 -在合并前确认以下项: -- 文档同步已完成(`docs/guides/` 已更新或无需更新) -- changelog 已记录偏差(如有) -- CI 已通过 - -### 6. 发表审查意见 -发现阻断性问题: -```bash -gh pr review <pr-number> --request-changes --body "..." -``` -无问题或非阻断性建议: -```bash -gh pr review <pr-number> --approve --body "..." -``` - -### 7. 等待 CI + 自动合并 -```bash -gh pr checks <pr-number> --watch -gh pr merge <pr-number> --squash --delete-branch -``` - -### 8. 关闭关联 Sub Issue -如果 PR body 未包含 `Closes #<num>`(或 PR 合并后 GitHub 未自动关闭),手动关闭: -```bash -gh issue close <issue-num> --comment "已完成,已合并至 main" -``` - -### 9. 清理 Worktree -PR 合并后,安全清理对应的 worktree 和分支: - -```bash -WORKTREE=".worktree/<task-slug>" -BRANCH="feat/<task-slug>" - -# Preflight 检查 -echo "=== Worktree 清理 Preflight ===" - -# 1. 确认 PR 已合并 -PR_NUM=<pr-number> -if ! gh pr view "$PR_NUM" --json merged --jq '.merged' 2>/dev/null | grep -q true; then - echo "ERROR: PR #$PR_NUM 未合并,跳过清理" - exit 1 -fi - -# 2. 确认分支已推送到远程 -if git ls-remote --exit-code origin "$BRANCH" >/dev/null 2>&1; then - echo "分支 $BRANCH 已推送到远程" -else - echo "WARNING: 分支 $BRANCH 未推送到远程,但 PR 已合并,继续清理" -fi - -# 3. 检查 worktree 是否干净 -if [ -d "$WORKTREE" ]; then - DIRTY=$(git -C "$WORKTREE" status --porcelain) - if [ -n "$DIRTY" ]; then - echo "WARNING: Worktree 有未提交变更:" - echo "$DIRTY" - echo "暂停。如需强制清理请手动执行 --force" - exit 1 - fi - - # 4. 全部通过 → 清理 - git worktree remove "$WORKTREE" - git branch -D "$BRANCH" 2>/dev/null || true - echo "Worktree 已清理:$WORKTREE" -else - echo "Worktree 不存在,跳过清理" -fi -``` - -> 清理前必须验证 PR 已合并、worktree 干净。只有在所有 preflight 检查通过后才删除。 -> 如果 PR 已合并但 worktree 仍有未提交变更,暂停并通知用户,不自动 --force。 +- **明确不检查 commit order** — RED→GREEN 顺序是编码过程约束,reviewer 不审查 commit 历史 + +## 审查材料获取 + +代码审查必须在本地分支/worktree 中进行,禁止通过 WebFetch 或浏览器访问远程 PR 页面获取代码。 ## Output -- PR 已审查 -- PR 已合并(条件满足时) -- 关联 Sub Issue 已关闭 -- changelog 已记录偏差(如有) +- 结构化审查报告(APPROVED / CHANGES_REQUESTED) +- PR 已合并(task_control merge-task,条件满足时) +- Worktree 已清理(task_control merge-task 后 preflight 销毁) ## 后续 - **/release** — 发布(如所有 PR 已合并) @@ -145,26 +49,25 @@ fi 由 `/review` 命令或 `@dev-lifecycle` Phase 3 审查步骤触发。 ### Inputs -- PR 编号(来源:`/code` 产出) +- PR 编号与 Task Record(来源:`/code` 产出) ### Preconditions - `/code` 已完成 → PR 已创建 ### Procedure -1. 获取 PR diff 和元数据 -2. 双轴审查(规范轴 + 规格轴) +1. 在本地 worktree/分支获取 PR diff 与元数据 +2. 双轴审查(规范轴 + 规格轴)+ TDD criterion coverage 3. 输出结构化审查报告 -4. 编排器发布审查结果 -5. 等待 CI 通过后合并 -6. 关闭关联 Sub Issue -7. 安全清理 Worktree(Preflight) +4. primary 调用 `task_control{op:"submit-review"}` 发布结果 +5. CI 通过后 `task_control{op:"merge-task"}` 合并(工具执行清理) ### Outputs - 结构化审查报告(APPROVED / CHANGES_REQUESTED) - PR 已合并(条件满足时) +- Worktree 已清理 ### Failure -- Critical/High → Request Changes +- Critical/High → request-changes - Worktree 清理失败 → 记录警告,不阻塞 ### Idempotency @@ -172,6 +75,6 @@ fi - 已审查的 PR → 更新结论 ### Prohibited Actions -- **禁止使用 WebFetch 或任何 Web 工具获取远程 PR 代码** — 审查代码时必须在已 checkout 的本地分支或 worktree 中直接读取源码文件 +- **禁止使用 WebFetch 或任何 Web 工具获取远程 PR 代码** — 审查代码时必须本地读取源码文件 +- reviewer 不直接执行 `gh pr review` / `gh pr merge`(由 task_control 完成) - 不使用默认 --force 清理 -- 不直接执行 gh pr merge(Orchestrator 执行) diff --git a/assets/skills/flow-setup/SKILL.md b/assets/skills/flow-setup/SKILL.md index 7008f43..bbdddd4 100644 --- a/assets/skills/flow-setup/SKILL.md +++ b/assets/skills/flow-setup/SKILL.md @@ -1,46 +1,42 @@ --- name: flow-setup -description: 初始化项目文档结构,验证开发环境 +description: 初始化 — 探测环境 → 生成 workflow → 确认 Profile --- # flow-setup -初始化项目文档结构,验证开发环境。 +初始化阶段:探测开发环境、生成缺失的 CI/release workflow、确认 AGENTS.md Project Profile。 +所有探测与写入由内核工具 `setup_control` 完成,本 skill 只定义流程,不复制工具实现。 -## Workflow +## 核心:调用 setup_control -### 1. 创建文档目录 -确保以下目录结构存在: -``` -docs/ -├── prd/ # 产品需求文档 -├── adr/ # 架构决策记录 -└── dev/ - ├── specs/ # 技术方案 - ├── tasks/ # DAG 任务定义 - ├── api/ # API 设计文档 - ├── db/ # 数据库设计 - ├── guides/ # 开发指南 - └── handoff/ # 上下文交接 -``` +调用 `setup_control` 完成三个阶段: -### 2. 验证环境 -```bash -gh auth status -git remote get-url origin -``` -确保 gh CLI 已认证、项目已关联 GitHub 远程。 +1. `setup_control{op:"probe"}` — 探测 git/gh/CI/Profile,输出 readiness 报告 +2. `setup_control{op:"generate-workflows"}` — 缺失时生成 CI/release workflow 草案 + Setup PR(人工合入) +3. `setup_control{op:"confirm-profile"}` — 用户确认后将 Profile 区块写入根 AGENTS.md -### 3. 完成标记 -```bash -mkdir -p .opencode/opencode-cabbage -echo "setup-complete" > .opencode/opencode-cabbage/setup-complete +Profile 区块格式(内核解析白名单,§9.1): + +```markdown +## Project Profile + +- test command: `npm test -- run` +- test file patterns: `test/**/*.test.ts` +- implementation file patterns: `src/**/*.ts` +- tdd default mode: `strict` +- version bump rule: `breaking→major, feature→minor, fix→patch` +- version file: `package.json` +- tag format: `v{version}` +- release workflow: `.github/workflows/release.yml` ``` +未确认 Profile 时 `flow_control{op:"create-flow"}` 会阻断并提示先执行 `/setup`。 + ## Output -- `docs/` 目录已就绪 -- 开发环境已验证 -- 可开始 `/requirements` +- readiness 报告(development-ready / release-ready 逐项布尔) +- `.github/workflows/` 草案(缺失时,经 Setup PR 人工合入) +- 根 AGENTS.md `## Project Profile` 区块 ## Contract @@ -48,28 +44,29 @@ echo "setup-complete" > .opencode/opencode-cabbage/setup-complete 由 `/setup` 命令触发。首次使用插件或切换新项目时执行。 ### Inputs -无外部输入。从当前工作目录检测项目状态。 +- 用户对 readiness 报告 / Profile 覆盖项的确认 ### Preconditions -无。不要求任何前置阶段。 +- gh CLI 已认证(宿主 `gh auth`) ### Procedure -1. 创建 docs 目录结构 -2. 验证 gh CLI 和 GitHub 远程 -3. 写入完成标记 +1. 调用 `setup_control{op:"probe"}` 读取 readiness 报告 +2. 缺失 workflow 时调用 `setup_control{op:"generate-workflows"}` 生成草案 +3. 用户确认后调用 `setup_control{op:"confirm-profile"}` 写入 Profile ### Outputs -- `docs/` 目录树(prd, adr, dev/specs, dev/tasks, dev/api, dev/db, dev/guides, dev/handoff) -- `.opencode/opencode-cabbage/setup-complete` 标记文件 +- readiness 报告 +- workflow 草案(可选) +- 根 AGENTS.md Profile 区块 ### Failure -- 目录创建失败 → 报告错误并退出 -- gh auth 失败 → 提示用户执行 `gh auth login` +- gh auth 失败 → 提示用户执行 `gh auth login` 后重试 +- workflow 生成失败 → 报告错误并停止 ### Idempotency -- 已存在的目录跳过 -- 已存在 `setup-complete` 标记则跳过全部步骤 +- Profile 已确认 → 重复 confirm 覆盖写入(工具内读后写) +- workflow 已存在 → 跳过生成 ### Prohibited Actions -- 不删除已有目录或文件 -- 不修改项目代码 +- 不手工编辑 AGENTS.md Profile 区块(由 setup_control 写回) +- 不直接 push 或创建 Setup PR(工具内部执行) diff --git a/assets/skills/flow-tasks/SKILL.md b/assets/skills/flow-tasks/SKILL.md index b22ef86..c6ea60f 100644 --- a/assets/skills/flow-tasks/SKILL.md +++ b/assets/skills/flow-tasks/SKILL.md @@ -1,172 +1,43 @@ --- name: flow-tasks -description: DAG 任务拆解 → Sub Issues 创建 +description: DAG 任务拆解 → Task Record(task_control) --- # flow-tasks -将设计方案拆解为 DAG(有向无环图)任务,创建 GitHub Sub Issues。 +将设计方案拆解为 DAG(有向无环图)任务,并用 `task_control{op:"create-task"}` 创建 Task Record(GitHub Sub Issue)。 -## Prerequisites -- `/design` 已完成 → `docs/dev/specs/<title>.md` 存在 -- Parent GitHub Issue 存在 +## 核心:调用 task_control -## Workflow +`task_control{op:"create-task", title:"<英文功能标题>", acceptance_criteria:"<JSON>", ...}` 创建 Sub Issue。 +内核负责 slug 派生与校验、标题规范(R3)、关联 Parent Issue——创建动作全部收敛到工具内。 -### 1. DAG 拆解 +## DAG 拆解 #### 拆解前自检 -在动手拆解前,先回答三个问题: - 1. **能否用一个任务完成?** — 如果功能足够内聚,不强拆多个任务 -2. **拆开后能否独立验证?** — 每个任务必须有独立的验收标准,不能依赖其他任务才能测试 +2. **拆开后能否独立验证?** — 每个任务必须有独立的验收标准 3. **拆开后耦合是否最低?** — 两个任务共享大量数据结构/模块 → 合并 -#### 拆解检查清单 - -- [ ] 每个任务满足 SRP:只有一个修改的理由 -- [ ] 每个任务有独立的验收标准(可单独测试) -- [ ] 依赖边最少化(DAG 的边越少越好) -- [ ] 无循环依赖 -- [ ] 无"万能任务"(一个任务做太多不相关的事) -- [ ] 无"微任务"(一个任务只改 1-2 行) - #### 反模式 | 反模式 | 示例 | 正确做法 | |--------|------|----------| -| 技术分层拆分 | "建表任务" → "DAO 任务" → "Service 任务" | 垂直切片:一个任务包含从表到 API 的完整链路 | -| 过度拆分 | 一个 CRUD 拆成 Create/Read/Update/Delete 四个任务 | 一个 CRUD 就是一个任务 | -| 预留式拆分 | "先搭框架,后面任务再填内容" | 不要有空壳任务。每个任务都是完整功能单元 | - -无依赖的任务标记为可并行。任务粒度以"一个人可独立完成"为标准。 - -DAG 使用 Mermaid 语法绘制,示例: +| 技术分层拆分 | "建表任务" → "DAO 任务" → "Service 任务" | 垂直切片:一个任务包含完整链路 | +| 过度拆分 | 一个 CRUD 拆成四个任务 | 一个 CRUD 就是一个任务 | +| 预留式拆分 | "先搭框架,后面任务再填内容" | 不要有空壳任务 | -```mermaid -graph TD - A["Task A(无依赖)"] --> B["Task B(依赖 A)"] - A --> C["Task C(依赖 A)"] - D["Task D(无依赖)"] -``` +DAG 使用 Mermaid 语法绘制,保存为 `docs/dev/tasks/<YYYY-MM-DD-NNN-slug>/DAG.md`。 -保存为 `docs/dev/tasks/<YYYY-MM-DD-NNN-slug>/DAG.md`。 +## 任务定义文件 -### 2. 创建任务文件 -每个任务 `docs/dev/tasks/<task-name>.md`: - -```markdown ---- -name: "<task-name>" -depends_on: ["<前置任务>"] -labels: ["developer"] -worktree_root: ".worktree/<task-name>/" -test_commands: - - "npm test -- <test-file>" - - "npm run typecheck" -verify_commands: - - "npm run lint" -tdd: - mode: strict - min_cycles: 1 -acceptance: - - criteria: "<验收标准描述>" - verification_type: test | manual | lint - test_command: "<关联的测试命令>" - - criteria: "<另一条验收标准>" - verification_type: test - test_command: "npm test -- <other-test>" ---- -``` - -Task frontmatter 新增字段说明: - -| 字段 | 类型 | 说明 | -|------|------|------| -| `test_commands` | `string[]` | TDD cycle 中运行的测试命令列表 | -| `verify_commands` | `string[]` | final-verification 阶段运行的验证命令 | -| `tdd.mode` | `"strict" \| "advisory"` | TDD 模式,默认 `strict` | -| `tdd.min_cycles` | `number` | 最少 RED→GREEN cycle 数量,默认 `1` | -| `acceptance` | `object[]` | 结构化验收标准列表 | -| `acceptance[].criteria` | `string` | 验收标准描述 | -| `acceptance[].verification_type` | `"test" \| "manual" \| "lint"` | 验证方式 | -| `acceptance[].test_command` | `string` | 关联的测试命令(verification_type 为 test 时必填) | - -`tdd` 配置块为 `flow-tdd` skill 提供运行时参数。模式默认 `strict`, -表示 Agent 必须遵循 RED→GREEN→final-regression→final-verification 完整流程。 - -## 目标 - -## 实现要点 - -## 验收标准 - -验收标准以结构化 `acceptance` 数组形式定义在 frontmatter 中(见上方模板)。 -每条标准包含 `criteria`(描述)、`verification_type`(验证方式)和 `test_command`(关联命令)。 -`flow-tdd` skill 在 final-verification 阶段逐条核验这些标准。 - -## TDD 集成 - -本 skill 生成的 `test_commands`、`verify_commands`、`tdd` 配置块和 `acceptance_criteria` -是 `flow-tdd` skill 的输入。`flow-tdd` 根据这些字段执行 RED→GREEN cycle、 -final-regression 和 final-verification。 - -## Worktree -- 路径: `.worktree/<task-name>/` -- 分支: `feat/<task-name>` -- 创建时机: `/code` 阶段首次执行时自动创建 -- 清理时机: PR 合并后自动删除 -``` - -### 3. 创建 Sub Issues - -```bash -mkdir -p docs/dev/handoff -cat > docs/dev/handoff/task-issue-body.md << 'EOF' -## 依赖 -前置任务: <列表> - -## Worktree -- 路径: `.worktree/<task-name>/` -- 分支: `feat/<task-name>` - -## 描述 -... -EOF -gh issue create \ - --title "<task-name>" \ - --label "task" \ - --parent <parent-number> \ - --body-file docs/dev/handoff/task-issue-body.md -``` - -### 4. 提交任务文件 -任务定义文件通过 Planning PR 合入默认分支: - -```bash -# 探测默认分支 -BASE=$(gh repo view --json defaultBranchRef --jq '.defaultBranchRef.name' 2>/dev/null || echo "main") - -git checkout -b chore/plan-tasks-<slug> $BASE -git add docs/dev/tasks/<YYYY-MM-DD-NNN-slug>/ -git commit -m "docs: <title> — 任务定义" -git push origin chore/plan-tasks-<slug> -gh pr create --title "docs: <title> — 任务定义" --base $BASE -``` +每个任务一个文件 `docs/dev/tasks/<task-name>.md`,frontmatter 含 `test_commands` / `verify_commands` / `tdd` 配置块 / `acceptance` 结构化验收标准。 +这些字段是 `flow-tdd` skill 的输入(RED→GREEN cycle、final-regression、final-verification)。 ## Output - `docs/dev/tasks/*.md` — 独立任务文件 -- GitHub Sub Issues 已创建(依赖关系在 body 中声明) -- 任务文件通过 Planning PR 合入默认分支 - -## Sub Issue 关闭时机 -Sub Issue 不在 `/tasks` 阶段关闭,而是在对应 PR 合并后自动关闭: - -| 阶段 | 动作 | -|------|------| -| `/code` | PR body 含 `Closes #<issue-num>`,合并后 GitHub 自动关闭 | -| `/review` | 如 PR 未自动关闭 Sub Issue,reviewer 手动 `gh issue close <num>` | +- GitHub Sub Issues(由 task_control 创建,依赖关系在 body 中声明) ## 后续 - **/code** — 认领 Sub Issue 开始编码 @@ -178,20 +49,18 @@ Sub Issue 不在 `/tasks` 阶段关闭,而是在对应 PR 合并后自动关 ### Inputs - `docs/dev/specs/<title>.md` — 技术方案 -- Parent Issue 编号 +- Flow Record 编号 ### Preconditions -- `/design` 已完成 → 技术方案和 ADR 存在 +- `/design` 已完成 → 技术方案和 ADR 存在(Planning Baseline 已合入) ### Procedure -1. 基于技术方案拆解 DAG -2. 创建 Task Manifest (`manifest.yaml`) 和任务文件 -3. 为每个任务创建 GitHub Sub Issue -4. 通过 Planning PR 提交任务文件 +1. 基于技术方案拆解 DAG(Mermaid 图 + 任务文件) +2. 为每个任务调用 `task_control{op:"create-task"}` 创建 Sub Issue +3. 任务文件随 Planning PR 合入默认分支 ### Outputs -- `docs/dev/tasks/<feature-slug>/manifest.yaml` — 任务清单 -- `docs/dev/tasks/<feature-slug>/*.md` — 任务文件 +- `docs/dev/tasks/<feature-slug>/` — DAG 与任务文件 - GitHub Sub Issues(含依赖声明) ### Failure @@ -200,8 +69,9 @@ Sub Issue 不在 `/tasks` 阶段关闭,而是在对应 PR 合并后自动关 ### Idempotency - 任务文件已存在 → 读取并更新 -- Sub Issue 已创建 → 更新而非重复创建 +- Sub Issue 已创建 → 内核检测同名冲突(未合并分支/worktree)并拒绝 ### Prohibited Actions - 不跳过 DAG 依赖检查 +- 不绕过 task_control 手工创建 Sub Issue(由 create-task 完成) - 不直接 push 到默认分支 diff --git a/assets/skills/flow-tdd/SKILL.md b/assets/skills/flow-tdd/SKILL.md index a5c244c..3c259a9 100644 --- a/assets/skills/flow-tdd/SKILL.md +++ b/assets/skills/flow-tdd/SKILL.md @@ -1,6 +1,6 @@ --- name: flow-tdd -description: TDD Prompt 协议 — Phase A Advisory 层,定义 RED→GREEN self-reported 流程 +description: TDD Prompt 协议 — RED→GREEN 状态机 + tdd_checkpoint Runtime 证据 --- # flow-tdd @@ -10,7 +10,7 @@ TDD(Test-Driven Development)Prompt 协议,为所有编码阶段提供统 ## Advisory Procedure -Phase A Advisory 层协议:Agent 自行遵循 TDD 流程并 self-report 状态,无需工具拦截。 +Agent 自行遵循 TDD 流程并 self-report 状态。每个 stage 同时通过 `tdd_checkpoint` 提交证据。 ### Cycle 状态机 @@ -64,7 +64,7 @@ Phase A Advisory 层协议:Agent 自行遵循 TDD 流程并 self-report 状态 **约束:** - 测试必须有明确的 fail/pass 边界 -- 测试运行命令必须与 `test_commands` 中定义的一致 +- 测试运行命令必须与 Task 的 `test_commands` 中定义的一致 - 记录测试失败输出作为 RED evidence **self-report 格式:** @@ -119,7 +119,7 @@ Phase A Advisory 层协议:Agent 自行遵循 TDD 流程并 self-report 状态 所有 cycle 完成后,运行项目全部测试套件,确认无回归。 **约束:** -- 必须运行 `test_commands` 中定义的全部命令 +- 必须运行 Task `test_commands` 中定义的全部命令 - 所有测试必须通过 - 如有失败 → 修复(不要求新 cycle,但需记录修复内容) @@ -145,21 +145,19 @@ Phase A Advisory 层协议:Agent 自行遵循 TDD 流程并 self-report 状态 ## Runtime Procedure -> **Phase C 启用** — 以下为 Runtime Enforcement 协议占位,当前阶段不生效。 -> -> Phase C 将引入 `tdd_checkpoint` 工具在运行时拦截 RED/GREEN 状态切换, -> 并将 evidence 写入 FlowRun 存储。Agent 不直接调用这些工具的时机和方式 -> 由 Phase C 的 `tdd_checkpoint` 工具实现决定。 - -<!-- -Phase C 启用后的 Runtime Procedure: -1. cycle-start → tdd_checkpoint({ stage: "cycle-start", task_id }) -2. red → tdd_checkpoint({ stage: "red", evidence: test_output, task_id }) -3. green → tdd_checkpoint({ stage: "green", evidence: test_output, task_id }) -4. abandon-cycle → tdd_checkpoint({ stage: "abandon-cycle", reason, task_id }) -5. final-regression → tdd_checkpoint({ stage: "final-regression", evidence: full_test_output, task_id }) -6. final-verification → tdd_checkpoint({ stage: "final-verification", evidence: criteria_checklist, task_id }) ---> +每个 stage 通过 `tdd_checkpoint` 提交证据到 Task Record 单个受控评论(marker 包裹,唯一证据源)。 +工具亲自执行测试,不接受内联 evidence;RED 有效性校验(失败分类 + 实现文件相对基线未变 + 输入未偷换)。 + +| Stage | tdd_checkpoint op | +|-------|-------------------| +| cycle-start | `tdd_checkpoint{op:"cycle-start", task_id, criterion_id, test_paths, test_selector}` | +| red | `tdd_checkpoint{op:"red", task_id, cycle_id, test_selector}` — 测试失败 + 实现文件未变 | +| green | `tdd_checkpoint{op:"green", task_id, cycle_id, test_selector}` — 同 selector 通过 | +| abandon-cycle | `tdd_checkpoint{op:"abandon-cycle", task_id, cycle_id, reason}` | +| final-regression | `tdd_checkpoint{op:"final-regression", task_id}` — 全量测试通过 | +| final-verification | `tdd_checkpoint{op:"final-verification", task_id}` — 每条 criterion 有 pass cycle | + +纯文档变更豁免:`tdd_checkpoint{op:"not-applicable"}`(仅 primary);其他豁免须用户批准(`exempt-request`)。 ## Contract @@ -167,27 +165,27 @@ Phase C 启用后的 Runtime Procedure: 由编码阶段(`flow-code`)自动触发。Agent 在开始编码任务时加载 `flow-tdd` skill 获取 TDD 流程约束。 ### Inputs -- Task 定义中的 `acceptance_criteria`(来源:`flow-tasks` 产出) -- Task 定义中的 `test_commands`(来源:`flow-tasks` 产出) -- Task 定义中的 `verify_commands`(来源:`flow-tasks` 产出) -- Task 定义中的 `tdd` 配置块 — `mode`(`strict`/`advisory`)、`min_cycles`(来源:`flow-tasks` 产出) +- Task Record 中的 `acceptance_criteria`(来源:`flow-tasks` 产出) +- Task Record 中的 `test_commands`(来源:`flow-tasks` 产出) +- Task Record 中的 `verify_commands`(来源:`flow-tasks` 产出) +- Task Record 中的 `tdd` 配置块 — `mode`(`strict`/`advisory`)、`min_cycles`(来源:`flow-tasks` 产出) ### Preconditions -- Task 文件存在,包含 `acceptance_criteria`、`test_commands`、`tdd` 配置块 -- 测试运行环境就绪(`npm install` 已完成) +- Task Record 存在,包含 `acceptance_criteria`、`test_commands`、`tdd` 配置块 +- 测试运行环境就绪(worktree 内依赖已安装) ### Procedure 1. 读取 Task 的 `acceptance_criteria`、`test_commands`、`verify_commands` -2. **Advisory Mode**:为每个验收标准识别对应的测试用例 -3. 执行 `cycle-start` → 声明当前 cycle 目标 -4. 执行 `red` → 编写测试,验证失败 -5. 执行 `green` → 最小实现,验证通过 +2. 为每个验收标准识别对应的测试用例 +3. 执行 `cycle-start` → 声明当前 cycle 目标并调用 tdd_checkpoint +4. 执行 `red` → 编写测试,验证失败,提交 evidence +5. 执行 `green` → 最小实现,验证通过,提交 evidence 6. 重复 cycle 直到所有 criterion 覆盖 7. 执行 `final-regression` → 运行全部测试 8. 执行 `final-verification` → 逐条对照 acceptance_criteria ### Outputs -- 每个 cycle 的 self-report(提交到 commit message 或 PR body) +- 每个 cycle 的 self-report + tdd_checkpoint evidence(Task Record 评论) - `final-regression` 报告(测试通过/失败统计) - `final-verification` 报告(criterion 覆盖情况) @@ -205,4 +203,5 @@ Phase C 启用后的 Runtime Procedure: - 不跳过 RED 阶段直接进入 GREEN - 不跳过 final-regression 直接 commit - 不在 abandon-cycle 后保留修改 -- 不修改 `tdd` 配置块中的 `mode` 和 `min_cycles` 值 +- 不修改 Task `tdd` 配置块中的 `mode` 和 `min_cycles` 值 +- 不伪造/内联 tdd_checkpoint evidence(工具亲自执行测试) diff --git a/assets/skills/flow-test/SKILL.md b/assets/skills/flow-test/SKILL.md deleted file mode 100644 index 6b8f6ef..0000000 --- a/assets/skills/flow-test/SKILL.md +++ /dev/null @@ -1,75 +0,0 @@ ---- -name: flow-test -description: 触发 CI → 监控 → 测试结果汇报 ---- - -# flow-test - -触发 CI 运行 E2E 测试,监控结果并汇报。 - -## Prerequisites -- PR 已创建 - -## Workflow - -### 1. 确认 CI 配置 -检查 `.github/workflows/`。如缺失,引导用户创建: - -```yaml -name: E2E -on: [pull_request] -jobs: - test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - run: npm ci - - run: npm run test:e2e -``` - -### 2. 触发并监控 -```bash -gh pr checks <pr-number> --watch -``` - -### 3. 结果汇报 -- **全部通过** ✅ → 继续 -- **失败** ❌ → 分析原因,建议修复 -- **超时/异常** ⚠️ → 检查 CI 配置 - -## Output -- CI 结果汇报给用户 - -## 后续 -- **/review** — 审查 PR - -## Contract - -### Trigger -由 `/test` 命令触发。在已创建的 PR 上运行 CI 测试。 - -### Inputs -- PR 编号(来源:`/code` 产出) - -### Preconditions -- `/code` 已完成 → PR 已创建 - -### Procedure -1. 检查 CI 配置是否存在 -2. 触发 CI 运行 -3. 监控运行状态 -4. 汇报结果 - -### Outputs -- CI 运行结果摘要 - -### Failure -- CI 失败 → 分析日志并建议修复 -- 无 CI 配置 → 引导用户创建 .github/workflows/ci.yml - -### Idempotency -- 重复触发同一 PR → 覆盖前一次运行 - -### Prohibited Actions -- 不修改 CI 配置 -- 不触发非当前 PR 的 CI diff --git a/src/plugin/prompt-lint.ts b/src/plugin/prompt-lint.ts index fc89d22..0907de9 100644 --- a/src/plugin/prompt-lint.ts +++ b/src/plugin/prompt-lint.ts @@ -1,4 +1,4 @@ -import { globSync } from "node:fs" +import { globSync, readdirSync } from "node:fs" import { readFileSync, existsSync } from "node:fs" import path from "node:path" import { parse as parseYaml } from "yaml" @@ -24,6 +24,18 @@ const FORBIDDEN_PATTERNS: Array<{ pattern: RegExp; rule: string; message: string { pattern: /git worktree remove.*--force/, rule: "no-default-force-cleanup", message: "contains default --force worktree cleanup" }, ] +// R12 skill/command 收敛(§6.2/§6.4):删除 flow-handoff / flow-test skill 与 /test /handoff 命令 +const REMOVED_SKILLS = ["flow-handoff", "flow-test"] +const REMOVED_COMMANDS = ["test", "handoff"] + +/** 残留引用:skill 名 / 命令引用(反引号、加粗、列表项) */ +const HANDOFF_TEST_RESIDUE_PATTERNS: Array<{ pattern: RegExp; rule: string; message: string }> = [ + { pattern: /\bflow-handoff\b|\bflow-test\b/, rule: "handoff-test-residue", message: "references removed skill flow-handoff / flow-test" }, + { pattern: /`\/(test|handoff)`/, rule: "handoff-test-residue", message: "references removed command /test / /handoff" }, + { pattern: /\*\*\/test\*\*|\*\*\/handoff\*\*/, rule: "handoff-test-residue", message: "references removed command /test / /handoff" }, + { pattern: /^\s*[-*]\s*\/(test|handoff)\b/m, rule: "handoff-test-residue", message: "references removed command /test / /handoff" }, +] + function findMdFiles(root: string, dirs: string[]): string[] { const results: string[] = [] for (const dir of dirs) { @@ -91,6 +103,54 @@ function checkForbiddenPatterns(content: string, filePath: string): LintFinding[ return findings } +/** R12:content 中残留 flow-handoff/flow-test skill 或 /test /handoff 命令引用 */ +function checkHandoffTestResidue(content: string, filePath: string): LintFinding[] { + const findings: LintFinding[] = [] + for (const { pattern, rule, message } of HANDOFF_TEST_RESIDUE_PATTERNS) { + if (pattern.test(content)) { + findings.push({ severity: "error", file: filePath, rule, message }) + break + } + } + return findings +} + +/** R12:assets/skills 下不得存在已删除的 flow-handoff / flow-test */ +function checkRemovedSkills(root: string): LintFinding[] { + const skillsDir = path.join(root, "assets", "skills") + if (!existsSync(skillsDir)) return [] + const findings: LintFinding[] = [] + for (const name of readdirSync(skillsDir)) { + if (REMOVED_SKILLS.includes(name)) { + findings.push({ + severity: "error", + file: path.join(skillsDir, name), + rule: "skill-removed", + message: `removed skill directory still exists: ${name}`, + }) + } + } + return findings +} + +/** R12:assets/commands 下不得存在已删除的 test.md / handoff.md */ +function checkRemovedCommands(root: string): LintFinding[] { + const commandsDir = path.join(root, "assets", "commands") + if (!existsSync(commandsDir)) return [] + const findings: LintFinding[] = [] + for (const name of readdirSync(commandsDir)) { + if (REMOVED_COMMANDS.includes(name.replace(/\.md$/, ""))) { + findings.push({ + severity: "error", + file: path.join(commandsDir, name), + rule: "command-removed", + message: `removed command file still exists: ${name}`, + }) + } + } + return findings +} + function parseAgentName(frontmatter: string): string | undefined { try { const parsed = parseYaml(frontmatter) as Record<string, unknown> @@ -302,6 +362,10 @@ export function lintAll(projectRoot: string): { findings: LintFinding[]; passed: const files = findMdFiles(projectRoot, assetDirs) const allFindings: LintFinding[] = [] + // R12:目录级收敛校验(flow-handoff/flow-test skill、/test /handoff 命令不得存在) + allFindings.push(...checkRemovedSkills(projectRoot)) + allFindings.push(...checkRemovedCommands(projectRoot)) + for (const file of files) { const content = readFileSync(file, "utf8") @@ -309,6 +373,7 @@ export function lintAll(projectRoot: string): { findings: LintFinding[]; passed: allFindings.push(...checkContractCompleteness(content, file)) } allFindings.push(...checkForbiddenPatterns(content, file)) + allFindings.push(...checkHandoffTestResidue(content, file)) allFindings.push(...checkRelativeRefs(content, file)) if (file.includes("agents/")) { diff --git a/test/commands.test.ts b/test/commands.test.ts index 17abb3a..ff42b6f 100644 --- a/test/commands.test.ts +++ b/test/commands.test.ts @@ -4,6 +4,9 @@ import path from "node:path" import os from "node:os" import { loadCommands } from "../src/plugin/commands.js" +const PROJECT_ROOT = path.resolve(import.meta.dirname || __dirname, "..") +const ASSETS_COMMANDS_DIR = path.join(PROJECT_ROOT, "assets", "commands") + let tmpDir: string let skillsDir: string @@ -111,3 +114,26 @@ describe("loadCommands", () => { } }) }) + +describe("command convergence (R12, §6.4)", () => { + const EXPECTED_COMMANDS = ["setup", "requirements", "design", "tasks", "code", "review", "release"] + + it("exactly 7 commands exist, no test / handoff", () => { + const files = fs.readdirSync(ASSETS_COMMANDS_DIR) + .filter(f => f.endsWith(".md")) + .map(f => f.replace(/\.md$/, "")) + expect(files.sort()).toEqual([...EXPECTED_COMMANDS].sort()) + }) + + it("each command references its skill, no handoff/test residue", () => { + for (const name of EXPECTED_COMMANDS) { + const content = fs.readFileSync(path.join(ASSETS_COMMANDS_DIR, `${name}.md`), "utf8") + // 命令引用对应 skill(不复制流程) + expect(content).toContain(`flow-${name}`) + expect(content).not.toContain("flow-handoff") + expect(content).not.toContain("flow-test") + expect(content).not.toContain("/handoff") + expect(content).not.toContain("/test") + } + }) +}) diff --git a/test/plugin/prompt-lint.test.ts b/test/plugin/prompt-lint.test.ts index 2696196..67de86c 100644 --- a/test/plugin/prompt-lint.test.ts +++ b/test/plugin/prompt-lint.test.ts @@ -114,3 +114,89 @@ worker prompt expect(permRules.length).toBe(0) }) }) + +describe("prompt-lint: skill/command convergence (R12)", () => { + const EXPECTED_SKILLS = [ + "flow-setup", "flow-requirements", "flow-design", "flow-tasks", + "flow-code", "flow-tdd", "flow-review", "flow-release", + ] + const EXPECTED_COMMANDS = ["setup", "requirements", "design", "tasks", "code", "review", "release"] + + function writeSkill(root: string, name: string, body: string) { + const dir = path.join(root, "assets", "skills", name) + fs.mkdirSync(dir, { recursive: true }) + fs.writeFileSync(path.join(dir, "SKILL.md"), `# ${name}\n\n${body}`, "utf8") + } + + function writeCommand(root: string, name: string, body: string) { + const dir = path.join(root, "assets", "commands") + fs.mkdirSync(dir, { recursive: true }) + fs.writeFileSync(path.join(dir, `${name}.md`), `---\ndescription: ${name}\n---\n\n${body}`, "utf8") + } + + function makeValidAssets(root: string) { + for (const s of EXPECTED_SKILLS) writeSkill(root, s, `## Contract\n### Trigger\n### Inputs\n### Preconditions\n### Procedure\n### Outputs\n### Failure\n### Idempotency\n### Prohibited Actions`) + for (const c of EXPECTED_COMMANDS) writeCommand(root, c, "body") + } + + it("passes a valid 8-skill / 7-command assets tree", () => { + const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "cabbage-lint-conv-")) + try { + makeValidAssets(tmp) + const { findings } = lintAll(tmp) + const conv = findings.filter(f => f.rule.startsWith("skill-") || f.rule.startsWith("command-")) + expect(conv.length).toBe(0) + } finally { + fs.rmSync(tmp, { recursive: true, force: true }) + } + }) + + it("flags removed skills flow-handoff / flow-test", () => { + const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "cabbage-lint-conv-")) + try { + makeValidAssets(tmp) + writeSkill(tmp, "flow-handoff", "## Contract\n### Trigger\n### Inputs\n### Preconditions\n### Procedure\n### Outputs\n### Failure\n### Idempotency\n### Prohibited Actions") + writeSkill(tmp, "flow-test", "## Contract\n### Trigger\n### Inputs\n### Preconditions\n### Procedure\n### Outputs\n### Failure\n### Idempotency\n### Prohibited Actions") + const { findings } = lintAll(tmp) + const removed = findings.filter(f => f.rule === "skill-removed") + expect(removed.length).toBeGreaterThanOrEqual(2) + } finally { + fs.rmSync(tmp, { recursive: true, force: true }) + } + }) + + it("flags removed commands /test and /handoff", () => { + const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "cabbage-lint-conv-")) + try { + makeValidAssets(tmp) + writeCommand(tmp, "test", "请加载 flow-test 技能") + writeCommand(tmp, "handoff", "请加载 flow-handoff 技能") + const { findings } = lintAll(tmp) + const removed = findings.filter(f => f.rule === "command-removed") + expect(removed.length).toBeGreaterThanOrEqual(2) + } finally { + fs.rmSync(tmp, { recursive: true, force: true }) + } + }) + + it("flags handoff/test residue inside skill/command/bootstrap content", () => { + const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "cabbage-lint-conv-")) + try { + makeValidAssets(tmp) + writeSkill(tmp, "flow-code", "加载 flow-handoff 打包进度;触发 /test 诊断") + const { findings } = lintAll(tmp) + const residue = findings.filter(f => f.rule === "handoff-test-residue") + expect(residue.length).toBeGreaterThanOrEqual(1) + } finally { + fs.rmSync(tmp, { recursive: true, force: true }) + } + }) + + it("current assets have no removed skills/commands and no handoff/test residue", () => { + const { findings } = lintAll(PROJECT_ROOT) + const violations = findings.filter(f => + f.rule === "skill-removed" || f.rule === "command-removed" || f.rule === "handoff-test-residue" + ) + expect(violations.length).toBe(0) + }) +}) diff --git a/test/skills.test.ts b/test/skills.test.ts index 287c34a..d23f464 100644 --- a/test/skills.test.ts +++ b/test/skills.test.ts @@ -135,11 +135,13 @@ describe("flow-tdd Advisory Skill", () => { expect(content!).toMatch(/Advisory Procedure/i) }) - it("flow-tdd SKILL.md contains Runtime Procedure placeholder", () => { + it("flow-tdd SKILL.md contains Runtime Procedure bound to tdd_checkpoint ops", () => { const content = readFlowTddSkill() expect(content).not.toBeNull() expect(content!).toMatch(/Runtime Procedure/i) - expect(content!).toContain("Phase C") + // 批 14:Runtime Procedure 从 Phase C 占位改为实际协议 — 每个 stage 对应 tdd_checkpoint op + expect(content!).toContain("tdd_checkpoint") + expect(content!).toContain("cycle-start") }) it("flow-tdd is loaded by setupSkillsDir", async () => { @@ -158,3 +160,54 @@ describe("flow-tdd Advisory Skill", () => { expect(loaded).toContain("cycle-start") }) }) + +describe("skills convergence (R12, §6.2)", () => { + const EXPECTED_SKILLS = [ + "flow-setup", "flow-requirements", "flow-design", "flow-tasks", + "flow-code", "flow-tdd", "flow-review", "flow-release", + ] + + // 每个 skill 指向的内核工具(§6.2 引用关系) + const SKILL_TOOL_REFS: Record<string, string[]> = { + "flow-setup": ["setup_control"], + "flow-requirements": ["flow_control"], + "flow-design": ["flow_control"], + "flow-tasks": ["task_control"], + "flow-code": ["task_control", "tdd_checkpoint"], + "flow-tdd": ["tdd_checkpoint"], + "flow-review": ["task_control"], + "flow-release": ["release_control"], + } + + function listSkillDirs(): string[] { + if (!fs.existsSync(ASSETS_SKILLS_DIR)) return [] + return fs.readdirSync(ASSETS_SKILLS_DIR, { withFileTypes: true }) + .filter(d => d.isDirectory()) + .map(d => d.name) + } + + it("exactly 8 skills exist, no flow-handoff / flow-test", () => { + const dirs = listSkillDirs() + expect(dirs.sort()).toEqual([...EXPECTED_SKILLS].sort()) + }) + + it("each skill references its kernel tool, does not copy tool implementation", () => { + for (const skill of EXPECTED_SKILLS) { + const skillPath = path.join(ASSETS_SKILLS_DIR, skill, "SKILL.md") + const content = fs.readFileSync(skillPath, "utf8") + for (const tool of SKILL_TOOL_REFS[skill]) { + expect(content).toContain(tool) + } + // 不复制内核工具实现:不出现 shell 层 gh/git 写命令 + expect(content).not.toMatch(/gh pr create/) + expect(content).not.toMatch(/gh issue create/) + expect(content).not.toMatch(/git push/) + expect(content).not.toMatch(/git worktree add/) + } + }) + + it("flow-code references flow-tdd as the single TDD source", () => { + const content = fs.readFileSync(path.join(ASSETS_SKILLS_DIR, "flow-code", "SKILL.md"), "utf8") + expect(content).toContain("flow-tdd") + }) +})