cowork-flow 0.0.27 → 0.0.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/README.md +51 -17
  2. package/package.json +1 -1
  3. package/src/commands/init.js +1 -1
  4. package/src/commands/update.js +2 -4
  5. package/src/lib/copy-template.js +52 -5
  6. package/src/lib/platforms.js +0 -3
  7. package/template/.agents/skills/before-dev/SKILL.md +1 -0
  8. package/template/.agents/skills/check/SKILL.md +1 -0
  9. package/template/.agents/skills/party-mode-v2/SKILL.md +2 -0
  10. package/template/.agents/skills/tdd/SKILL.md +61 -0
  11. package/template/.agents/skills/writing-plans/SKILL.md +1 -0
  12. package/template/.claude/agents/cowork-check.md +3 -3
  13. package/template/.claude/agents/cowork-implement.md +7 -13
  14. package/template/.claude/agents/cowork-research.md +3 -3
  15. package/template/.claude/hooks/inject-workflow-state.py +18 -18
  16. package/template/.claude/skills/before-dev/SKILL.md +1 -0
  17. package/template/.claude/skills/check/SKILL.md +1 -0
  18. package/template/.claude/skills/party-mode-v2/SKILL.md +2 -0
  19. package/template/.claude/skills/tdd/SKILL.md +61 -0
  20. package/template/.claude/skills/writing-plans/SKILL.md +1 -0
  21. package/template/.codex/agents/cowork-check.toml +2 -0
  22. package/template/.codex/agents/cowork-implement.toml +2 -0
  23. package/template/.codex/hooks/inject-workflow-state.py +19 -19
  24. package/template/.cowork-flow/.version +1 -1
  25. package/template/.cowork-flow/adapters/claude-code/adapter.yaml +0 -1
  26. package/template/.cowork-flow/adapters/codex/adapter.yaml +0 -1
  27. package/template/.cowork-flow/adapters/opencode/adapter.yaml +0 -1
  28. package/template/.cowork-flow/scripts/commands/__init__.py +1 -0
  29. package/template/.cowork-flow/scripts/commands/_bootstrap.py +10 -0
  30. package/template/.cowork-flow/scripts/{add_session.py → commands/add_session.py} +8 -10
  31. package/template/.cowork-flow/scripts/{change.py → commands/change.py} +6 -2
  32. package/template/.cowork-flow/scripts/{doctor.py → commands/doctor.py} +26 -75
  33. package/template/.cowork-flow/scripts/{get_context.py → commands/get_context.py} +5 -1
  34. package/template/.cowork-flow/scripts/{get_developer.py → commands/get_developer.py} +6 -2
  35. package/template/.cowork-flow/scripts/{init_developer.py → commands/init_developer.py} +6 -2
  36. package/template/.cowork-flow/scripts/commands/party_mode_v2.py +1166 -0
  37. package/template/.cowork-flow/scripts/{resume.py → commands/resume.py} +6 -2
  38. package/template/.cowork-flow/scripts/{subagent.py → commands/subagent.py} +7 -3
  39. package/template/.cowork-flow/scripts/{task.py → commands/task.py} +189 -60
  40. package/template/.cowork-flow/scripts/common/__init__.py +1 -1
  41. package/template/.cowork-flow/scripts/common/core/__init__.py +1 -0
  42. package/template/.cowork-flow/scripts/common/{developer.py → core/developer.py} +1 -1
  43. package/template/.cowork-flow/scripts/common/gates/__init__.py +1 -0
  44. package/template/.cowork-flow/scripts/common/gates/coding_standards.py +210 -0
  45. package/template/.cowork-flow/scripts/common/gates/gates.py +66 -0
  46. package/template/.cowork-flow/scripts/common/gates/tdd_evidence.py +365 -0
  47. package/template/.cowork-flow/scripts/common/gates/test_intent.py +266 -0
  48. package/template/.cowork-flow/scripts/common/{validate_coding_standards.py → gates/validate_coding_standards.py} +23 -21
  49. package/template/.cowork-flow/scripts/common/gates/validate_implementation.py +243 -0
  50. package/template/.cowork-flow/scripts/common/gates/validate_rules.py +465 -0
  51. package/template/.cowork-flow/scripts/common/git/__init__.py +1 -0
  52. package/template/.cowork-flow/scripts/common/{git_context.py → git/git_context.py} +3 -3
  53. package/template/.cowork-flow/scripts/common/git/git_snapshot.py +83 -0
  54. package/template/.cowork-flow/scripts/common/task/__init__.py +1 -0
  55. package/template/.cowork-flow/scripts/common/{active_task.py → task/active_task.py} +1 -1
  56. package/template/.cowork-flow/scripts/common/{readiness.py → task/readiness.py} +1 -1
  57. package/template/.cowork-flow/scripts/common/task/state_machine.py +57 -0
  58. package/template/.cowork-flow/scripts/common/{task_utils.py → task/task_utils.py} +2 -2
  59. package/template/.cowork-flow/scripts/run.py +32 -25
  60. package/template/.cowork-flow/spec/contracts/index.md +11 -0
  61. package/template/.cowork-flow/spec/{party-mode-v2-board.md → contracts/party-mode-v2-board.md} +25 -0
  62. package/template/.cowork-flow/spec/{workflow-state-templates.md → contracts/workflow-state-templates.md} +1 -1
  63. package/template/.cowork-flow/spec/index.md +15 -0
  64. package/template/.cowork-flow/spec/{registry.json → runtime/contract-registry.json} +4 -16
  65. package/template/.cowork-flow/spec/runtime/index.md +10 -0
  66. package/template/.cowork-flow/spec/{rules.json → runtime/rules.json} +28 -14
  67. package/template/.cowork-flow/spec/{adapter.schema.json → schemas/adapter.schema.json} +1 -5
  68. package/template/.cowork-flow/spec/schemas/index.md +10 -0
  69. package/template/.cowork-flow/spec/schemas/party-mode-v2-actions.schema.json +90 -0
  70. package/template/.cowork-flow/spec/{rules.schema.json → schemas/rules.schema.json} +17 -4
  71. package/template/.cowork-flow/workflow.md +13 -13
  72. package/template/.opencode/agents/cowork-check.md +3 -3
  73. package/template/.opencode/agents/cowork-implement.md +3 -3
  74. package/template/.opencode/agents/cowork-research.md +3 -3
  75. package/template/.opencode/commands/party-mode-v2.md +5 -0
  76. package/template/.opencode/plugins/cowork-flow.js +17 -15
  77. package/template/AGENTS.md +4 -3
  78. package/template/.cowork-flow/scripts/common/entry_classifier.py +0 -122
  79. package/template/.cowork-flow/scripts/common/errors.py +0 -107
  80. package/template/.cowork-flow/scripts/common/validate_implementation.py +0 -195
  81. package/template/.cowork-flow/scripts/common/validate_rules.py +0 -302
  82. package/template/.cowork-flow/scripts/party_mode_v2.py +0 -646
  83. package/template/.cowork-flow/scripts/project_context.py +0 -230
  84. package/template/.cowork-flow/spec/entry-contract.md +0 -45
  85. package/template/.cowork-flow/spec/party-mode-v2-actions.schema.json +0 -43
  86. /package/template/.cowork-flow/scripts/common/{config.py → core/config.py} +0 -0
  87. /package/template/.cowork-flow/scripts/common/{execution_context.py → core/execution_context.py} +0 -0
  88. /package/template/.cowork-flow/scripts/common/{files.py → core/files.py} +0 -0
  89. /package/template/.cowork-flow/scripts/common/{paths.py → core/paths.py} +0 -0
  90. /package/template/.cowork-flow/scripts/common/{archive_utils.py → task/archive_utils.py} +0 -0
  91. /package/template/.cowork-flow/spec/{capabilities.md → contracts/capabilities.md} +0 -0
  92. /package/template/.cowork-flow/spec/{subagent-dispatch.md → contracts/subagent-dispatch.md} +0 -0
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # cowork-flow
1
+ # cowork-flow
2
2
 
