cowork-flow 0.0.27 → 0.0.28

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 (51) hide show
  1. package/README.md +12 -6
  2. package/package.json +1 -1
  3. package/src/lib/copy-template.js +2 -2
  4. package/template/.agents/skills/before-dev/SKILL.md +1 -0
  5. package/template/.agents/skills/check/SKILL.md +1 -0
  6. package/template/.agents/skills/party-mode-v2/SKILL.md +2 -0
  7. package/template/.agents/skills/tdd/SKILL.md +61 -0
  8. package/template/.agents/skills/writing-plans/SKILL.md +1 -0
  9. package/template/.claude/agents/cowork-implement.md +4 -10
  10. package/template/.claude/hooks/inject-workflow-state.py +17 -8
  11. package/template/.claude/skills/before-dev/SKILL.md +1 -0
  12. package/template/.claude/skills/check/SKILL.md +1 -0
  13. package/template/.claude/skills/party-mode-v2/SKILL.md +2 -0
  14. package/template/.claude/skills/tdd/SKILL.md +61 -0
  15. package/template/.claude/skills/writing-plans/SKILL.md +1 -0
  16. package/template/.codex/agents/cowork-check.toml +2 -0
  17. package/template/.codex/agents/cowork-implement.toml +2 -0
  18. package/template/.codex/hooks/inject-workflow-state.py +17 -8
  19. package/template/.cowork-flow/.version +1 -1
  20. package/template/.cowork-flow/scripts/common/coding_standards.py +209 -0
  21. package/template/.cowork-flow/scripts/common/gates.py +66 -0
  22. package/template/.cowork-flow/scripts/common/git_snapshot.py +83 -0
  23. package/template/.cowork-flow/scripts/common/state_machine.py +57 -0
  24. package/template/.cowork-flow/scripts/common/tdd_evidence.py +301 -0
  25. package/template/.cowork-flow/scripts/common/test_intent.py +265 -0
  26. package/template/.cowork-flow/scripts/common/validate_coding_standards.py +23 -21
  27. package/template/.cowork-flow/scripts/common/validate_implementation.py +99 -51
  28. package/template/.cowork-flow/scripts/common/validate_rules.py +252 -89
  29. package/template/.cowork-flow/scripts/doctor.py +18 -36
  30. package/template/.cowork-flow/scripts/party_mode_v2.py +770 -254
  31. package/template/.cowork-flow/scripts/task.py +161 -37
  32. package/template/.cowork-flow/spec/contracts/index.md +12 -0
  33. package/template/.cowork-flow/spec/{party-mode-v2-board.md → contracts/party-mode-v2-board.md} +25 -0
  34. package/template/.cowork-flow/spec/{workflow-state-templates.md → contracts/workflow-state-templates.md} +1 -1
  35. package/template/.cowork-flow/spec/index.md +15 -0
  36. package/template/.cowork-flow/spec/{registry.json → runtime/contract-registry.json} +5 -5
  37. package/template/.cowork-flow/spec/runtime/index.md +10 -0
  38. package/template/.cowork-flow/spec/{rules.json → runtime/rules.json} +28 -14
  39. package/template/.cowork-flow/spec/schemas/index.md +10 -0
  40. package/template/.cowork-flow/spec/schemas/party-mode-v2-actions.schema.json +90 -0
  41. package/template/.cowork-flow/spec/{rules.schema.json → schemas/rules.schema.json} +17 -4
  42. package/template/.cowork-flow/workflow.md +7 -7
  43. package/template/.opencode/commands/party-mode-v2.md +5 -0
  44. package/template/.opencode/plugins/cowork-flow.js +18 -7
  45. package/template/.cowork-flow/scripts/common/entry_classifier.py +0 -122
  46. package/template/.cowork-flow/scripts/common/errors.py +0 -107
  47. package/template/.cowork-flow/spec/party-mode-v2-actions.schema.json +0 -43
  48. /package/template/.cowork-flow/spec/{capabilities.md → contracts/capabilities.md} +0 -0
  49. /package/template/.cowork-flow/spec/{entry-contract.md → contracts/entry-contract.md} +0 -0
  50. /package/template/.cowork-flow/spec/{subagent-dispatch.md → contracts/subagent-dispatch.md} +0 -0
  51. /package/template/.cowork-flow/spec/{adapter.schema.json → schemas/adapter.schema.json} +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。
