cowork-flow 1.9.2 → 2.0.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 (61) hide show
  1. package/CHANGELOG.md +23 -1
  2. package/docs/architecture.md +1 -34
  3. package/docs/direction.md +1 -1
  4. package/docs/hosts.md +13 -9
  5. package/package.json +1 -1
  6. package/presets/claude-code/.claude-plugin/plugin.json +1 -1
  7. package/presets/codex/.codex-plugin/plugin.json +1 -1
  8. package/presets/dsh/plugins/workflow-state.js +31 -16
  9. package/presets/kimi-code/.kimi-plugin/plugin.json +1 -1
  10. package/presets/opencode/cowork-flow/plugin-core.js +42 -87
  11. package/presets/opencode/plugins/cowork-flow.js +7 -9
  12. package/presets/qoder/.qoder-plugin/plugin.json +1 -1
  13. package/presets/qoder/hooks/hooks.json +1 -1
  14. package/presets/zcode/.zcode-plugin/plugin.json +1 -1
  15. package/src/commands/host.js +23 -2
  16. package/src/commands/registry.js +4 -2
  17. package/template/.cowork-flow/.version +1 -1
  18. package/template/.cowork-flow/adapters/claude-code/adapter.yaml +1 -1
  19. package/template/.cowork-flow/adapters/codex/adapter.yaml +1 -1
  20. package/template/.cowork-flow/adapters/dsh/adapter.yaml +1 -1
  21. package/template/.cowork-flow/adapters/opencode/adapter.yaml +1 -1
  22. package/template/.cowork-flow/adapters/qoder/adapter.yaml +1 -1
  23. package/template/.cowork-flow/scripts/adapters/cli/task_lifecycle_commands.py +0 -1
  24. package/template/.cowork-flow/scripts/adapters/cli/task_parser.py +0 -7
  25. package/template/.cowork-flow/scripts/adapters/host/claude_code_policy.py +6 -4
  26. package/template/.cowork-flow/scripts/adapters/host/codex_policy.py +8 -6
  27. package/template/.cowork-flow/scripts/adapters/host/edit_scope.py +113 -0
  28. package/template/.cowork-flow/scripts/adapters/host/opencode_policy.py +12 -0
  29. package/template/.cowork-flow/scripts/adapters/host/qoder_policy.py +7 -5
  30. package/template/.cowork-flow/scripts/adapters/host/workflow_state_hook.py +0 -91
  31. package/template/.cowork-flow/scripts/adapters/host/zcode_policy.py +15 -77
  32. package/template/.cowork-flow/scripts/run.py +0 -2
  33. package/template/.cowork-flow/scripts/services/context_paths.py +2 -2
  34. package/template/.cowork-flow/scripts/services/fact_view.py +49 -21
  35. package/template/.cowork-flow/scripts/services/lifecycle_checks.py +1 -216
  36. package/template/.cowork-flow/scripts/services/lifecycle_policy.py +0 -5
  37. package/template/.cowork-flow/scripts/services/task_gate_profiles.py +1 -1
  38. package/template/.cowork-flow/scripts/services/task_lifecycle.py +14 -31
  39. package/template/.cowork-flow/spec/backend/error-handling.md +0 -14
  40. package/template/.cowork-flow/spec/contracts/context-injection.md +49 -22
  41. package/template/.cowork-flow/spec/contracts/decision-anchor.md +3 -3
  42. package/template/.cowork-flow/spec/contracts/error-codes.md +1 -3
  43. package/template/.cowork-flow/spec/contracts/evidence-completion.md +6 -8
  44. package/template/.cowork-flow/spec/contracts/index.md +0 -1
  45. package/template/.cowork-flow/spec/contracts/plan-binding.md +3 -2
  46. package/template/.cowork-flow/spec/contracts/subagent-dispatch.md +6 -21
  47. package/template/.cowork-flow/spec/contracts/task-adopt.md +1 -1
  48. package/template/.cowork-flow/spec/runtime/contract-registry.json +0 -13
  49. package/template/.cowork-flow/spec/runtime/host-assets.json +10 -7
  50. package/template/.cowork-flow/spec/runtime/scope-rules.json +2 -2
  51. package/template/.opencode/cowork-flow/plugin-core.js +42 -87
  52. package/template/.opencode/plugins/cowork-flow.js +7 -9
  53. package/template/AGENTS.md +0 -1
  54. package/template/skills/agent-dispatch/SKILL.md +0 -15
  55. package/template/skills/runtime-health/scripts/doctor.py +0 -49
  56. package/template/skills/spec-sync/SKILL.md +0 -1
  57. package/template/skills/task-planning/SKILL.md +1 -1
  58. package/template/skills/task-review/SKILL.md +3 -3
  59. package/template/.cowork-flow/scripts/adapters/cli/spec_check.py +0 -178
  60. package/template/.cowork-flow/scripts/services/spec_check.py +0 -573
  61. package/template/.cowork-flow/spec/contracts/spec-checks.md +0 -281