3
3
  cowork-flow 是一个用于新项目初始化协作流程的模板仓库。它把项目说明、任务流转、规格治理、开发计划和会话记录放在一套可复制的目录结构里,帮助团队在项目早期就建立清晰的工作闭环。
4
4
 
@@ -43,6 +43,12 @@ cowork-flow 是一个用于新项目初始化协作流程的模板仓库。它
43
43
  ├── workflow.md
44
44
  ├── scripts/
45
45
  ├── spec/
46
+ │ ├── contracts/
47
+ │ ├── runtime/
48
+ │ ├── schemas/
49
+ │ ├── backend/
50
+ │ ├── frontend/
51
+ │ └── guides/
46
52
  ├── changes/
47
53
  ├── plans/
48
54
  ├── tasks/
@@ -65,19 +71,19 @@ cowork-flow 是一个用于新项目初始化协作流程的模板仓库。它
65
71
  固定角色 agent 定义,包含 `cowork-research`、`cowork-implement` 和 `cowork-check`,以及对 Codex 默认 `worker`、`default`、`explorer` 的项目级防漂移约束。主会话负责计划与收口,并通过 host adapter 派发固定 agent;正式派发必须先创建 runtime context,再把 `cowork_runtime_context_id` 和 `cowork_host_context_key` 传给子代理。
66
72
 
67
73
  `template/.codex/config.toml`、`template/.codex/hooks.json`、`template/.codex/hooks/`
68
- Codex 项目级配置与每轮上下文注入入口。hook 会先用共享 entry classifier 判定当前输入,再读取当前 session task、`.cowork-flow/spec/workflow-state-templates.md` 中的 `workflow-state` 片段和 `codex.dispatch_mode`,把流程状态注入到当前轮对话。注入的 `codex-dispatch-mode` 只是主会话调度提示,不代表当前线程身份。
74
+ Codex 项目级配置与每轮上下文注入入口。hook 会先解析 runtime context,再读取当前 session task、`.cowork-flow/spec/contracts/workflow-state-templates.md` 中的 `workflow-state` 片段和 `codex.dispatch_mode`,把流程状态注入到当前轮对话。注入的 `codex-dispatch-mode` 只是主会话调度提示,不代表当前线程身份。
69
75
 
70
76
  `template/CLAUDE.md`、`template/.claude/settings.json`、`template/.claude/agents/`、`template/.claude/skills/`、`template/.claude/commands/`、`template/.claude/hooks/`
71
77
  Claude Code 项目级记忆、hook、固定 agent、skills 和 slash command。`CLAUDE.md` 显式导入 `AGENTS.md`。`.claude/settings.json` 注册 `UserPromptSubmit` 和 `SessionStart` hook,注入当前 workflow state 与 contract digest。固定 agent 通过 `.cowork-flow/run subagent init` 生成 runtime context,并在 prompt、env 或 metadata transport 中传递 `cowork_runtime_context_id`;hook 绑定成功后才进入叶子执行,绑定失败则 fail closed,避免被项目 bootstrap 或无任务首屏上下文拉偏。Claude Code 不自动加载 `.agents/skills/`,所以项目技能要放进 `.claude/skills/`。
72
78
 
73
79
  `template/.opencode/agents/`、`template/.opencode/commands/`、`template/.opencode/plugins/`
74
- OpenCode 项目级固定 agent、slash command 和系统上下文注入插件。插件会读取 `.cowork-flow/spec/registry.json`,注入短 contract digest。
80
+ OpenCode 项目级固定 agent、slash command 和系统上下文注入插件。插件会读取 `.cowork-flow/spec/runtime/contract-registry.json`,注入短 contract digest。
75
81
 
76
82
  `template/.cowork-flow/`
77
83
  工作流目录,包含流程说明、任务状态、开发者工作区、项目规范、行为变更规格、实现计划和辅助脚本。`.cowork-flow/config.yaml` 只承载真实接线的 session/journal, Codex hint, and task lifecycle hook settings。