@@ -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
 
@@ -249,7 +255,7 @@ cowork_host_context_key: <host_context_key>
249
255
 
250
256
  子代理首步执行 `./.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
257
 
252
- Codex hook 启用后,每轮会自动注入 `<workflow-state>`,其中包含入口分类、当前任务、状态来源和下一步流程提示。状态文本来自 `.cowork-flow/spec/workflow-state-templates.md`;`workflow.md` 只描述流程,不内联状态片段。hook 不替代主会话验收;它只负责把 task 状态和 workflow gate 放进上下文。
258
+ Codex hook 启用后,每轮会自动注入 `<workflow-state>`,其中包含入口分类、当前任务、状态来源和下一步流程提示。状态文本来自 `.cowork-flow/spec/contracts/workflow-state-templates.md`;`workflow.md` 只描述流程,不内联状态片段。hook 不替代主会话验收;它只负责把 task 状态和 workflow gate 放进上下文。
253
259
 
254
260
  记录 session:
255
261
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cowork-flow",
3
- "version": "0.0.27",
3
+ "version": "0.0.28",
4
4
  "description": "CLI for installing and maintaining cowork-flow collaboration templates.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,4 +1,4 @@
1
- import { constants } from 'node:fs';
1
+ import { constants } from 'node:fs';
2
2
  import { access, chmod, copyFile, mkdir, readFile, readdir, stat, writeFile } from 'node:fs/promises';
3
3
  import { dirname, join, relative } from 'node:path';
4
4
 
@@ -96,7 +96,7 @@ 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
100
  ]);
101
101
 
102
102
  const COWORK_FLOW_START = '<!-- COWORK-FLOW:START -->';
@@ -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
 
@@ -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
 
@@ -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.
@@ -19,7 +19,7 @@ DEFAULT_CONTRACT_REGISTRY = {
19
19
  "contracts": [
20
20
  {
21
21
  "id": "COWORK_ENTRY_CONTRACT_V1",
22
- "path": ".cowork-flow/spec/entry-contract.md",
22
+ "path": ".cowork-flow/spec/contracts/entry-contract.md",
23
23
  "digest": [
24
24
  "Classify main-session requests before task start, resume, archive, or commit.",
25
25
  "Runtime context, not prompt labels, identifies formal subagent sessions.",
@@ -28,7 +28,7 @@ DEFAULT_CONTRACT_REGISTRY = {
28
28
  },
29
29
  {
30
30
  "id": "RUNTIME_CONTEXT_DISPATCH_V2",
31
- "path": ".cowork-flow/spec/subagent-dispatch.md",
31
+ "path": ".cowork-flow/spec/contracts/subagent-dispatch.md",
32
32
  "digest": [
33
33
  "Formal subagent work is keyed by cowork_runtime_context_id.",
34
34
  "Explicit shim bind records bound_context_key before formal output is accepted.",
@@ -88,7 +88,7 @@ def _normalize_hook_input(hook_input: dict[str, Any]) -> dict[str, Any]:
88
88
 
89
89
 
90
90
  def _load_breadcrumbs(root: Path) -> dict[str, str]:
91
- workflow = root / ".cowork-flow" / "spec" / "workflow-state-templates.md"
91
+ workflow = root / ".cowork-flow" / "spec" / "contracts" / "workflow-state-templates.md"
92
92
  try:
93
93
  text = workflow.read_text(encoding="utf-8")
94
94
  except OSError:
@@ -102,16 +102,23 @@ def _as_string_list(value: Any) -> list[str]:
102
102
  return [item for item in value if isinstance(item, str) and item.strip()]
103
103
 
104
104
 
105
- def _load_contract_registry(root: Path) -> list[dict[str, Any]]:
106
- registry_file = root / ".cowork-flow" / "spec" / "registry.json"
105
+ def _load_contract_registry(root: Path) -> tuple[list[dict[str, Any]], str | None]:
106
+ registry_file = root / ".cowork-flow" / "spec" / "runtime" / "contract-registry.json"
107
107
  try:
108
108
  data = json.loads(registry_file.read_text(encoding="utf-8"))
109
- except (OSError, json.JSONDecodeError):
109
+ except OSError:
110
110
  data = DEFAULT_CONTRACT_REGISTRY
111
+ warning = f"contract registry unavailable at {registry_file}; using fallback digest"
112
+ except json.JSONDecodeError:
113
+ data = DEFAULT_CONTRACT_REGISTRY
114
+ warning = f"contract registry invalid at {registry_file}; using fallback digest"
115
+ else:
116
+ warning = None
111
117
  contracts = data.get("contracts") if isinstance(data, dict) else None
112
118
  if not isinstance(contracts, list):
113
119
  contracts = DEFAULT_CONTRACT_REGISTRY["contracts"]
114
- return [contract for contract in contracts if isinstance(contract, dict)]
120
+ warning = warning or f"contract registry has no contracts array at {registry_file}; using fallback digest"
121
+ return [contract for contract in contracts if isinstance(contract, dict)], warning
115
122
 
116
123
 
117
124
  def _contract_fingerprint(root: Path, contracts: list[dict[str, Any]]) -> str:
@@ -129,13 +136,15 @@ def _contract_fingerprint(root: Path, contracts: list[dict[str, Any]]) -> str:
129
136
 
130
137
 
131
138
  def _build_contract_digest(root: Path) -> str:
132
- contracts = _load_contract_registry(root)
139
+ contracts, warning = _load_contract_registry(root)
133
140
  fingerprint = _contract_fingerprint(root, contracts)
134
141
  lines = [
135
142
  '<cowork-runtime host="claude-code" adapter="claude-code.hooks">',
136
143
  f'<contract-digest fingerprint="{fingerprint}">',
137
144
  "policy: repeat this short digest every hook; read full spec files only before listed actions.",
138
145
  ]
146
+ if warning:
147
+ lines.append(f"warning: {warning}")
139
148
  for contract in contracts:
140
149
  contract_id = contract.get("id")
141
150
  path = contract.get("path")
@@ -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
 
@@ -29,12 +29,14 @@ Load context before checking:
29
29
  2. Read `<task>/prd.md`.
30
30
  3. Read `<task>/check.jsonl`.
31
31
  4. Read `git diff`.
32
+ 5. Include `test_intent_review` in the review output for meaningful behavior breaks coverage and shallow tests rejection.
32
33
 
33
34
  Rules:
34
35
  - Fix issues directly when they are clearly in scope.
35
36
  - MUST NOT spawn, wait for, list, or close other agents.
36
37
  - MUST NOT commit, archive, or mutate cowork-flow task state.
37
38
  - Report findings, changed files, and exact verification commands.
39
+ - Include test intent findings that map tests back to PRD acceptance or regression behavior.
38
40
  """
39
41
 
40
42
  [features]
@@ -30,12 +30,14 @@ Load context before editing:
30
30
  3. Read `<task>/info.md` if present.
31
31
  4. Read `<task>/implement.jsonl`.
32
32
  5. Read each JSONL `file` entry.
33
+ 6. For behavior-change tasks, create or verify `<task>/tdd.jsonl` before implementation or record a documented exemption.
33
34
 
34
35
  Rules:
35
36
  - MUST NOT spawn, wait for, list, or close other agents.
36
37
  - MUST NOT run task start, task finish, task archive, or unscoped resume.
37
38
  - MUST NOT commit or push.
38
39
  - Keep edits inside requested scope.
40
+ - Do not treat shallow tests as TDD evidence; red-green-refactor proof must include redCommand, redExitCode, greenCommand, greenExitCode, and PRD acceptance ID mapping.
39
41
  - Report changed files and exact verification commands.
40
42
  """
41
43
 
@@ -20,7 +20,7 @@ DEFAULT_CONTRACT_REGISTRY = {
20
20
  "contracts": [
21
21
  {
22
22
  "id": "COWORK_ENTRY_CONTRACT_V1",
23
- "path": ".cowork-flow/spec/entry-contract.md",
23
+ "path": ".cowork-flow/spec/contracts/entry-contract.md",
24
24
  "digest": [
25
25
  "Classify main-session requests before task start, resume, archive, or commit.",
26
26
  "Runtime context, not prompt labels, identifies formal subagent sessions.",
@@ -29,7 +29,7 @@ DEFAULT_CONTRACT_REGISTRY = {
29
29
  },
30
30
  {
31
31
  "id": "RUNTIME_CONTEXT_DISPATCH_V2",
32
- "path": ".cowork-flow/spec/subagent-dispatch.md",
32
+ "path": ".cowork-flow/spec/contracts/subagent-dispatch.md",
33
33
  "digest": [
34
34
  "Formal subagent work is keyed by cowork_runtime_context_id.",
35
35
  "Explicit shim bind records bound_context_key before formal output is accepted.",
@@ -72,7 +72,7 @@ def _read_hook_input() -> dict[str, Any]:
72
72
 
73
73
 
74
74
  def _load_breadcrumbs(root: Path) -> dict[str, str]:
75
- workflow = root / ".cowork-flow" / "spec" / "workflow-state-templates.md"
75
+ workflow = root / ".cowork-flow" / "spec" / "contracts" / "workflow-state-templates.md"
76
76
  try:
77
77
  text = workflow.read_text(encoding="utf-8")
78
78
  except OSError:
@@ -86,16 +86,23 @@ def _as_string_list(value: Any) -> list[str]:
86
86
  return [item for item in value if isinstance(item, str) and item.strip()]
87
87
 
88
88
 
89
- def _load_contract_registry(root: Path) -> list[dict[str, Any]]:
90
- registry_file = root / ".cowork-flow" / "spec" / "registry.json"
89
+ def _load_contract_registry(root: Path) -> tuple[list[dict[str, Any]], str | None]:
90
+ registry_file = root / ".cowork-flow" / "spec" / "runtime" / "contract-registry.json"
91
91
  try:
92
92
  data = json.loads(registry_file.read_text(encoding="utf-8"))
93
- except (OSError, json.JSONDecodeError):
93
+ except OSError:
94
94
  data = DEFAULT_CONTRACT_REGISTRY
95
+ warning = f"contract registry unavailable at {registry_file}; using fallback digest"
96
+ except json.JSONDecodeError:
97
+ data = DEFAULT_CONTRACT_REGISTRY
98
+ warning = f"contract registry invalid at {registry_file}; using fallback digest"
99
+ else:
100
+ warning = None
95
101
  contracts = data.get("contracts") if isinstance(data, dict) else None
96
102
  if not isinstance(contracts, list):
97
103
  contracts = DEFAULT_CONTRACT_REGISTRY["contracts"]
98
- return [contract for contract in contracts if isinstance(contract, dict)]
104
+ warning = warning or f"contract registry has no contracts array at {registry_file}; using fallback digest"
105
+ return [contract for contract in contracts if isinstance(contract, dict)], warning
99
106
 
100
107
 
101
108
  def _contract_fingerprint(root: Path, contracts: list[dict[str, Any]]) -> str:
@@ -113,13 +120,15 @@ def _contract_fingerprint(root: Path, contracts: list[dict[str, Any]]) -> str:
113
120
 
114
121
 
115
122
  def _build_contract_digest(root: Path) -> str:
116
- contracts = _load_contract_registry(root)
123
+ contracts, warning = _load_contract_registry(root)
117
124
  fingerprint = _contract_fingerprint(root, contracts)
118
125
  lines = [
119
126
  '<cowork-runtime host="codex" adapter="codex.spawn_agent">',
120
127
  f'<contract-digest fingerprint="{fingerprint}">',
121
128
  "policy: repeat this short digest every hook; read full spec files only before listed actions.",
122
129
  ]
130
+ if warning:
131
+ lines.append(f"warning: {warning}")
123
132
  for contract in contracts:
124
133
  contract_id = contract.get("id")
125
134
  path = contract.get("path")
@@ -1 +1 @@
1
- 0.0.27
1
+ 0.0.28