@@ -108,23 +108,6 @@ does not defend against a forged runtime record — writing
108
108
  `.cowork-flow/.runtime/**` is out of scope here and would need a signed or
109
109
  externally rooted record, which is a separate contract.
110
110
 
111
- ## Delegated Spec Self-Check
112
-
113
- Delegated subagent tool calls receive no edit-phase hook feedback on hosts
114
- whose plugin hooks cover only the main session stream (verified on zcode:
115
- plugin PostToolUse fires for main-session tool calls but never for
116
- Agent-tool children). The reliable pull path is the CLI: before reporting
117
- completion, `cowork-implement` (and any child that changes files) runs
118
-
119
- ```text
120
- ./.cowork-flow/run spec-check
121
- ```
122
-
123
- fixes every reported violation, and re-runs until the summary is clean or the
124
- remaining violations are reported as blockers. This is a protocol behavior,
125
- not a second lifecycle gate — the closeout gate re-runs the same checks and
126
- blocks completion regardless.
127
-
128
111
  ## Return Acceptance And Closeout
129
112
 
130
113
  - Converge on the child result before acceptance — via the wait primitive or
@@ -133,10 +116,12 @@ blocks completion regardless.
133
116
  - Confirm no stray running children with the adapter list primitive.
134
117
  - Verify the child report by checking files, commands, and results; do not
135
118
  trust completed text alone.
136
- - Before accepting delegated output, run `./.cowork-flow/run spec-check` in
137
- the main session and treat every violation as an acceptance blocker: the
138
- child receives no edit-phase hook feedback, so acceptance-time checking is
139
- the earliest reliable checkpoint for delegated work.
119
+ - Before accepting delegated output, verify the child's changed paths against
120
+ the task scope and the applicable `spec/` requirements from the diff
121
+ (`git diff --stat` / `task next <dir> --validate`): child sessions receive no
122
+ edit-phase hook feedback on hosts whose plugin hooks cover only the main
123
+ session stream, so acceptance-time review is the earliest reliable checkpoint
124
+ for delegated work.
140
125
  - After completion or failure, close the child with the adapter cancel/close
141
126
  primitive and clean the runtime context sessions.
142
127
  - The child remains a leaf executor and must not dispatch, wait for, list, or
@@ -74,7 +74,7 @@ that does not advance status); `check_result` is `null` (no check is exempted).
74
74
  ## Session binding
75
75
 
76
76
  On successful adoption the CLI binds the adopter's session to that task (the same
77
- shape as a rebind by `task next <dir>` or the binding after `task create`), so a
77
+ shape as a rebind by `task next <dir>` or the binding at task creation), so a
78
78
  later `task next <dir> --run` can omit the directory and continue the
79
79
  review/complete/archive chain.
80
80
 
@@ -115,19 +115,6 @@
115
115
  "when changing task start readiness",
116
116
  "when changing resume context for active tasks"
117
117
  ]
118
- },
119
- {
120
- "id": "SPEC_CHECKS_V1",
121
- "path": ".cowork-flow/spec/contracts/spec-checks.md",
122
- "digest": [
123
- "Specs may declare `checks:` commands in frontmatter; the mechanism runs declarations, never prose.",
124
- "A declared command that cannot be found counts as unchecked and blocks completion."
125
- ],
126
- "readWhen": [
127
- "before adding or editing spec files",
128
- "when a spec clause can be judged by a command exit code",
129
- "when spec-check reports violations or unchecked"
130
- ]
131
118
  }