78
84
 
79
85
  `template/.cowork-flow/spec/`
80
- 项目规范目录,预置了 backend、frontend 和 guides 三类说明。接入时应按项目事实保留、改写或删除对应规范。
86
+ 项目规范目录。`contracts/` 存人读 workflow/host/agent 合同,`runtime/` 存运行时读取的规则和 contract registry,`schemas/` 存 JSON Schema,`backend/`、`frontend/`、`guides/` 存工程规范和思考指引。接入时应按项目事实保留、改写或删除对应规范。
81
87
 
82
88
  `template/.cowork-flow/changes/`
83
89
  由 `change.py` 管理 proposal、design、behavior specs 和归档,不维护实现 checklist。
@@ -93,7 +99,7 @@ OpenCode 项目级固定 agent、slash command 和系统上下文注入插件。
93
99
  npx cowork-flow init ./my-project --platform codex
94
100
  ```
95
101
 
96
- `init` 会显式要求平台和开发者身份。平台可选 `codex`、`opencode`、`claude-code`、`all`,其中 `both` 仍作为兼容别名表示 `codex,opencode`。交互式终端会提示输入;脚本或 CI 请传入:
102
+ `init` 会显式要求平台和开发者身份。平台可选 `codex`、`opencode`、`claude-code`、`all`。交互式终端会提示输入;脚本或 CI 请传入:
97
103
 
98
104
  ```bash
99
105
  npx cowork-flow init ./my-project --platform codex --developer <your-name>
@@ -128,7 +134,7 @@ npx cowork-flow --help
128
134
  cowork-flow init ./my-project --platform codex
129
135
  ```
130
136
 
131
- `init` 会直接复制模板中的通用 `.cowork-flow/`,并按 `--platform` 只复制对应 host 资产:`codex` 复制 `.codex/`、`.agents/skills/` 与 `.cowork-flow/adapters/codex/`,`opencode` 复制 `.opencode/`、`.agents/skills/` 与 `.cowork-flow/adapters/opencode/`,`claude-code` 复制 `CLAUDE.md`、`.claude/`(含 `.claude/settings.json`、`.claude/hooks/`、`.claude/skills/`)与 `.cowork-flow/adapters/claude-code/`,不复制 `.agents/skills/`。`all` 或 `--platform codex,opencode,claude-code` 同时复制三者和 `.agents/skills/`。`both` 保持兼容,只复制 Codex 和 OpenCode。它还会初始化 `.cowork-flow/.developer` 与 `.cowork-flow/workspace/<developer>/`。交互式终端会先显示 checkbox 多选界面选择平台,再提示开发者名称;非交互式环境必须传入 `--platform <codex|opencode|claude-code|all>` 与 `--developer <name>`。`init` 不会复制额外技能包。
137
+ `init` 会直接复制模板中的通用 `.cowork-flow/`,并按 `--platform` 只复制对应 host 资产:`codex` 复制 `.codex/`、`.agents/skills/` 与 `.cowork-flow/adapters/codex/`,`opencode` 复制 `.opencode/`、`.agents/skills/` 与 `.cowork-flow/adapters/opencode/`,`claude-code` 复制 `CLAUDE.md`、`.claude/`(含 `.claude/settings.json`、`.claude/hooks/`、`.claude/skills/`)与 `.cowork-flow/adapters/claude-code/`,不复制 `.agents/skills/`。`all` 或 `--platform codex,opencode,claude-code` 同时复制三者和 `.agents/skills/`。它还会初始化 `.cowork-flow/.developer` 与 `.cowork-flow/workspace/<developer>/`。交互式终端会先显示 checkbox 多选界面选择平台,再提示开发者名称;非交互式环境必须传入 `--platform <codex|opencode|claude-code|all>` 与 `--developer <name>`。`init` 不会复制额外技能包。
132
138
 
133
139
  初始化到当前项目:
134
140
 
@@ -163,7 +169,7 @@ cowork-flow sync .
163
169
  cowork-flow sync . --dry-run
164
170
  ```
165
171
 
166
- `sync` 会自动识别目标项目已安装的 host 目录:只有 `.codex/` 时只同步 Codex 资产,只有 `.opencode/` 时只同步 OpenCode 资产,两者都有时同步两者;对应 `.cowork-flow/adapters/<host>/` 也按识别出的平台同步。检测到 Codex 或 OpenCode 时会刷新 `.agents/skills/`;Claude Code-only 项目只刷新 `.claude/settings.json`、`.claude/hooks/`、`.claude/skills/`,不会创建 `.agents/skills/`。通用部分默认刷新 `.cowork-flow/scripts/`、`.cowork-flow/spec/workflow-state-templates.md`,以及 `AGENTS.md` 和 `CLAUDE.md` 中的 `<!-- COWORK-FLOW:START --> ... <!-- COWORK-FLOW:END -->` 托管块,保留托管块之外的项目自定义内容。`.cowork-flow/config.yaml`、`.cowork-flow/workflow.md`、除 `workflow-state-templates.md` 外的 `.cowork-flow/spec/`、任务、计划、变更和 workspace 记录默认受保护。只有明确传入 `--force` 时才整文件覆盖保护文件。
172
+ `sync` 会自动识别目标项目已安装的 host 目录:只有 `.codex/` 时只同步 Codex 资产,只有 `.opencode/` 时只同步 OpenCode 资产,两者都有时同步两者;对应 `.cowork-flow/adapters/<host>/` 也按识别出的平台同步。检测到 Codex 或 OpenCode 时会刷新 `.agents/skills/`;Claude Code-only 项目只刷新 `.claude/settings.json`、`.claude/hooks/`、`.claude/skills/`,不会创建 `.agents/skills/`。通用部分默认刷新 `.cowork-flow/scripts/`、`.cowork-flow/spec/contracts/workflow-state-templates.md`,以及 `AGENTS.md` 和 `CLAUDE.md` 中的 `<!-- COWORK-FLOW:START --> ... <!-- COWORK-FLOW:END -->` 托管块,保留托管块之外的项目自定义内容。`.cowork-flow/config.yaml`、`.cowork-flow/workflow.md`、除 `workflow-state-templates.md` 外的 `.cowork-flow/spec/`、任务、计划、变更和 workspace 记录默认受保护。只有明确传入 `--force` 时才整文件覆盖保护文件。
167
173
 
