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.
- package/CHANGELOG.md +23 -1
- package/docs/architecture.md +1 -34
- package/docs/direction.md +1 -1
- package/docs/hosts.md +13 -9
- package/package.json +1 -1
- package/presets/claude-code/.claude-plugin/plugin.json +1 -1
- package/presets/codex/.codex-plugin/plugin.json +1 -1
- package/presets/dsh/plugins/workflow-state.js +31 -16
- package/presets/kimi-code/.kimi-plugin/plugin.json +1 -1
- package/presets/opencode/cowork-flow/plugin-core.js +42 -87
- package/presets/opencode/plugins/cowork-flow.js +7 -9
- package/presets/qoder/.qoder-plugin/plugin.json +1 -1
- package/presets/qoder/hooks/hooks.json +1 -1
- package/presets/zcode/.zcode-plugin/plugin.json +1 -1
- package/src/commands/host.js +23 -2
- package/src/commands/registry.js +4 -2
- package/template/.cowork-flow/.version +1 -1
- package/template/.cowork-flow/adapters/claude-code/adapter.yaml +1 -1
- package/template/.cowork-flow/adapters/codex/adapter.yaml +1 -1
- package/template/.cowork-flow/adapters/dsh/adapter.yaml +1 -1
- package/template/.cowork-flow/adapters/opencode/adapter.yaml +1 -1
- package/template/.cowork-flow/adapters/qoder/adapter.yaml +1 -1
- package/template/.cowork-flow/scripts/adapters/cli/task_lifecycle_commands.py +0 -1
- package/template/.cowork-flow/scripts/adapters/cli/task_parser.py +0 -7
- package/template/.cowork-flow/scripts/adapters/host/claude_code_policy.py +6 -4
- package/template/.cowork-flow/scripts/adapters/host/codex_policy.py +8 -6
- package/template/.cowork-flow/scripts/adapters/host/edit_scope.py +113 -0
- package/template/.cowork-flow/scripts/adapters/host/opencode_policy.py +12 -0
- package/template/.cowork-flow/scripts/adapters/host/qoder_policy.py +7 -5
- package/template/.cowork-flow/scripts/adapters/host/workflow_state_hook.py +0 -91
- package/template/.cowork-flow/scripts/adapters/host/zcode_policy.py +15 -77
- package/template/.cowork-flow/scripts/run.py +0 -2
- package/template/.cowork-flow/scripts/services/context_paths.py +2 -2
- package/template/.cowork-flow/scripts/services/fact_view.py +49 -21
- package/template/.cowork-flow/scripts/services/lifecycle_checks.py +1 -216
- package/template/.cowork-flow/scripts/services/lifecycle_policy.py +0 -5
- package/template/.cowork-flow/scripts/services/task_gate_profiles.py +1 -1
- package/template/.cowork-flow/scripts/services/task_lifecycle.py +14 -31
- package/template/.cowork-flow/spec/backend/error-handling.md +0 -14
- package/template/.cowork-flow/spec/contracts/context-injection.md +49 -22
- package/template/.cowork-flow/spec/contracts/decision-anchor.md +3 -3
- package/template/.cowork-flow/spec/contracts/error-codes.md +1 -3
- package/template/.cowork-flow/spec/contracts/evidence-completion.md +6 -8
- package/template/.cowork-flow/spec/contracts/index.md +0 -1
- package/template/.cowork-flow/spec/contracts/plan-binding.md +3 -2
- package/template/.cowork-flow/spec/contracts/subagent-dispatch.md +6 -21
- package/template/.cowork-flow/spec/contracts/task-adopt.md +1 -1
- package/template/.cowork-flow/spec/runtime/contract-registry.json +0 -13
- package/template/.cowork-flow/spec/runtime/host-assets.json +10 -7
- package/template/.cowork-flow/spec/runtime/scope-rules.json +2 -2
- package/template/.opencode/cowork-flow/plugin-core.js +42 -87
- package/template/.opencode/plugins/cowork-flow.js +7 -9
- package/template/AGENTS.md +0 -1
- package/template/skills/agent-dispatch/SKILL.md +0 -15
- package/template/skills/runtime-health/scripts/doctor.py +0 -49
- package/template/skills/spec-sync/SKILL.md +0 -1
- package/template/skills/task-planning/SKILL.md +1 -1
- package/template/skills/task-review/SKILL.md +3 -3
- package/template/.cowork-flow/scripts/adapters/cli/spec_check.py +0 -178
- package/template/.cowork-flow/scripts/services/spec_check.py +0 -573
- 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,
|
|
137
|
-
the
|
|
138
|
-
|
|
139
|
-
|
|
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
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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/
|
|
464
|
-
".cowork-flow/spec/
|
|
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",
|
|
@@ -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
|
|
4
|
-
// <scripts>/adapters/host/inject.py --host opencode; this module
|
|
5
|
-
// bytes only. What stays here is what the Python side cannot
|
|
6
|
-
// the config-hook skills registration, the shell.env context-key
|
|
7
|
-
// export, and the
|
|
8
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
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
|
-
|
|
15
|
-
|
|
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
|
|
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
|
}
|
package/template/AGENTS.md
CHANGED
|
@@ -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
|
|
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
|
|
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,
|
|
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
|
|
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.
|