132
119
  ],
133
120
  "references": [
@@ -155,7 +155,7 @@
155
155
  "runtimeContextDispatch": "native",
156
156
  "runtimeContextBinding": "shim",
157
157
  "runtimeContextCleanup": "native",
158
- "editScopeWarning": "unsupported"
158
+ "editScopeWarning": "plugin"
159
159
  },
160
160
  "commandTargets": [
161
161
  {
@@ -204,7 +204,7 @@
204
204
  "runtimeContextDispatch": "native",
205
205
  "runtimeContextBinding": "shim",
206
206
  "runtimeContextCleanup": "native",
207
- "editScopeWarning": "unsupported"
207
+ "editScopeWarning": "plugin"
208
208
  },
209
209
  "commandTargets": [],
210
210
  "payload": {
@@ -251,7 +251,7 @@
251
251
  "runtimeContextDispatch": "native",
252
252
  "runtimeContextBinding": "shim",
253
253
  "runtimeContextCleanup": "native",
254
- "editScopeWarning": "unsupported"
254
+ "editScopeWarning": "plugin"
255
255
  },
256
256
  "commandTargets": [
257
257
  {
@@ -300,7 +300,7 @@
300
300
  "runtimeContextDispatch": "native",
301
301
  "runtimeContextBinding": "shim",
302
302
  "runtimeContextCleanup": "shim",
303
- "editScopeWarning": "unsupported"
303
+ "editScopeWarning": "plugin"
304
304
  },
305
305
  "commandTargets": [],
306
306
  "payload": null
@@ -431,7 +431,7 @@
431
431
  "runtimeContextDispatch": "native",
432
432
  "runtimeContextBinding": "shim",
433
433
  "runtimeContextCleanup": "native",
434
- "editScopeWarning": "unsupported"
434
+ "editScopeWarning": "plugin"
435
435
  },
436
436
  "commandTargets": [],
437
437
  "payload": {
@@ -460,8 +460,8 @@
460
460
  ".cowork-flow/.version",
461
461
  ".cowork-flow/run",
462
462
  ".cowork-flow/run.cmd",
463
- ".cowork-flow/spec/contracts/spec-checks.md",
464
- ".cowork-flow/spec/contracts/workflow-state-templates.md"
463
+ ".cowork-flow/spec/contracts/workflow-state-templates.md",
464
+ ".cowork-flow/spec/runtime/scope-rules.json"
465
465
  ],
466
466
  "safePrefixes": [
467
467
  ".cowork-flow/"
@@ -515,6 +515,7 @@
515
515
  ".cowork-flow/scripts/application",
516
516
  ".cowork-flow/scripts/services/batch_execution.py",
517
517
  ".cowork-flow/scripts/services/decision_review.py",
518
+ ".cowork-flow/scripts/services/spec_check.py",
518
519
  ".cowork-flow/scripts/commands",
519
520
  ".cowork-flow/scripts/common",
520
521
  ".cowork-flow/scripts/common/__init__.py",
@@ -567,6 +568,7 @@
567
568
  ".cowork-flow/scripts/subagent.py",
568
569
  ".cowork-flow/scripts/task.py",
569
570
  ".cowork-flow/spec/contracts/entry-contract.md",
571
+ ".cowork-flow/spec/contracts/spec-checks.md",
570
572
  ".cowork-flow/spec/runtime/rules.json",
571
573
  ".cowork-flow/spec/schemas/rules.schema.json",
572
574
  ".cowork-flow/spec/runtime/skill-registry.json",
@@ -575,6 +577,7 @@
575
577
  ".cowork-flow/scripts/adapters/cli/add_session.py",
576
578
  ".cowork-flow/workspace/index.md",
577
579
  ".cowork-flow/scripts/adapters/cli/change.py",
580
+ ".cowork-flow/scripts/adapters/cli/spec_check.py",
578
581
  ".cowork-flow/changes/",
579
582
  ".cowork-flow/skills/adversarial-review",
580
583
  ".cowork-flow/skills/agent-dispatch",
@@ -8,9 +8,9 @@
8
8
  "trailingSlashRejectedTypes": ["planned-file", "deleted-file"]
9
9
  },
10
10
  "stageContract": {
11
- "budget": 1200,
11
+ "budget": 3400,
12
12
  "scopeLimit": 8,
13
13
  "specLimit": 4,
14
- "verifyLimit": 3
14
+ "verifyLimit": 10
15
15
  }
16
16
  }