168
174
  ## 常用入口
169
175
 
@@ -179,6 +185,16 @@ cowork-flow sync . --dry-run
179
185
  下面示例使用 macOS / Linux 写法;Windows 原生命令行中把
180
186
  `./.cowork-flow/run` 替换为 `.\.cowork-flow\run.cmd`。
181
187
 
188
+ 当前主流程以 `.cowork-flow/workflow.md` 为准。默认路径是:
189
+
190
+ ```text
191
+ changes -> brainstorming -> read spec -> plan -> tasks -> implement -> check -> complete
192
+ ```
193
+
194
+ L0 文档、格式、小范围重构或测试补充可简化为
195
+ `brainstorming -> read spec -> implement -> check -> complete`;L1/L2 在完成后继续
196
+ `archive -> add session`。
197
+
182
198
  初始化或查看开发者身份:
183
199
 
184
200
  ```bash
@@ -190,7 +206,6 @@ cowork-flow sync . --dry-run
190
206
 
191
207
  ```bash
192
208
  ./.cowork-flow/run get-context
193
- ./.cowork-flow/run project-context refresh
194
209
  ./.cowork-flow/run task list
195
210
  ./.cowork-flow/run task next
196
211
  ```
@@ -210,18 +225,35 @@ cowork-flow sync . --dry-run
210
225
  ./.cowork-flow/run task start <task-dir>
211
226
  ```
212
227
 
213
- `task next` is read-only. It reports the active task, status, blockers, and the
214
- next safe command before the main session starts work, dispatches fixed agents,
215
- checks, archives, or records the session.
216
-
217
- `project-context refresh` 会创建或刷新 `.cowork-flow/project-context.md`。生成块由
218
- 本地项目文件确定,`Manual Notes` 区保留人工补充;该文件是项目索引,不替代
219
- `AGENTS.md`、`.cowork-flow/workflow.md` 或 `.cowork-flow/spec/`。
228
+ `task next` 只读,不推进状态。它根据当前会话任务和 `task.json.status`
229
+ 报告当前任务、阻塞项和下一条安全命令;主会话在启动工作、派发固定代理、
230
+ 进入检查、归档或记录 session 前都应先看它。
220
231
 
221
232
  L2 任务的 readiness gate 会在 `task start` 前阻塞缺失的 proposal/spec/design、
222
233
  计划、任务链接、边界、假设、验收标准或 verification commands;同一 blocker
223
234
  会出现在 `task next` 输出中。
224
235
 
236
+ 任务状态流转:
237
+
238
+ ```mermaid
239
+ stateDiagram-v2
240
+ [*] --> NoTask
241
+ state "无当前任务" as NoTask
242
+ state "planning" as Planning
243
+ state "in_progress" as InProgress
244
+ state "review" as Review
245
+ state "completed" as Completed
246
+ state "已归档" as Archived
247
+
248
+ NoTask --> Planning: task create
249
+ Planning --> InProgress: task start
250
+ InProgress --> Review: task review
251
+ Review --> Completed: task complete
252
+ Completed --> Archived: task archive
253
+ Completed --> NoTask: task finish
254
+ Archived --> [*]
255
+ ```
256
+
225
257
  任务状态由阶段命令推进,`task next` 只读取状态:
226
258
 
227
259
  | 阶段 | 命令 | `task.json.status` |
@@ -230,11 +262,13 @@ L2 任务的 readiness gate 会在 `task start` 前阻塞缺失的 proposal/spec
230
262
  | 开始执行 | `task start <task-dir>` | `in_progress` |
231
263
  | 进入检查 | `task review [task-dir]` | `review` |
232
264
  | 检查完成 | `task complete [task-dir]` | `completed` |
265
+ | 归档任务 | `task archive <task-name>` | 归档副本保持 `completed` |
233
266
  | 清会话指针 | `task finish` | 不改变状态 |
234
267
 
235
268
  执行 plan 时使用固定 cowork agents:
236
269
 
237
- 主会话负责创建/启动任务、维护计划与收口验证;可并行的实现或检查工作通过 Codex subagent 交给固定角色 agent。
270
+ 主会话负责创建/启动任务、维护计划与收口验证;实现和检查默认通过当前 Host Adapter
271
+ 派发给固定角色 agent。固定 agent 是叶子执行者,不能再派发其他 agent,也不能替代主会话完成归档、记录 session 或提交。
238
272
 
239
273
  ```text
240
274
  ./.cowork-flow/run subagent init --role implement --agent-type cowork-implement --execution-task-dir <task-dir> --title "<title>"
@@ -249,7 +283,7 @@ cowork_host_context_key: <host_context_key>
249
283
 
250
284
  子代理首步执行 `./.cowork-flow/run subagent bind <runtime_context_id> <host_context_key>`;主会话验收前必须确认 runtime context 中 `status=bound` 且 `bound_context_key` 匹配。默认角色为 `cowork-research`、`cowork-implement`、`cowork-check`,任务上下文来自已绑定 runtime context,而不是 prompt 形状。
251
285
 
252
- Codex hook 启用后,每轮会自动注入 `<workflow-state>`,其中包含入口分类、当前任务、状态来源和下一步流程提示。状态文本来自 `.cowork-flow/spec/workflow-state-templates.md`;`workflow.md` 只描述流程,不内联状态片段。hook 不替代主会话验收;它只负责把 task 状态和 workflow gate 放进上下文。
286
+ Host hook/plugin 启用后,每轮会自动注入 workflow state,其中包含入口分类、当前任务、状态来源和下一步流程提示。状态文本来自 `.cowork-flow/spec/contracts/workflow-state-templates.md`;`workflow.md` 只描述流程,不内联状态片段。hook 不替代主会话验收;它只负责把 task 状态和 workflow gate 放进上下文。
253
287
 
254
288
  记录 session:
255
289
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cowork-flow",
3
- "version": "0.0.27",
3
+ "version": "0.0.29",
4
4
  "description": "CLI for installing and maintaining cowork-flow collaboration templates.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -195,7 +195,7 @@ function initialIndexContent(name) {
195
195
  - Sessions are appended to the journal file
196
196
  - A new journal file is created automatically after the current file exceeds 2000 lines
197
197
  - Use \`add_session.py\` to record sessions
198
- - New records use English text; legacy records can remain as they are
198
+ - New records use English text; existing records can remain as they are
199
199
  `;
