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.
Files changed (33) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/README.md +4 -3
  3. package/package.json +1 -1
  4. package/template/.claude/hooks/inject-workflow-state.py +12 -98
  5. package/template/.codex/hooks/inject-workflow-state.py +13 -89
  6. package/template/.cowork-flow/.version +1 -1
  7. package/template/.cowork-flow/scripts/adapters/cli/task.py +4 -1
  8. package/template/.cowork-flow/scripts/adapters/cli/task_context_commands.py +65 -0
  9. package/template/.cowork-flow/scripts/adapters/cli/task_parser.py +23 -0
  10. package/template/.cowork-flow/scripts/adapters/host/claude_code_policy.py +52 -0
  11. package/template/.cowork-flow/scripts/adapters/host/codex_policy.py +61 -0
  12. package/template/.cowork-flow/scripts/adapters/host/inject.py +249 -0
  13. package/template/.cowork-flow/scripts/adapters/host/workflow_state_hook.py +315 -38
  14. package/template/.cowork-flow/scripts/adapters/host/zcode_policy.py +285 -0
  15. package/template/.cowork-flow/scripts/adapters/mcp/state_server.py +34 -11
  16. package/template/.cowork-flow/scripts/runtime/session_state.py +54 -9
  17. package/template/.cowork-flow/scripts/services/context_discovery.py +18 -5
  18. package/template/.cowork-flow/scripts/services/fact_view.py +14 -0
  19. package/template/.cowork-flow/scripts/services/spec_check.py +34 -12
  20. package/template/.cowork-flow/scripts/services/task_context.py +0 -1
  21. package/template/.cowork-flow/spec/contracts/context-injection.md +40 -23
  22. package/template/.cowork-flow/spec/contracts/fact-layer-access.md +43 -8
  23. package/template/.cowork-flow/spec/contracts/spec-checks.md +40 -8
  24. package/template/.cowork-flow/spec/contracts/subagent-dispatch.md +21 -0
  25. package/template/.cowork-flow/spec/runtime/contract-registry.json +12 -0
  26. package/template/.opencode/plugins/cowork-flow.js +12 -0
  27. package/template/.zcode/.zcode-plugin/plugin.json +1 -1
  28. package/template/.zcode/hooks/inject-context.js +95 -971
  29. package/template/skills/agent-dispatch/SKILL.md +15 -0
  30. package/template/skills/runtime-health/scripts/doctor.py +90 -0
  31. package/template/skills/task-planning/SKILL.md +1 -0
  32. package/template/skills/task-review/SKILL.md +1 -0
  33. 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. All host implementations — zcode JS hook, the Python
7
- shared core (codex / claude-code / dsh), and the opencode JS plugin — must
8
- follow this document; cross-host behavior tests lock the three independent
9
- implementations to it.
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`), stdout JSON | `hookSpecificOutput.{hookEventName, additionalContext}` |
20
- | codex | command hook (Python shell), stdout JSON | same shape |
21
- | claude-code | command hook (Python shell), stdout JSON | same shape |
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. Exactly two things are byte-identical and
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. Attribute values are XML-escaped
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 by Python and both JS mirrors at runtime; a missing
175
- or malformed file degrades to embedded defaults that are byte-identical
176
- with the shipped file (locked by `tests/test_scope_rules.py`).
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 the three host implementations for the whole fixture
179
- matrix (`test/fixtures/stage-contract-matrix.json` drives the cross-host
180
- equality test: canonical, `./`-prefixed, invalid-boundary, over-budget,
181
- emoji, missing-anchor, empty-scope, delegated-readonly). The JS scope
182
- filter is a port of the Python rules and is locked by that matrix — a
183
- port, not a mechanism, so the matrix list grows whenever the rules grow.
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 JS implementations use the equivalent
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 the three implementations: same
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 facts. Stage 3 of `docs/direction.md`.
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 — register it once (`command: cowork-flow, args:
29
- ["mcp-state"]`) and every cowork-flow project works, because the root
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
- 就地执行,超时被钳制到 3 秒;慢命令请声明 `when: lifecycle`。
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
- 剔除 `();` 字符;文件缺失不加注解。digest 是条目名索引不是正文,受
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
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cowork-flow",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "AI 协作工作流引擎:任务生命周期、子代理调度、门禁与状态注入。将 cowork-flow 的完整工作流带入 ZCode。",
5
5
  "author": {
6
6
  "name": "fisklikewater"