cowork-flow 1.2.0 → 1.4.0
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.
- package/CHANGELOG.md +45 -0
- package/README.md +4 -3
- package/package.json +1 -1
- package/template/.claude/hooks/inject-workflow-state.py +12 -98
- package/template/.codex/hooks/inject-workflow-state.py +13 -89
- package/template/.cowork-flow/.version +1 -1
- package/template/.cowork-flow/scripts/adapters/cli/task.py +4 -1
- package/template/.cowork-flow/scripts/adapters/cli/task_context_commands.py +65 -0
- package/template/.cowork-flow/scripts/adapters/cli/task_parser.py +23 -0
- package/template/.cowork-flow/scripts/adapters/host/claude_code_policy.py +52 -0
- package/template/.cowork-flow/scripts/adapters/host/codex_policy.py +61 -0
- package/template/.cowork-flow/scripts/adapters/host/inject.py +249 -0
- package/template/.cowork-flow/scripts/adapters/host/workflow_state_hook.py +315 -38
- package/template/.cowork-flow/scripts/adapters/host/zcode_policy.py +285 -0
- package/template/.cowork-flow/scripts/adapters/mcp/state_server.py +34 -11
- package/template/.cowork-flow/scripts/runtime/session_state.py +54 -9
- package/template/.cowork-flow/scripts/services/context_discovery.py +18 -5
- package/template/.cowork-flow/scripts/services/fact_view.py +14 -0
- package/template/.cowork-flow/scripts/services/spec_check.py +34 -12
- package/template/.cowork-flow/scripts/services/task_context.py +0 -1
- package/template/.cowork-flow/spec/contracts/context-injection.md +40 -23
- package/template/.cowork-flow/spec/contracts/fact-layer-access.md +43 -8
- package/template/.cowork-flow/spec/contracts/spec-checks.md +40 -8
- package/template/.cowork-flow/spec/contracts/subagent-dispatch.md +21 -0
- package/template/.cowork-flow/spec/runtime/contract-registry.json +12 -0
- package/template/.opencode/plugins/cowork-flow.js +12 -0
- package/template/.zcode/.zcode-plugin/plugin.json +1 -1
- package/template/.zcode/hooks/inject-context.js +95 -971
- package/template/skills/agent-dispatch/SKILL.md +15 -0
- package/template/skills/runtime-health/scripts/doctor.py +90 -0
- package/template/skills/task-planning/SKILL.md +1 -0
- package/template/skills/task-review/SKILL.md +1 -0
- package/template/.zcode/hooks/inject-context.selfcheck.mjs +0 -795
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
Single source for how every host adapter injects cowork-flow runtime context
|
|
4
4
|
into a session: the transport shape, the event timing matrix, the contract
|
|
5
5
|
digest shape rules (full block vs fingerprint line), and the fingerprint
|
|
6
|
-
serialization norm.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
serialization norm. The workflow facts are rendered by ONE Python source —
|
|
7
|
+
`adapters/host/inject.py` over `workflow_state_hook.py` +
|
|
8
|
+
`services.fact_view.py` — consumed by the zcode transport shim (node),
|
|
9
|
+
claude-code, codex, and dsh; the opencode JS plugin remains the one
|
|
10
|
+
independent mirror, locked to the same bytes by cross-host behavior tests.
|
|
10
11
|
|
|
11
12
|
This document is a meta-protocol: it describes the injection layer itself and
|
|
12
13
|
is intentionally **not** registered in `contract-registry.json` (registering it
|
|
@@ -16,9 +17,9 @@ would make the digest self-referential).
|
|
|
16
17
|
|
|
17
18
|
| Host | Transport | Shape |
|
|
18
19
|
|---|---|---|
|
|
19
|
-
| zcode | process hook (`inject-context.js`)
|
|
20
|
-
| codex |
|
|
21
|
-
| claude-code |
|
|
20
|
+
| zcode | process hook shim (`inject-context.js`) → `inject.py --host zcode`, stdout JSON | `hookSpecificOutput.{hookEventName, additionalContext}` |
|
|
21
|
+
| codex | thin wrapper (`inject-workflow-state.py`) → `inject.py --host codex`, stdout JSON | same shape |
|
|
22
|
+
| claude-code | thin wrapper (`inject-workflow-state.py`) → `inject.py --host claude-code`, stdout JSON | same shape |
|
|
22
23
|
| opencode | plugin `experimental.chat.system.transform` + `shell.env` | system-prompt section push / env object |
|
|
23
24
|
| dsh | preset plugin system-prompt section | named section, replace semantics |
|
|
24
25
|
|
|
@@ -27,7 +28,8 @@ runtime preamble, the contract digest (full block or fingerprint line, see
|
|
|
27
28
|
below), optional `decision-anchor` / `stage-contract` blocks, and the
|
|
28
29
|
`<workflow-state>` block. It is **not** byte-identical across hosts at the
|
|
29
30
|
string level — the digest policy line differs per host wording and zcode
|
|
30
|
-
drops the registry-warning line
|
|
31
|
+
drops the registry-warning line; both differences are selected inside the
|
|
32
|
+
single Python source by `--host`. Exactly two things are byte-identical and
|
|
31
33
|
test-locked: the contract digest **fingerprint value** and the
|
|
32
34
|
`<stage-contract>` block.
|
|
33
35
|
|
|
@@ -83,8 +85,13 @@ The `<workflow-state>` open tag carries the machine-readable fact header as
|
|
|
83
85
|
XML attributes; the body keeps the human-readable breadcrumb prose.
|
|
84
86
|
|
|
85
87
|
- Attributes: `status` and `source` are always present; `task` (repo-relative
|
|
86
|
-
task path) appears whenever a task is bound
|
|
87
|
-
|
|
88
|
+
task path) appears whenever a task is bound; `session` (the caller's
|
|
89
|
+
resolved context key) appears whenever a session identity is resolvable
|
|
90
|
+
from the hook input or environment and is omitted otherwise (hosts that
|
|
91
|
+
cannot resolve an identity never guess one — the attribute is the machine
|
|
92
|
+
handle for re-binding with `COWORK_FLOW_CONTEXT_ID`). Attribute values are
|
|
93
|
+
XML-escaped (`& < > "`). The opencode JS mirror does not emit `session`
|
|
94
|
+
(delegated-only transport; documented behavioral difference, not drift).
|
|
88
95
|
- The legacy `Task:` / `Status:` / `Source:` label lines are gone — machines
|
|
89
96
|
parse the attributes, humans read the body. There is no bare `Scope:`
|
|
90
97
|
line in the body: the stage-contract block owns the scope declaration
|
|
@@ -92,11 +99,19 @@ XML attributes; the body keeps the human-readable breadcrumb prose.
|
|
|
92
99
|
as a read-only `[read-only]` reference of the parent task).
|
|
93
100
|
|
|
94
101
|
```text
|
|
95
|
-
<workflow-state task=".cowork-flow/tasks/08-28-demo" status="in_progress" source="runtime-session">
|
|
102
|
+
<workflow-state task=".cowork-flow/tasks/08-28-demo" status="in_progress" source="runtime-session" session="zcode_probe">
|
|
96
103
|
活动任务正在执行。...
|
|
97
104
|
</workflow-state>
|
|
98
105
|
```
|
|
99
106
|
|
|
107
|
+
- Unbound-session fallback (zcode only, display/warning-only): a hook
|
|
108
|
+
session with no binding follows another main session's binding only while
|
|
109
|
+
**exactly one** main-session binding exists (`source="session-fallback"`).
|
|
110
|
+
With two or more main-session bindings the newest one belongs to some
|
|
111
|
+
other window, so the fallback refuses: the injection renders
|
|
112
|
+
`status="no_task"` plus the rebind hints list. The CLI lifecycle commands
|
|
113
|
+
keep their strict identity semantics regardless (FALLBACK_BINDING_BLOCKER).
|
|
114
|
+
|
|
100
115
|
## Decision-anchor injection (stage 1)
|
|
101
116
|
|
|
102
117
|
For tasks whose status is `planning`, `in_progress`, or `review` (a delegated
|
|
@@ -171,16 +186,17 @@ Verify: npm run test:fast; python3 -m pytest tests/ -q
|
|
|
171
186
|
(min 1) — the closing tag and the Gates row always survive; tests assert
|
|
172
187
|
the block stays well-formed. The scope-filter rules and the stage-contract
|
|
173
188
|
budget/limits come from `.cowork-flow/spec/runtime/scope-rules.json`, the
|
|
174
|
-
single source consumed
|
|
175
|
-
|
|
176
|
-
|
|
189
|
+
single source consumed at runtime; a missing or malformed file degrades to
|
|
190
|
+
embedded defaults that are byte-identical with the shipped file (locked by
|
|
191
|
+
`tests/test_scope_rules.py`).
|
|
177
192
|
- Absent for `no_task` / `planning` / `completed` states. Block content is
|
|
178
|
-
byte-identical across
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
193
|
+
byte-identical across host lines for the whole fixture matrix
|
|
194
|
+
(`test/fixtures/stage-contract-matrix.json` drives the equality test:
|
|
195
|
+
canonical, `./`-prefixed, invalid-boundary, over-budget, emoji,
|
|
196
|
+
missing-anchor, empty-scope, delegated-readonly). The zcode line now runs
|
|
197
|
+
through the same Python source as the python line (transport shim), so the
|
|
198
|
+
matrix's remaining cross-host assertion is opencode's JS mirror against
|
|
199
|
+
the single source — a mirror, not a mechanism.
|
|
184
200
|
|
|
185
201
|
The authoritative per-file verdict lives in the MCP `task_scope` tool
|
|
186
202
|
(`services.fact_view.file_scope_whitelist` — same semantics, live data).
|
|
@@ -189,15 +205,16 @@ The authoritative per-file verdict lives in the MCP `task_scope` tool
|
|
|
189
205
|
|
|
190
206
|
sha256 over (1) the registry `contracts` array serialized with
|
|
191
207
|
**recursively sorted keys** (Python `json.dumps(..., sort_keys=True,
|
|
192
|
-
ensure_ascii=False)`; the
|
|
208
|
+
ensure_ascii=False)`; the opencode plugin uses the equivalent
|
|
193
209
|
`stableStringify`), then (2) each contract file's raw bytes in registry
|
|
194
210
|
order; a missing file hashes as the literal string `missing:<path>`. The
|
|
195
211
|
fingerprint is the first 16 hex chars of the digest.
|
|
196
212
|
|
|
197
|
-
Serialization must be byte-identical across
|
|
213
|
+
Serialization must be byte-identical across implementations: same
|
|
198
214
|
key order, same escaping (ASCII-safe content), same UTF-8 encoding. The
|
|
199
215
|
cross-host test (`test/contract-fingerprint.test.js`) asserts this and is the
|
|
200
|
-
regression lock against drift
|
|
216
|
+
regression lock against drift between the opencode mirror and the Python
|
|
217
|
+
single source.
|
|
201
218
|
|
|
202
219
|
## Change guard
|
|
203
220
|
|
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
# Fact-layer access
|
|
2
2
|
|
|
3
3
|
How external tools (MCP clients today, other ecosystem protocols as they
|
|
4
|
-
converge) read cowork-flow task
|
|
4
|
+
converge) and agents on hosts without injection hooks read cowork-flow task
|
|
5
|
+
facts. Stage 3 of `docs/direction.md`.
|
|
6
|
+
|
|
7
|
+
## Three channels, one fact layer
|
|
8
|
+
|
|
9
|
+
All channels consume the same services layer (`services/fact_view.py`) and
|
|
10
|
+
cannot drift on facts:
|
|
11
|
+
|
|
12
|
+
| Channel | Direction | Role |
|
|
13
|
+
|---|---|---|
|
|
14
|
+
| Hooks (host adapter → `inject.py`) | push | Proactive context injection; host-enforced, agents cannot skip it. |
|
|
15
|
+
| CLI lifecycle commands | write + pull | The only write path; enforces the full gate chain. Pull fallback when MCP is unavailable. |
|
|
16
|
+
| MCP tools | pull | Standardized read access for external clients (IDEs, dashboards) and hosts without hook support. |
|
|
5
17
|
|
|
6
18
|
## Stance
|
|
7
19
|
|
|
@@ -25,19 +37,42 @@ stderr is kept log-only).
|
|
|
25
37
|
|
|
26
38
|
Global registration: `cowork-flow mcp-state` (npm CLI) resolves the nearest
|
|
27
39
|
`.cowork-flow/` from the client's cwd and execs that project's runner with
|
|
28
|
-
inherited stdio
|
|
29
|
-
|
|
30
|
-
resolution is identical to the server's own (`get_repo_root`). Outside a
|
|
31
|
-
project the passthrough fails with a clear error; inside one, nested
|
|
32
|
-
subdirectories resolve to the project root.
|
|
40
|
+
inherited stdio. Outside a project the passthrough fails with a clear error;
|
|
41
|
+
inside one, nested subdirectories resolve to the project root.
|
|
33
42
|
|
|
34
43
|
Server info: `cowork-flow-facts`. On `initialize` the server echoes the
|
|
35
44
|
client's requested `protocolVersion` (falling back to `2025-06-18`).
|
|
36
45
|
|
|
37
46
|
| Tool | Input | Output | Reads |
|
|
38
47
|
|---|---|---|---|
|
|
39
|
-
| `task_state` | `{task?: string}` | The fact view as JSON text (`task`, `decisionAnchor`, `plan`, `sessions`, `snapshot`); without `task`, the session-bound active task, or `{"task": null, "reason": "no-active-task"}` | `services/fact_view.build_fact_view` |
|
|
48
|
+
| `task_state` | `{task?: string}` | The fact view as JSON text (`task`, `decisionAnchor`, `plan`, `sessions`, `snapshot`); without `task`, the session-bound active task when one resolves, or `{"task": null, "reason": "no-active-task"}` | `services/fact_view.build_fact_view` |
|
|
40
49
|
| `task_list` | `{}` | Active tasks overview: name, path, status, assignee, parent/children, active flag | `TaskTreeService.active_nodes` via the list records |
|
|
50
|
+
| `task_scope` | `{task?: string, path?: string}` | The file-scope whitelist summary, or a per-path `inScope` verdict with `path` | `services/fact_view.file_scope_whitelist` / `path_in_scope` |
|
|
51
|
+
| `task_specs` | `{task?: string}` | The spec/skill reading list dispatched by the task's `dev_type` | `services/fact_view.spec_entries_for_task` |
|
|
52
|
+
|
|
53
|
+
`task_scope` and `task_specs` have CLI fact-command equivalents
|
|
54
|
+
(`task scope`, `task specs`) consuming the same facade — MCP holds no
|
|
55
|
+
exclusive capability.
|
|
56
|
+
|
|
57
|
+
The MCP server process has no session concept: `task_state` without `task`
|
|
58
|
+
resolves the caller's session only when session identity reaches the server
|
|
59
|
+
through the environment (session-bound env keys). Where it cannot, it returns
|
|
60
|
+
`no-active-task` — pass `task` explicitly for deterministic project-level
|
|
61
|
+
queries. Session-bound facts remain the hook/CLI channels' responsibility.
|
|
62
|
+
|
|
63
|
+
## Registration map (two tiers)
|
|
64
|
+
|
|
65
|
+
| Host | Global (supported default) | Project-level opt-in |
|
|
66
|
+
|---|---|---|
|
|
67
|
+
| claude-code | `claude mcp add cowork-flow -- cowork-flow mcp-state` | `.mcp.json` with an `mcpServers` entry |
|
|
68
|
+
| zcode | user config `mcp.servers.cowork-flow = {command: cowork-flow, args: [mcp-state]}` | plugin format has no MCP field — use the global tier |
|
|
69
|
+
| codex | `~/.codex/config.toml` `[mcp_servers.cowork-flow]` | project config cannot enable user-approval-gated capabilities — use the global tier |
|
|
70
|
+
| opencode | `opencode.json` `mcp` entry | — |
|
|
71
|
+
|
|
72
|
+
`run doctor` reports the registration state (project entry present, absent,
|
|
73
|
+
or duplicated across tiers) as an advisory, read-only health item. Registering
|
|
74
|
+
a new contract changes the digest fingerprint everywhere; see the change
|
|
75
|
+
guard.
|
|
41
76
|
|
|
42
77
|
Tool failures (unknown task, missing task.json) ride the tool result with
|
|
43
78
|
`isError: true`; the JSON-RPC frame stays valid.
|
|
@@ -47,4 +82,4 @@ Tool failures (unknown task, missing task.json) ride the tool result with
|
|
|
47
82
|
- New fact sources (fields on the fact view) are additive; consumers must
|
|
48
83
|
tolerate unknown keys.
|
|
49
84
|
- Any write-capable tool proposal must re-open this contract first — and is
|
|
50
|
-
rejected by default under the current stance.
|
|
85
|
+
rejected by default under the current stance.
|
|
@@ -21,9 +21,11 @@ checks:
|
|
|
21
21
|
|
|
22
22
|
- `files` 只支持两种形式:目录前缀(`src/`,以 `/` 结尾或自动补齐)与
|
|
23
23
|
扩展名(`*.ts`)。完整 glob 形态(`**`、`[]`)解析为错误,声明不执行,
|
|
24
|
-
doctor 报告——绝不回退为"匹配一切"
|
|
24
|
+
doctor 报告——绝不回退为"匹配一切"。多值形态(如 `files: "src/", "lib/"`)
|
|
25
|
+
每个 token 按同样的引号规则去引号后生效。
|
|
25
26
|
- `when: edit` 的声明在编辑期(PostToolUse)对命中 `files` 的改动文件
|
|
26
|
-
就地执行,超时被钳制到
|
|
27
|
+
就地执行,超时被钳制到 2.5 秒(为解释器启动与 cmd 包装留 spawn 预算);
|
|
28
|
+
慢命令请声明 `when: lifecycle`。
|
|
27
29
|
- 引号规则:仅值整体为单对引号时去引号;`"python" -c "..."`
|
|
28
30
|
这类含多对引号的命令值原样保留,由执行器按 shell 规则拆分。
|
|
29
31
|
|
|
@@ -42,12 +44,26 @@ spec 为无检查并进 doctor 报告,不炸流程。
|
|
|
42
44
|
|
|
43
45
|
- **编辑期**(zcode/claude):违规输出单行
|
|
44
46
|
`spec-check[<spec>] violation: <首个违规行>`;同一文件 10 秒内只报一次
|
|
45
|
-
(节流状态在 `.cowork-flow/.runtime/spec-edit-throttle.json
|
|
46
|
-
|
|
47
|
+
(节流状态在 `.cowork-flow/.runtime/spec-edit-throttle.json`,检查完整
|
|
48
|
+
跑过才写节流——执行器崩溃不消费时间窗,重试仍会执行);执行器
|
|
49
|
+
缺失或超时静默,绝不阻断编辑流。delegated 子代理会话同样收到 spec
|
|
50
|
+
违规反馈(子代理是主力写码者);scope 警告保持 main-only。zcode 主
|
|
51
|
+
会话的 hook 身份(对话自身 session id)不会被绑定——激活发生在 Bash
|
|
52
|
+
侧显式身份下——显示与编辑期警告按最新主会话兜底;claude/codex 保持
|
|
53
|
+
严格会话身份(其 Bash env 携带同一 session id)。zcode 经
|
|
54
|
+
shim 转发到单源入口(`inject.py`),scope 警告与 spec 警告合并进同一个
|
|
55
|
+
additionalContext 载荷;claude 走 exit 2 stderr 反馈。
|
|
47
56
|
- **收口期**(task complete):全量执行,结果进 `meta.specCheckSummary`
|
|
48
57
|
遥测;阻断消息只列未过项摘要。
|
|
49
58
|
- 手动查询:`./.cowork-flow/run spec-check [--file <path>] [--json]
|
|
50
59
|
[--verbose]`。
|
|
60
|
+
- **edit-only 是 best-effort 提示,不是门禁**:Bash 直写绕过 PostToolUse
|
|
61
|
+
不会触发编辑期反馈;无编辑期快跑能力的宿主(见能力矩阵)同样没有
|
|
62
|
+
这层提示,且收口期不会对 `when: edit` 声明补跑。需要硬门禁的检查
|
|
63
|
+
必须声明 `when: lifecycle` 或默认 `both`。
|
|
64
|
+
- **开放决策**:收口期全量执行没有聚合预算——声明多且慢的仓库 complete
|
|
65
|
+
会线性变慢;是否给收口加并行/预算上限留给后续产品决策,当前保持
|
|
66
|
+
简单串行。
|
|
51
67
|
|
|
52
68
|
## 归属与 sync 策略
|
|
53
69
|
|
|
@@ -60,15 +76,31 @@ spec 为无检查并进 doctor 报告,不炸流程。
|
|
|
60
76
|
|
|
61
77
|
| 宿主 | digest 注入 | 编辑期快跑 | 收口强制 |
|
|
62
78
|
| --- | --- | --- | --- |
|
|
63
|
-
| zcode | 有(Specs 行 h2 digest) | 有(PostToolUse
|
|
79
|
+
| zcode | 有(Specs 行 h2 digest) | 有(PostToolUse 短路径;**仅主会话**) | 有 |
|
|
64
80
|
| claude-code | 有(Python 单源) | 有(PostToolUse,exit 2 反馈) | 有 |
|
|
65
81
|
| opencode | 有 | **降级:无**(插件无 PostToolUse 短路径) | 有 |
|
|
66
82
|
|
|
83
|
+
**delegated 子代理编辑期缺口(zcode 实测,2026-09-09 两轮)**:ZCode 的插件
|
|
84
|
+
hook 只在主会话工具流上派发(事件模型七事件均挂主会话;子代理是独立内部
|
|
85
|
+
会话流 `sess_subagent_agent_*`),Agent 子代理的 Edit/Write 收不到
|
|
86
|
+
`spec-check[...] violation` 行——runtime 绑定与检查逻辑均正常,缺的是宿主
|
|
87
|
+
派发。补偿机制(subagent-dispatch.md 契约固化):
|
|
88
|
+
|
|
89
|
+
1. 子代理完成前自查:`./.cowork-flow/run spec-check` 全量并修复(拉取路径,
|
|
90
|
+
实测有效);
|
|
91
|
+
2. 父会话验收前再查:violation 视为验收阻塞(反馈时点从收口提前到返回);
|
|
92
|
+
3. 实现类任务 Verify 命令默认含 spec-check 自查;
|
|
93
|
+
4. 收口硬门禁兜底不变(violation 阻断 complete)。
|
|
94
|
+
|
|
95
|
+
治理方向:向宿主提需求扩展子代理工具流的 hook 派发;落地后第 1 条自动
|
|
96
|
+
退化为冗余保险。
|
|
97
|
+
|
|
67
98
|
## digest 注入
|
|
68
99
|
|
|
69
100
|
进入 in_progress 后,stage-contract 的 Specs 行升级为
|
|
70
|
-
`Specs: <spec>(<h2 标题树>); …`:最多 6 个 h2 条目、每条截断 24
|
|
71
|
-
|
|
101
|
+
`Specs: <spec>(<h2 标题树>); …`:最多 6 个 h2 条目、每条截断 24 字符、剔除
|
|
102
|
+
`();` 字符;文件缺失不加注解。digest 是条目名索引不是正文,受
|
|
72
103
|
scope-rules.json `stageContract.budget` 硬顶,超预算整行让位
|
|
73
|
-
(Scope/Gates
|
|
104
|
+
(Scope/Gates 行永不丢)。python/zcode 输出同源于单一 Python 入口,
|
|
105
|
+
opencode 镜像与它逐字相等由 matrix 用例锁定
|
|
74
106
|
(`test/stage-contract.test.js` matrix)。
|
|
@@ -61,12 +61,33 @@ child output. If binding fails, the child must receive fail-closed subagent stat
|
|
|
61
61
|
and must not run main-session start/resume, task activation, archive, commit,
|
|
62
62
|
or agent coordination.
|
|
63
63
|
|
|
64
|
+
## Delegated Spec Self-Check
|
|
65
|
+
|
|
66
|
+
Delegated subagent tool calls receive no edit-phase hook feedback on hosts
|
|
67
|
+
whose plugin hooks cover only the main session stream (verified on zcode:
|
|
68
|
+
plugin PostToolUse fires for main-session tool calls but never for
|
|
69
|
+
Agent-tool children). The reliable pull path is the CLI: before reporting
|
|
70
|
+
completion, `cowork-implement` (and any child that changes files) runs
|
|
71
|
+
|
|
72
|
+
```text
|
|
73
|
+
./.cowork-flow/run spec-check
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
fixes every reported violation, and re-runs until the summary is clean or the
|
|
77
|
+
remaining violations are reported as blockers. This is a protocol behavior,
|
|
78
|
+
not a second lifecycle gate — the closeout gate re-runs the same checks and
|
|
79
|
+
blocks completion regardless.
|
|
80
|
+
|
|
64
81
|
## Return Acceptance And Closeout
|
|
65
82
|
|
|
66
83
|
- Wait for the child result with the adapter wait primitive.
|
|
67
84
|
- Confirm no stray running children with the adapter list primitive.
|
|
68
85
|
- Verify the child report by checking files, commands, and results; do not
|
|
69
86
|
trust completed text alone.
|
|
87
|
+
- Before accepting delegated output, run `./.cowork-flow/run spec-check` in
|
|
88
|
+
the main session and treat every violation as an acceptance blocker: the
|
|
89
|
+
child receives no edit-phase hook feedback, so acceptance-time checking is
|
|
90
|
+
the earliest reliable checkpoint for delegated work.
|
|
70
91
|
- After completion or failure, close the child with the adapter cancel/close
|
|
71
92
|
primitive and clean the runtime context sessions.
|
|
72
93
|
- The child remains a leaf executor and must not dispatch, wait for, list, or
|
|
@@ -63,6 +63,18 @@
|
|
|
63
63
|
"when adding new workflow states"
|
|
64
64
|
]
|
|
65
65
|
},
|
|
66
|
+
{
|
|
67
|
+
"id": "FACT_LAYER_ACCESS_V1",
|
|
68
|
+
"path": ".cowork-flow/spec/contracts/fact-layer-access.md",
|
|
69
|
+
"digest": [
|
|
70
|
+
"Task/scope/spec facts are read-only via MCP tools or CLI; writes only flow through CLI lifecycle gates.",
|
|
71
|
+
"Prefer MCP task_state/task_scope/task_specs for fact queries when available; CLI is the fallback."
|
|
72
|
+
],
|
|
73
|
+
"readWhen": [
|
|
74
|
+
"when querying task facts or file scope",
|
|
75
|
+
"when registering the fact layer for a new host or external client"
|
|
76
|
+
]
|
|
77
|
+
},
|
|
66
78
|
{
|
|
67
79
|
"id": "PARTY_MODE_V2_BOARD_V1",
|
|
68
80
|
"path": ".cowork-flow/spec/contracts/party-mode-v2-board.md",
|
|
@@ -14,6 +14,18 @@ const DEFAULT_CONTRACT_REGISTRY = {
|
|
|
14
14
|
],
|
|
15
15
|
readWhen: ["before formal subagent dispatch", "when checking subagent health"],
|
|
16
16
|
},
|
|
17
|
+
{
|
|
18
|
+
id: "FACT_LAYER_ACCESS_V1",
|
|
19
|
+
path: ".cowork-flow/spec/contracts/fact-layer-access.md",
|
|
20
|
+
digest: [
|
|
21
|
+
"Task/scope/spec facts are read-only via MCP tools or CLI; writes only flow through CLI lifecycle gates.",
|
|
22
|
+
"Prefer MCP task_state/task_scope/task_specs for fact queries when available; CLI is the fallback.",
|
|
23
|
+
],
|
|
24
|
+
readWhen: [
|
|
25
|
+
"when querying task facts or file scope",
|
|
26
|
+
"when registering the fact layer for a new host or external client",
|
|
27
|
+
],
|
|
28
|
+
},
|
|
17
29
|
],
|
|
18
30
|
}
|
|
19
31
|
|