200
200
  }
201
201
 
@@ -6,10 +6,8 @@ import {
6
6
  } from '../lib/package-info.js';
7
7
 
8
8
  function validateUpdateArgs(args) {
9
- for (const arg of args) {
10
- if (arg !== '--global' && arg !== '--yes') {
11
- throw new Error(`Unknown update option: ${arg}`);
12
- }
9
+ if (args.length > 0) {
10
+ throw new Error(`Unknown update option: ${args[0]}`);
13
11
  }
14
12
  }
15
13
 
@@ -1,5 +1,5 @@
1
- import { constants } from 'node:fs';
2
- import { access, chmod, copyFile, mkdir, readFile, readdir, stat, writeFile } from 'node:fs/promises';
1
+ import { constants } from 'node:fs';
2
+ import { access, chmod, copyFile, mkdir, readFile, readdir, rm, stat, writeFile } from 'node:fs/promises';
3
3
  import { dirname, join, relative } from 'node:path';
4
4
 
5
5
  import { templateRoot } from './paths.js';
@@ -96,7 +96,42 @@ const SAFE_SYNC_FILES = new Set([
96
96
  '.cowork-flow/.version',
97
97
  '.cowork-flow/run',
98
98
  '.cowork-flow/run.cmd',
99
- '.cowork-flow/spec/workflow-state-templates.md'
99
+ '.cowork-flow/spec/contracts/workflow-state-templates.md'
100
+ ]);
101
+
102
+ const OBSOLETE_SYNC_FILES = new Set([
103
+ '.cowork-flow/project-context.md',
104
+ '.cowork-flow/scripts/add_session.py',
105
+ '.cowork-flow/scripts/change.py',
106
+ '.cowork-flow/scripts/doctor.py',
107
+ '.cowork-flow/scripts/get_context.py',
108
+ '.cowork-flow/scripts/get_developer.py',
109
+ '.cowork-flow/scripts/init_developer.py',
110
+ '.cowork-flow/scripts/party_mode_v2.py',
111
+ '.cowork-flow/scripts/project_context.py',
112
+ '.cowork-flow/scripts/resume.py',
113
+ '.cowork-flow/scripts/subagent.py',
114
+ '.cowork-flow/scripts/task.py',
115
+ '.cowork-flow/scripts/common/active_task.py',
116
+ '.cowork-flow/scripts/common/archive_utils.py',
117
+ '.cowork-flow/scripts/common/coding_standards.py',
118
+ '.cowork-flow/scripts/common/config.py',
119
+ '.cowork-flow/scripts/common/developer.py',
120
+ '.cowork-flow/scripts/common/execution_context.py',
121
+ '.cowork-flow/scripts/common/files.py',
122
+ '.cowork-flow/scripts/common/gates.py',
123
+ '.cowork-flow/scripts/common/git_context.py',
124
+ '.cowork-flow/scripts/common/git_snapshot.py',
125
+ '.cowork-flow/scripts/common/paths.py',
126
+ '.cowork-flow/scripts/common/readiness.py',
127
+ '.cowork-flow/scripts/common/state_machine.py',
128
+ '.cowork-flow/scripts/common/task_utils.py',
129
+ '.cowork-flow/scripts/common/tdd_evidence.py',
130
+ '.cowork-flow/scripts/common/test_intent.py',
131
+ '.cowork-flow/scripts/common/validate_coding_standards.py',
132
+ '.cowork-flow/scripts/common/validate_implementation.py',
133
+ '.cowork-flow/scripts/common/validate_rules.py',
134
+ '.cowork-flow/spec/contracts/entry-contract.md'
100
135
  ]);
101
136
 
102
137
  const COWORK_FLOW_START = '<!-- COWORK-FLOW:START -->';
@@ -200,6 +235,13 @@ export async function buildSyncPlan(targetDir, options = {}) {
200
235
  }
201
236
  }
202
237
 