@@ -1,11 +1,11 @@
1
1
  // Transport shim behind the opencode plugin. Every workflow fact
2
2
  // (contract digest, workflow-state, decision anchor, stage contract,
3
- // runtime-context bind) is rendered by the Python source
4
- // <scripts>/adapters/host/inject.py --host opencode; this module moves
5
- // bytes only. What stays here is what the Python side cannot reach:
6
- // the config-hook skills registration, the shell.env context-key
7
- // export, and the edit-phase spec-check the tool.execute.after hook
8
- // delegates to the project runtime.
3
+ // runtime-context bind, edit-scope warning) is rendered by the Python
4
+ // source <scripts>/adapters/host/inject.py --host opencode; this module
5
+ // moves bytes only. What stays here is what the Python side cannot
6
+ // reach: the config-hook skills registration, the shell.env context-key
7
+ // export, and the tool-shape translation for the tool.execute.after
8
+ // edit-scope delegation.
9
9
  //
10
10
  // This module must NOT live under plugins/: opencode
11
11
  // treats every file directly inside plugins/ as a plugin
@@ -175,8 +175,9 @@ function runInject(candidate, script, payload) {
175
175
  // inject.py and hand back whatever it printed. The payload spreads the
176
176
  // host input so every session/runtime-context key the Python identity
177
177
  // table knows arrives unchanged; hook_event_name and cwd are the two
178
- // fields the hook contract requires.
179
- async function renderViaInject(input) {
178
+ // fields the hook contract requires. `overrides` swaps the event name
179
+ // and adds the tool fields the PostToolUse contract needs.
180
+ async function renderViaInject(input, overrides = null) {
180
181
  if (
181
182
  process.env.COWORK_FLOW_HOOKS === "0" ||
182
183
  process.env.COWORK_FLOW_DISABLE_HOOKS === "1"
@@ -188,9 +189,11 @@ async function renderViaInject(input) {
188
189
  if (!script) {
189
190
  return ""
190
191
  }
192
+ const extra = overrides && typeof overrides === "object" ? overrides : {}
191
193
  const payload = {
192
194
  ...(input && typeof input === "object" ? input : {}),
193
- hook_event_name: "UserPromptSubmit",
195
+ ...extra,
196
+ hook_event_name: extra.hook_event_name || "UserPromptSubmit",
194
197
  cwd: inputCwd(input) || (root === pluginRoot ? null : root) || process.cwd(),
195
198
  }
196
199
  for (const candidate of pythonCandidates()) {
@@ -203,6 +206,34 @@ async function renderViaInject(input) {
203
206
  return ""
204
207
  }
205
208
 
209
+ // Editor-phase scope warning: opencode sessions get the same
210
+ // single-line out-of-scope advisory the other hosts receive on
211
+ // PostToolUse. Best-effort — a missing runtime, a timeout, or any
212
+ // error stays silent and never blocks the edit that already happened.
213
+ const EDIT_TOOL_NAMES = new Set(["edit", "write"])
214
+
215
+ function editedFilePath(input) {
216
+ const tool = typeof input?.tool === "string" ? input.tool : ""
217
+ if (!EDIT_TOOL_NAMES.has(tool)) {
218
+ return ""
219
+ }
220
+ const args = input?.args
221
+ const candidate = args && typeof args === "object" ? args.filePath : null
222
+ return typeof candidate === "string" && candidate.trim() ? candidate.trim() : ""
223
+ }
224
+
225
+ async function renderEditScopeWarning(input) {
226
+ const filePath = editedFilePath(input)
227
+ if (!filePath) {
228
+ return ""
229
+ }
230
+ return renderViaInject(input, {
231
+ hook_event_name: "PostToolUse",
232
+ tool_name: typeof input?.tool === "string" ? input.tool : "edit",
233
+ tool_input: { file_path: filePath },
234
+ })
235
+ }
236
+
206
237
  function sanitize(value) {
207
238
  return String(value ?? "")
208
239
  .trim()
@@ -262,82 +293,6 @@ function injectShellEnv(input, output) {
262
293
  }
263
294
  }
264
295
 
265
- // Editor-phase spec-check: opencode sessions get the same
266
- // single-line violation hint zcode and claude-code
267
- // receive. Best-effort — a missing runtime, a timeout, or
268
- // any error stays silent and never blocks the edit that
269
- // already happened.
270
- const EDIT_TOOL_NAMES = new Set(["edit", "write"])
271
- const EDIT_CHECK_TIMEOUT_MS = 2500
272
-
273
- function editedFilePath(input) {
274
- const tool = typeof input?.tool === "string" ? input.tool : ""
275
- if (!EDIT_TOOL_NAMES.has(tool)) {
276
- return ""
277
- }
278
- const args = input?.args
279
- const candidate = args && typeof args === "object" ? args.filePath : null
280
- return typeof candidate === "string" && candidate.trim() ? candidate.trim() : ""
281
- }
282
-
283
- function runEditSpecCheck(root, filePath) {
284
- return new Promise((resolvePromise) => {
285
- let settled = false
286
- const finish = (value) => {
287
- if (!settled) {
288
- settled = true
289
- resolvePromise(value)
290
- }
291
- }
292
- const entry = resolve(
293
- root,
294
- DIR_WORKFLOW,
295
- process.platform === "win32" ? "run.cmd" : "run"
296
- )
297
- if (!existsSync(entry)) {
298
- finish("")
299
- return
300
- }
301
- let child
302
- try {
303
- child = spawn(
304
- entry,
305
- ["spec-check", "--phase", "edit", "--file", filePath, "--throttled"],
306
- {
307
- cwd: root,
308
- stdio: ["ignore", "pipe", "ignore"],
309
- shell: process.platform === "win32",
310
- }
311
- )
312
- } catch {
313
- finish("")
314
- return
315
- }
316
- const timer = setTimeout(() => {
317
- try {
318
- child.kill()
319
- } catch {
320
- // the edit must not be held up by a stuck checker
321
- }
322
- finish("")
323
- }, EDIT_CHECK_TIMEOUT_MS)
324
- let stdout = ""
325
- if (child.stdout) {
326
- child.stdout.on("data", (chunk) => {
327
- stdout += String(chunk)
328
- })
329
- }
330
- child.on("error", () => {
331
- clearTimeout(timer)
332
- finish("")
333
- })
334
- child.on("close", () => {
335
- clearTimeout(timer)
336
- finish(stdout.trim())
337
- })
338
- })
339
- }
340
-
341
296
  // The payload's own skills directory, located from this
342
297
  // module's file path and not from the repository root: the
343
298
  // machine install puts this file in
@@ -382,11 +337,11 @@ export function registerPayloadSkills(config) {
382
337
 
383
338
  // The plugin adapter imports the hook helpers; these are
384
339
  // the unit-test surface (transport, fail-open, shell env,
385
- // edit-phase delegation). opencode never reads this module.
340
+ // edit-scope delegation). opencode never reads this module.
386
341
  export {
387
342
  renderViaInject,
388
343
  editedFilePath,
344
+ renderEditScopeWarning,
389
345
  findRepoRoot,
390
346
  injectShellEnv,
391
- runEditSpecCheck,
392
347
  }
@@ -7,12 +7,10 @@
7
7
  // re-exported helper would be called as a plugin and break
8
8
  // host bootstrap.
9
9
  import {
10
- editedFilePath,
11
- findRepoRoot,
12
10
  injectShellEnv,
13
11
  registerPayloadSkills,
14
- renderViaInject,
15
- runEditSpecCheck
12
+ renderEditScopeWarning,
13
+ renderViaInject
16
14
  } from "../cowork-flow/plugin-core.js"
17
15
 
18
16
  export const CoworkFlowPlugin = async () => {
@@ -37,12 +35,12 @@ export const CoworkFlowPlugin = async () => {
37
35
  output.system.push(text)
38
36
  }
39
37
  },
38
+ // Editor-phase scope advisory: the tool already ran, so
39
+ // the single out-of-scope line is appended to its output
40
+ // and the call is never blocked. Non-edit tools and a
41
+ // missing/failing runtime stay silent.
40
42
  "tool.execute.after": async (input, output) => {
41
- const filePath = editedFilePath(input)
42
- if (!filePath) {
43
- return
44
- }
45
- const warning = await runEditSpecCheck(findRepoRoot(input), filePath)
43
+ const warning = await renderEditScopeWarning(input)
46
44
  if (warning) {
47
45
  output.output = [output.output, warning].filter(Boolean).join("\n")
48
46
  }
@@ -144,5 +144,4 @@
144
144
 
145
145
  <!-- COWORK-FLOW:START -->
146
146
  项目流程以 `task next`、runtime gate、Skill command manifests 和 `.cowork-flow/spec/` 硬契约为准;不要维护第二套流程文档或 Skill 注册控制面。
147
- 规范声明的检查以 `.cowork-flow/spec/contracts/spec-checks.md` 为准;交付或收口前运行 `./.cowork-flow/run spec-check`。
148
147
  <!-- COWORK-FLOW:END -->
@@ -30,21 +30,6 @@ Use this Skill only inside fixed subagents such as `cowork-implement`,
30
30
  - Treat runtime gates as CLI adapters backed by kernel services; this Skill may
31
31
  explain when to run or report them, but does not enforce them.
32
32
 
33
- ## Spec Self-Check
34
-
35
- Delegated subagent tool calls receive no edit-phase hook feedback on hosts
36
- whose plugin hooks cover only the main session stream — a `spec-check[...]
37
- violation` line never reaches this session, even when the edit violates a
38
- declared check. Before reporting completion after any file change, run:
39
-
40
- ```text
41
- ./.cowork-flow/run spec-check
42
- ```
43
-
44
- Fix every reported violation and re-run until the summary is clean, or report
45
- the remaining violations as blockers. Honor any hook feedback the host does
46
- deliver; the self-check exists because delivery is not guaranteed.
47
-
48
33
  ## Role Outputs
49
34
 
50
35
  - `cowork-implement`: report changed files, acceptance IDs, verification
@@ -672,52 +672,6 @@ def check_runtime(repo_root: Path) -> list[str]:
672
672
  return errors
673
673
 
674
674
 
675
- def check_spec_checks(repo_root: Path) -> list[dict[str, str]]:
676
- """Spec-check declaration health: parse errors and command entry
677
- existence. Execution belongs to `run spec-check`; doctor only proves
678
- the declarations can run, before a completion gate finds out they
679
- cannot."""
680
- issues: list[dict[str, str]] = []
681
- import shutil
682
-
683
- try:
684
- from services.spec_check import collect_spec_declarations
685
- except Exception as error:
686
- return [
687
- {
688
- "kind": "spec-checks",
689
- "spec": "",
690
- "message": f"spec-check service unavailable: {error}",
691
- }
692
- ]
693
- for spec_decls in collect_spec_declarations(repo_root):
694
- for message in spec_decls.errors:
695
- issues.append(
696
- {"kind": "spec-checks", "spec": spec_decls.spec, "message": message}
697
- )
698
- for decl in spec_decls.decls:
699
- entry = (decl.cmd_win if sys.platform == "win32" else decl.cmd) or decl.cmd
700
- first_token = entry.split()[0] if entry.split() else ""
701
- if not first_token:
702
- issues.append(
703
- {
704
- "kind": "spec-checks",
705
- "spec": spec_decls.spec,
706
- "message": f"empty command declaration: {decl.cmd!r}",
707
- }
708
- )
709
- continue
710
- if shutil.which(first_token) is None:
711
- issues.append(
712
- {
713
- "kind": "spec-checks",
714
- "spec": spec_decls.spec,
715
- "message": f"command entry not found: {first_token}",
716
- }
717
- )
718
- return issues
719
-
720
-
721
675
  def _global_mcp_registered() -> bool:
722
676
  """Tolerant probe of user-level host configs for a cowork-flow MCP
723
677
  entry. Read-only; missing files simply mean "not registered"."""
@@ -1654,7 +1608,6 @@ def _all_check_result(repo_root: Path) -> dict[str, object]:
1654
1608
  task_hygiene_issues = check_task_hygiene(repo_root)
1655
1609
  state_recovery_issues = check_state_recovery(repo_root)
1656
1610
  session_hygiene_issues = check_session_hygiene(repo_root)
1657
- spec_check_issues = check_spec_checks(repo_root)
1658
1611
  mcp_issues = check_mcp_registration(repo_root)
1659
1612
  dsh_preset_issues = check_dsh_preset(repo_root)
1660
1613
  kimi_hook_issues = check_kimi_hook(repo_root)
@@ -1675,7 +1628,6 @@ def _all_check_result(repo_root: Path) -> dict[str, object]:
1675
1628
  {"kind": "distribution", "message": error}
1676
1629
  for error in distribution_errors
1677
1630
  )
1678
- errors.extend({"kind": "spec-checks", **issue} for issue in spec_check_issues)
1679
1631
  return {
1680
1632
  "ok": not errors,
1681
1633
  "errors": errors,
@@ -1684,7 +1636,6 @@ def _all_check_result(repo_root: Path) -> dict[str, object]:
1684
1636
  "taskHygiene": task_hygiene_issues,
1685
1637
  "stateRecovery": state_recovery_issues,
1686
1638
  "sessionHygiene": session_hygiene_issues,
1687
- "specChecks": spec_check_issues,
1688
1639
  "mcpRegistration": mcp_issues,
1689
1640
  "dshPreset": dsh_preset_issues,
1690
1641
  "kimiHook": kimi_hook_issues,
@@ -33,7 +33,6 @@ Keep each update concrete:
33
33
  - Show the contract or command shape.
34
34
  - Include good/bad cases when useful.
35
35
  - State tests or checks that protect the behavior.
36
- - When a clause is machine-checkable (可机检), add or refresh its `checks:` declaration in the same pass (main session only — never rewrite the clause text): the command must pass on a clean tree, a negative case must prove it can fail, `files` must narrow the scope, and a `spec-check` run after writing must confirm the declaration parses.
37
36
  - Update the matching `index.md` when adding a new topic.
38
37
  - Check for duplicate guidance and remove stale wording before finishing.
39
38
 
@@ -128,7 +128,7 @@ Then list task briefs, followed by integrated verification and completion checks
128
128
 
129
129
  - For Tiny tasks, keep the brief short but still name exact file boundaries, completion conditions, and verification commands.
130
130
  - For behavior changes, include a failing test before implementation when behavior can be tested.
131
- - For implementation tasks whose changed files are covered by spec `checks:` declarations, include the spec self-check in the task's Verification Command (for example `./.cowork-flow/run spec-check`): delegated subagents receive no edit-phase hook feedback on some hosts (zcode), so the explicit command is the reliable pull path for both self-check and acceptance.
131
+ - For implementation tasks whose changed files are covered by `.cowork-flow/spec/` requirements, name the covered spec files in the plan so review can check them clause by clause.
132
132
  - Do not add shallow tests just to satisfy process. Avoid tests that only assert existence, mirror implementation details, count mock calls without behavior, or snapshot empty structure.
133
133
  - For complex problems, test depth first: business invariants, cross-layer contracts, state transitions, error boundaries, and real regression paths before narrow unit cases.
134
134
  - Map behavior-changing tests to stable `decision-anchor.md` acceptance IDs when useful; do not create `tdd.jsonl` or write TDD evidence records into `check.jsonl`.
@@ -39,11 +39,11 @@ Read only what is needed for the current task:
39
39
  - **Tests**: test intent is explicit; tests fail for meaningful regressions, reject shallow tests such as existence/mock/snapshot-only checks, and cover boundary/error paths when relevant.
40
40
  - **Evidence coverage**: when the decision anchor declares acceptance criteria (`AC-xxx`), verify each one has an evidence record in `<task>/evidence.jsonl` — or a recorded `meta.evidenceExempt` exemption from an explicit `--allow-missing-evidence` completion. Schema and gate codes (`LIFECYCLE-AC-001..004`) are defined in `.cowork-flow/spec/contracts/evidence-completion.md`. A record must point at real proof (test, command output, or manual verification note); a record that restates the AC without evidence is a finding.
41
41
  - **Receipt freshness**: a strict task's completion gate requires the latest receipt to match the current change fingerprint, which covers the changed paths' content, the anchor's goal and AC text, the bound plan, `dev_type`, the referenced spec bodies, and `implement.jsonl`. Appending a scope entry to `implement.jsonl` — or editing a referenced spec body — therefore invalidates the receipt, so the review is re-run on the final state. That is the intended cost, not a defect. Evidence appends, AC ticks, and `.cowork-flow/.runtime/**` stay fingerprint-neutral, so closeout never invalidates what it just checked.
42
- - **User specs**: every applicable backend/frontend requirement is verified against the diff and marked `pass`, `finding`, or `not_applicable` with reason. Every `finding` is fixed during review; an unfixed spec violation blocks completion and is never accepted as-is or deferred to the user. For each requirement, also ask whether it is machine-checkable (可机检) and already declared with `checks:`; identifying checkable clauses and adding or refreshing declarations is a main-session duty (never rewrite the clause text — raise a conflict through the review instead), and a declared check must earn its place: the command passes on a clean tree, a negative case proves it can fail, `files` narrows the scope, and a `spec-check` run after declaring confirms the declaration parses. Delegated subagents do not edit specs; they report candidates up.
42
+ - **User specs**: every applicable backend/frontend requirement is verified against the diff and marked `pass`, `finding`, or `not_applicable` with reason. Every `finding` is fixed during review; an unfixed spec violation blocks completion and is never accepted as-is or deferred to the user. For each requirement, also ask whether it is machine-checkable (可机检) and name the command that decides it together with what that command reports when the clause is broken: a machine-checkable clause left with no deciding command is itself a finding. Review never rewrites the clause text — a conflict between a clause and the implementation is raised through the review for the user to decide. Delegated subagents do not edit specs; they report candidates up.
43
43
  - **Specs**: project specs are updated when behavior/contracts changed, or the review states why no spec update is needed.
44
44
  - **Code quality**: naming, layering, error handling, state boundaries, security-sensitive paths, and complexity are reviewed against applicable user specs.
45
45
  - **Lifecycle blockers**: state/scope blockers are fixed before acceptance; review does not invent hard blockers for natural-language specs.
46
- - **Delegated acceptance**: when the implementation came from a delegated subagent, run `./.cowork-flow/run spec-check` before accepting its report — delegated sessions receive no edit-phase hook feedback on some hosts (zcode), so the report can be silently non-compliant; every violation is fixed or blocks acceptance.
46
+ - **Delegated acceptance**: when the implementation came from a delegated subagent, verify the changed paths against the task scope and the applicable `spec/` requirements before accepting its report — delegated sessions receive no edit-phase hook feedback on some hosts, so the report can be silently out of scope or out of spec; every violation is fixed or blocks acceptance.
47
47
  - **Advisory facts**: helper output is used to focus review, not to declare pass/fail or block completion.
48
48
  - **Anti-self-proof**: every claimed pass maps to source, diff, test output, or lifecycle output.
49
49
  - **Anti-rationalization**: every accepted gap has an explicit user decision or narrow technical reason.
@@ -85,7 +85,7 @@ Return a concise review result with:
85
85
  - `status`: `pass`, `needs_fix`, or `blocked`.
86
86
  - `findings`: severity, file, line/scope, impact, fix, plus the `clause` / `command` / `negativeProof` triple defined in Review Report Format.
87
87
  - `test_intent_review`: why tests prove the intended behavior or what is missing.
88
- - `user_spec_review`: applicable spec files, per-requirement result, findings, and the "clause → command → negative proof" list for every `checks:` declaration added or refreshed in this review (`none` when nothing became machine-checkable).
88
+ - `user_spec_review`: applicable spec files, per-requirement result, findings, and the "clause → command → negative proof" list for every machine-checkable requirement the review judged (`none` when nothing was machine-checkable).
89
89
  - `lifecycle_check_review`: lifecycle commands run, blocker status, and resolution.
90
90
  - `verification`: exact commands run from this checkout.
91
91
  - `specUpdates`: files updated or reason no update was needed.