238
+ for (const file of OBSOLETE_SYNC_FILES) {
239
+ const destination = join(targetDir, file);
240
+ if (await pathExists(destination)) {
241
+ actions.push({ action: 'delete', source: null, destination, relativePath: file });
242
+ }
243
+ }
244
+
203
245
  actions.push({
204
246
  action: 'update',
205
247
  source: null,
@@ -226,7 +268,7 @@ export async function detectInstalledPlatforms(targetDir) {
226
268
  }
227
269
 
228
270
  export function summarizePlan(actions, dryRun = false) {
229
- const counts = { create: 0, update: 0, skip: 0, protected: 0 };
271
+ const counts = { create: 0, update: 0, skip: 0, protected: 0, delete: 0 };
230
272
  for (const action of actions) {
231
273
  counts[action.action] += 1;
232
274
  }
@@ -234,7 +276,8 @@ export function summarizePlan(actions, dryRun = false) {
234
276
  const prefix = dryRun ? 'dry-run ' : '';
235
277
  const createLabel = dryRun ? 'would-create' : 'created';
236
278
  const updateLabel = dryRun ? 'would-update' : 'updated';
237
- return `${prefix}${createLabel}=${counts.create} ${updateLabel}=${counts.update} skipped=${counts.skip} protected=${counts.protected}\n`;
279
+ const deleteLabel = dryRun ? 'would-delete' : 'deleted';
280
+ return `${prefix}${createLabel}=${counts.create} ${updateLabel}=${counts.update} ${deleteLabel}=${counts.delete} skipped=${counts.skip} protected=${counts.protected}\n`;
238
281
  }
239
282
 
240
283
  export async function applyPlan(actions, options = {}) {
@@ -246,6 +289,10 @@ export async function applyPlan(actions, options = {}) {
246
289
  if (item.action === 'skip' || item.action === 'protected') {
247
290
  continue;
248
291
  }
292
+ if (item.action === 'delete') {
293
+ await rm(item.destination, { force: true });
294
+ continue;
295
+ }
249
296
 
250
297
  await mkdir(dirname(item.destination), { recursive: true });
251
298
  if (item.content !== undefined) {
@@ -1,10 +1,7 @@
1
1
  export const SUPPORTED_PLATFORMS = ['codex', 'opencode', 'claude-code'];
2
2
 
3
- const LEGACY_BOTH_PLATFORMS = ['codex', 'opencode'];
4
-
5
3
  const PLATFORM_ALIASES = new Map([
6
4
  ['all', SUPPORTED_PLATFORMS],
7
- ['both', LEGACY_BOTH_PLATFORMS],
8
5
  ['codex', ['codex']],
9
6
  ['opencode', ['opencode']],
10
7
  ['claude', ['claude-code']],
@@ -15,6 +15,7 @@ Use this immediately before code edits.
15
15
  4. Active task `implement.jsonl`
16
16
  5. Relevant `.cowork-flow/spec/*/index.md`
17
17
  6. Any exact files named by the task, plan, or user
18
+ 7. If the task changes behavior, locate or create `tdd.jsonl` evidence before implementation.
18
19
 
19
20
  Do not bulk-load unrelated tasks, archived sessions, or every spec file.
20
21
 
@@ -16,6 +16,7 @@ Use this after implementation and before `finish-work`.
16
16
  5. Review test intent: reject shallow tests that do not fail for meaningful behavior breaks.
17
17
  6. Run focused tests that would fail if the changed behavior broke.
18
18
  7. Run broader validation when the change touches shared runtime, templates, packaging, or public workflow.
19
+ 8. Report `test_intent_review` with the key tests that defend PRD acceptance behavior.
19
20
 
20
21
  ## Report
21
22
 
@@ -26,6 +26,7 @@ Always use the runtime controller:
26
26
  .cowork-flow/run party-v2 post
27
27
  .cowork-flow/run party-v2 respond
28
28
  .cowork-flow/run party-v2 advance
29
+ .cowork-flow/run party-v2 record-action-result
29
30
  .cowork-flow/run party-v2 finalize
30
31
  ```
31
32
 
@@ -38,6 +39,7 @@ Do not bypass a runtime rejection by manually accepting child output. Runtime va
38
39
  - Historical board state is runtime-private and may be used only for audit or final reports.
39
40
  - Use at least three child agents unless the effective runtime config explicitly allows a different value.
40
41
  - The runtime emits host-neutral next actions. The active Host Adapter or moderator executes them for Codex, Claude Code, or OpenCode.
42
+ - Host action results must be recorded back through `record-action-result` so `agents.json`, audit logs, and action history can prove lifecycle state.
41
43
 
42
44
  ## Child Response Rules
43
45
 
@@ -0,0 +1,61 @@
1
+ ---
2
+ name: tdd
3
+ description: Use when implementing behavior changes, bug fixes, state transitions, protocols, CLI/runtime contracts, data format changes, or error handling that can be tested before implementation.
4
+ ---
5
+
6
+ # TDD
7
+
8
+ Use this before editing behavior-changing code.
9
+
10
+ ## Red-Green-Refactor
11
+
12
+ 1. Map the task PRD acceptance criteria to stable IDs such as `AC-001`.
13
+ 2. Write the smallest meaningful failing test first.
14
+ 3. Run the red command and confirm it fails because the target behavior is missing or wrong.
15
+ 4. Implement the smallest change that makes the behavior pass.
16
+ 5. Run the green command and relevant broader verification.
17
+ 6. Refactor only after the behavior is green.
18
+
19
+ Do not count shallow tests as TDD evidence. Tests that only import code, assert a function exists, assert `True`, count mocks without behavior, or mirror implementation details do not satisfy red-green-refactor.
20
+
21
+ ## Evidence
22
+
23
+ Record TDD proof in `<task>/tdd.jsonl`. Each evidence line is one JSON object:
24
+
25
+ ```json
26
+ {
27
+ "acceptanceId": "AC-001",
28
+ "testFile": "tests/test_example.py",
29
+ "testName": "test_behavior",
30
+ "redCommand": "python -m unittest tests.test_example.TestCase.test_behavior -v",
31
+ "redExitCode": 1,
32
+ "redOutputExcerpt": "expected failure excerpt",
33
+ "failureReason": "target behavior was not implemented",
34
+ "whyThisTestMatters": "explains which user-visible behavior would regress",
35
+ "greenCommand": "python -m unittest tests.test_example.TestCase.test_behavior -v",
36
+ "greenExitCode": 0,
37
+ "broaderVerification": "python -m unittest tests.test_example -v"
38
+ }
39
+ ```
40
+
41
+ Every evidence record must map to a PRD `acceptanceId`. The red failure must be about the target behavior, not syntax, import, environment, fixture, or setup failure.
42
+
43
+ `testName` must resolve to the exact behavior test in `testFile`. Use one of:
44
+ `test_method`, `ClassName.test_method`, or `module.ClassName.test_method`.
45
+ Do not point evidence at a class, module, or a name that only exists in a command string.
46
+
47
+ ## Exemption
48
+
49
+ Pure documentation, comment-only, or formatting-only tasks may use an exemption record instead of red/green evidence:
50
+
51
+ ```json
52
+ {
53
+ "type": "exemption",
54
+ "acceptanceId": "AC-001",
55
+ "exemptionType": "docs-only",
56
+ "reason": "Only documentation wording changed; runtime behavior is untouched.",
57
+ "verificationCommand": "git diff --check"
58
+ }
59
+ ```
60
+
61
+ Do not use an exemption for runtime, CLI, protocol, state, data format, permission, or error-handling changes.
@@ -42,6 +42,7 @@ Start with:
42
42
  - Include a failing test before implementation when behavior can be tested.
43
43
  - Do not add shallow tests just to satisfy process. Avoid tests that only assert existence, mirror implementation details, count mocks without behavior, or snapshot empty structure.
44
44
  - For complex problems, test depth first: cover invariants, cross-layer contracts, state transitions, error boundaries, and real regression paths before narrow unit cases.
45
+ - Map each behavior-changing test to a stable PRD acceptance ID and note where `tdd.jsonl` will record the red-green evidence.
45
46
  - Avoid placeholders such as TODO, TBD, "handle edge cases", or "write tests".
46
47
  - Keep root/template parity explicit when both copies exist.
47
48
 
@@ -24,9 +24,9 @@ state is injected. The first child step must still run:
24
24
  ```
25
25
 
26
26
  If the explicit bind fails, or if the bound context is missing, closed, invalid,
27
- or names another agent type, report `needs_context` and stop. Do not use
28
- `COWORK_ENTRY_CONTRACT_V1` to infer subagent identity; that contract classifies
29
- main-session prompts only.
27
+ or names another agent type, report `needs_context` and stop. Do not infer
28
+ subagent identity from prompt shape; runtime context binding is the only formal
29
+ signal.
30
30
 
31
31
  Rules:
32
32
 
@@ -1,16 +1,10 @@
1
1
  ---
2
2
  name: cowork-implement
3
3
  description: Cowork-flow implementation fixed subagent.
4
- mode: subagent
5
- permission:
6
- edit: ask
7
- bash: ask
8
- task: deny
9
- todowrite: deny
10
- external_directory: deny
4
+ tools: Read, Write, Edit, MultiEdit, Grep, Glob, LS, Bash
11
5
  ---
12
6
 
13
- You are the `cowork-implement` fixed subagent for OpenCode.
7
+ You are the `cowork-implement` fixed subagent for Claude Code.
14
8
  You are a leaf executor and must not invoke other agents.
15
9
 
16
10
  Formal `cowork-implement` work requires a bound runtime context. The prompt,
@@ -21,7 +15,7 @@ cowork_runtime_context_id: <runtime_context_id>
21
15
  cowork_host_context_key: <host_context_key>
22
16
  ```
23
17
 
24
- The plugin may bind that id to
18
+ The hook may bind that id to
25
19
  `.cowork-flow/.runtime/subagents/<runtime_context_id>.json` before workflow
26
20
  state is injected. The first child step must still run:
27
21
 
@@ -30,9 +24,9 @@ state is injected. The first child step must still run:
30
24
  ```
31
25
 
32
26
  If the explicit bind fails, or if the bound context is missing, closed, invalid,
33
- or names another agent type, report `needs_context` and stop. Do not use
34
- `COWORK_ENTRY_CONTRACT_V1` to infer subagent identity; that contract classifies
35
- main-session prompts only.
27
+ or names another agent type, report `needs_context` and stop. Do not infer
28
+ subagent identity from prompt shape; runtime context binding is the only formal
29
+ signal.
36
30
 
37
31
  ---
38
32
 
@@ -194,6 +188,6 @@ Rules:
194
188
  and each JSONL `file` entry.
195
189
  - Keep edits inside the assigned scope.
196
190
  - Report changed files and exact verification commands.
197
- - Do not use the `task` tool or invoke subagents.
191
+ - Do not use the Task tool or invoke subagents.
198
192
  - Do not run task start, task finish, task archive, unscoped resume, commit, or
199
193
  push.
@@ -24,9 +24,9 @@ state is injected. The first child step must still run:
24
24
  ```
25
25
 
26
26
  If the explicit bind fails, or if the bound context is missing, closed, invalid,
27
- or names another agent type, report `needs_context` and stop. Do not use
28
- `COWORK_ENTRY_CONTRACT_V1` to infer subagent identity; that contract classifies
29
- main-session prompts only.
27
+ or names another agent type, report `needs_context` and stop. Do not infer
28
+ subagent identity from prompt shape; runtime context binding is the only formal
29
+ signal.
30
30
 
31
31
  Rules:
32
32
 
@@ -17,18 +17,9 @@ TAG_RE = re.compile(
17
17
  )
18
18
  DEFAULT_CONTRACT_REGISTRY = {
19
19
  "contracts": [
20
- {
21
- "id": "COWORK_ENTRY_CONTRACT_V1",
22
- "path": ".cowork-flow/spec/entry-contract.md",
23
- "digest": [
24
- "Classify main-session requests before task start, resume, archive, or commit.",
25
- "Runtime context, not prompt labels, identifies formal subagent sessions.",
26
- ],
27
- "readWhen": ["before task start/resume/archive", "when prompt and bootstrap text conflict"],
28
- },
29
20
  {
30
21
  "id": "RUNTIME_CONTEXT_DISPATCH_V2",
31
- "path": ".cowork-flow/spec/subagent-dispatch.md",
22
+ "path": ".cowork-flow/spec/contracts/subagent-dispatch.md",
32
23
  "digest": [
33
24
  "Formal subagent work is keyed by cowork_runtime_context_id.",
34
25
  "Explicit shim bind records bound_context_key before formal output is accepted.",
@@ -88,7 +79,7 @@ def _normalize_hook_input(hook_input: dict[str, Any]) -> dict[str, Any]:
88
79
 
89
80
 
90
81
  def _load_breadcrumbs(root: Path) -> dict[str, str]:
91
- workflow = root / ".cowork-flow" / "spec" / "workflow-state-templates.md"
82
+ workflow = root / ".cowork-flow" / "spec" / "contracts" / "workflow-state-templates.md"
92
83
  try:
93
84
  text = workflow.read_text(encoding="utf-8")
94
85
  except OSError:
@@ -102,16 +93,23 @@ def _as_string_list(value: Any) -> list[str]:
102
93
  return [item for item in value if isinstance(item, str) and item.strip()]
103
94
 
104
95
 
105
- def _load_contract_registry(root: Path) -> list[dict[str, Any]]:
106
- registry_file = root / ".cowork-flow" / "spec" / "registry.json"
96
+ def _load_contract_registry(root: Path) -> tuple[list[dict[str, Any]], str | None]:
97
+ registry_file = root / ".cowork-flow" / "spec" / "runtime" / "contract-registry.json"
107
98
  try:
108
99
  data = json.loads(registry_file.read_text(encoding="utf-8"))
109
- except (OSError, json.JSONDecodeError):
100
+ except OSError:
110
101
  data = DEFAULT_CONTRACT_REGISTRY
102
+ warning = f"contract registry unavailable at {registry_file}; using fallback digest"
103
+ except json.JSONDecodeError:
104
+ data = DEFAULT_CONTRACT_REGISTRY
105
+ warning = f"contract registry invalid at {registry_file}; using fallback digest"
106
+ else:
107
+ warning = None
111
108
  contracts = data.get("contracts") if isinstance(data, dict) else None
112
109
  if not isinstance(contracts, list):
113
110
  contracts = DEFAULT_CONTRACT_REGISTRY["contracts"]
114
- return [contract for contract in contracts if isinstance(contract, dict)]
111
+ warning = warning or f"contract registry has no contracts array at {registry_file}; using fallback digest"
112
+ return [contract for contract in contracts if isinstance(contract, dict)], warning
115
113
 
116
114
 
117
115
  def _contract_fingerprint(root: Path, contracts: list[dict[str, Any]]) -> str:
@@ -129,13 +127,15 @@ def _contract_fingerprint(root: Path, contracts: list[dict[str, Any]]) -> str:
129
127
 
130
128
 
131
129
  def _build_contract_digest(root: Path) -> str:
132
- contracts = _load_contract_registry(root)
130
+ contracts, warning = _load_contract_registry(root)
133
131
  fingerprint = _contract_fingerprint(root, contracts)
134
132
  lines = [
135
133
  '<cowork-runtime host="claude-code" adapter="claude-code.hooks">',
136
134
  f'<contract-digest fingerprint="{fingerprint}">',
137
135
  "policy: repeat this short digest every hook; read full spec files only before listed actions.",
138
136
  ]
137
+ if warning:
138
+ lines.append(f"warning: {warning}")
139
139
  for contract in contracts:
140
140
  contract_id = contract.get("id")
141
141
  path = contract.get("path")
@@ -161,7 +161,7 @@ def _load_common(root: Path) -> None:
161
161
  def _get_active_task(root: Path, hook_input: dict[str, Any]) -> tuple[str | None, str, str]:
162
162
  _load_common(root)
163
163
  try:
164
- from common.active_task import get_active_task # type: ignore[import-not-found]
164
+ from common.task.active_task import get_active_task # type: ignore[import-not-found]
165
165
  except Exception:
166
166
  return None, "no_task", "unavailable"
167
167
 
@@ -185,7 +185,7 @@ def _get_active_task(root: Path, hook_input: dict[str, Any]) -> tuple[str | None
185
185
  def _resolve_runtime_context(root: Path, hook_input: dict[str, Any]) -> tuple[dict[str, Any] | None, str | None]:
186
186
  _load_common(root)
187
187
  try:
188
- from common.active_task import ( # type: ignore[import-not-found]
188
+ from common.task.active_task import ( # type: ignore[import-not-found]
189
189
  bind_runtime_context,
190
190
  read_runtime_context,
191
191
  resolve_runtime_context_id,
@@ -15,6 +15,7 @@ Use this immediately before code edits.
15
15
  4. Active task `implement.jsonl`
16
16
  5. Relevant `.cowork-flow/spec/*/index.md`
17
17
  6. Any exact files named by the task, plan, or user
18
+ 7. If the task changes behavior, locate or create `tdd.jsonl` evidence before implementation.
18
19
 
19
20
  Do not bulk-load unrelated tasks, archived sessions, or every spec file.
20
21
 
@@ -16,6 +16,7 @@ Use this after implementation and before `finish-work`.
16
16
  5. Review test intent: reject shallow tests that do not fail for meaningful behavior breaks.
17
17
  6. Run focused tests that would fail if the changed behavior broke.
18
18
  7. Run broader validation when the change touches shared runtime, templates, packaging, or public workflow.
19
+ 8. Report `test_intent_review` with the key tests that defend PRD acceptance behavior.
19
20
 
20
21
  ## Report
21
22
 
@@ -26,6 +26,7 @@ Always use the runtime controller:
26
26
  .cowork-flow/run party-v2 post
27
27
  .cowork-flow/run party-v2 respond
28
28
  .cowork-flow/run party-v2 advance
29
+ .cowork-flow/run party-v2 record-action-result
29
30
  .cowork-flow/run party-v2 finalize
30
31
  ```
31
32
 
@@ -38,6 +39,7 @@ Do not bypass a runtime rejection by manually accepting child output. Runtime va
38
39
  - Historical board state is runtime-private and may be used only for audit or final reports.
39
40
  - Use at least three child agents unless the effective runtime config explicitly allows a different value.
40
41
  - The runtime emits host-neutral next actions. The active Host Adapter or moderator executes them for Codex, Claude Code, or OpenCode.
42
+ - Host action results must be recorded back through `record-action-result` so `agents.json`, audit logs, and action history can prove lifecycle state.
41
43
 
42
44
  ## Child Response Rules
43
45