cowork-flow 0.0.41 → 0.0.43
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/README.md +1 -1
- package/package.json +1 -1
- package/src/commands/install-zcode-plugin.js +11 -0
- package/template/.claude/agents/cowork-check.md +13 -2
- package/template/.claude/agents/cowork-implement.md +15 -4
- package/template/.codex/agents/cowork-check.toml +12 -7
- package/template/.codex/agents/cowork-implement.toml +13 -6
- package/template/.cowork-flow/.version +1 -1
- package/template/.cowork-flow/scripts/application/batch_execution.py +21 -0
- package/template/.cowork-flow/scripts/application/task_context.py +18 -8
- package/template/.cowork-flow/scripts/commands/task_navigation.py +1 -1
- package/template/.cowork-flow/scripts/common/core/quality_sources.py +140 -0
- package/template/.cowork-flow/scripts/common/gates/anti_rationalization.py +1 -1
- package/template/.cowork-flow/scripts/common/gates/gates.py +55 -2
- package/template/.cowork-flow/scripts/common/gates/quality_review.py +332 -0
- package/template/.cowork-flow/scripts/common/gates/tdd_evidence.py +231 -71
- package/template/.cowork-flow/scripts/common/gates/test_intent.py +6 -30
- package/template/.cowork-flow/scripts/common/gates/validate_coding_standards.py +13 -5
- package/template/.cowork-flow/spec/contracts/decision-anchor.md +2 -2
- package/template/.cowork-flow/spec/protocols/review.md +20 -1
- package/template/.cowork-flow/spec/protocols/tdd.md +3 -3
- package/template/.cowork-flow/spec/references/definition-of-done.md +1 -1
- package/template/.cowork-flow/spec/runtime/host-assets.json +0 -2
- package/template/.cowork-flow/spec/runtime/skill-registry.json +25 -2
- package/template/.cowork-flow/workflow.md +4 -3
- package/template/.opencode/agents/cowork-check.md +6 -0
- package/template/.opencode/agents/cowork-implement.md +15 -3
- package/template/.zcode/.zcode-plugin/plugin.json +1 -1
- package/template/.zcode/scaffold/.cowork-flow/workflow.md +4 -3
- package/template/skills/batch-mode/SKILL.md +72 -59
- package/template/skills/tdd/SKILL.md +44 -0
- package/template/skills/writing-plans/SKILL.md +44 -17
- package/template/.zcode/scaffold/.cowork-flow/spec/backend/database-guidelines.md +0 -23
- package/template/.zcode/scaffold/.cowork-flow/spec/backend/directory-structure.md +0 -27
- package/template/.zcode/scaffold/.cowork-flow/spec/backend/encoding-guidelines.md +0 -27
- package/template/.zcode/scaffold/.cowork-flow/spec/backend/error-handling.md +0 -20
- package/template/.zcode/scaffold/.cowork-flow/spec/backend/index.md +0 -40
- package/template/.zcode/scaffold/.cowork-flow/spec/backend/logging-guidelines.md +0 -20
- package/template/.zcode/scaffold/.cowork-flow/spec/backend/quality-guidelines.md +0 -25
- package/template/.zcode/scaffold/.cowork-flow/spec/contracts/capabilities.md +0 -34
- package/template/.zcode/scaffold/.cowork-flow/spec/contracts/decision-anchor.md +0 -74
- package/template/.zcode/scaffold/.cowork-flow/spec/contracts/index.md +0 -11
- package/template/.zcode/scaffold/.cowork-flow/spec/contracts/party-mode-v2-board.md +0 -68
- package/template/.zcode/scaffold/.cowork-flow/spec/contracts/subagent-dispatch.md +0 -83
- package/template/.zcode/scaffold/.cowork-flow/spec/contracts/workflow-state-templates.md +0 -72
- package/template/.zcode/scaffold/.cowork-flow/spec/frontend/component-guidelines.md +0 -19
- package/template/.zcode/scaffold/.cowork-flow/spec/frontend/directory-structure.md +0 -18
- package/template/.zcode/scaffold/.cowork-flow/spec/frontend/hook-guidelines.md +0 -17
- package/template/.zcode/scaffold/.cowork-flow/spec/frontend/index.md +0 -39
- package/template/.zcode/scaffold/.cowork-flow/spec/frontend/quality-guidelines.md +0 -25
- package/template/.zcode/scaffold/.cowork-flow/spec/frontend/state-management.md +0 -18
- package/template/.zcode/scaffold/.cowork-flow/spec/frontend/type-safety.md +0 -18
- package/template/.zcode/scaffold/.cowork-flow/spec/game/asset-pipeline.md +0 -53
- package/template/.zcode/scaffold/.cowork-flow/spec/game/engine-guidelines.md +0 -43
- package/template/.zcode/scaffold/.cowork-flow/spec/game/index.md +0 -23
- package/template/.zcode/scaffold/.cowork-flow/spec/game/multiplayer-guidelines.md +0 -44
- package/template/.zcode/scaffold/.cowork-flow/spec/game/performance-guidelines.md +0 -58
- package/template/.zcode/scaffold/.cowork-flow/spec/guides/code-reuse-thinking-guide.md +0 -26
- package/template/.zcode/scaffold/.cowork-flow/spec/guides/cross-layer-thinking-guide.md +0 -21
- package/template/.zcode/scaffold/.cowork-flow/spec/guides/index.md +0 -31
- package/template/.zcode/scaffold/.cowork-flow/spec/guides/pre-implementation-checklist.md +0 -15
- package/template/.zcode/scaffold/.cowork-flow/spec/index.md +0 -15
- package/template/.zcode/scaffold/.cowork-flow/spec/runtime/contract-registry.json +0 -68
- package/template/.zcode/scaffold/.cowork-flow/spec/runtime/index.md +0 -10
- package/template/.zcode/scaffold/.cowork-flow/spec/runtime/rules.json +0 -176
- package/template/.zcode/scaffold/.cowork-flow/spec/schemas/adapter.schema.json +0 -128
- package/template/.zcode/scaffold/.cowork-flow/spec/schemas/index.md +0 -10
- package/template/.zcode/scaffold/.cowork-flow/spec/schemas/party-mode-v2-actions.schema.json +0 -90
- package/template/.zcode/scaffold/.cowork-flow/spec/schemas/rules.schema.json +0 -89
package/README.md
CHANGED
|
@@ -68,7 +68,7 @@ Skills 维护在 `template/skills/` 唯一源码,`init` 时按平台分发到
|
|
|
68
68
|
| `codex` / `opencode` | `.agents/skills/` |
|
|
69
69
|
| `claude-code` | `.claude/skills/` |
|
|
70
70
|
|
|
71
|
-
分发动作:`batch-mode`、`brainstorming`、`break-loop`、`cowork-flow`、`doubt-review`、`game-design`、`party-mode`、`writing-plans`
|
|
71
|
+
分发动作:`batch-mode`、`brainstorming`、`break-loop`、`cowork-flow`、`doubt-review`、`game-design`、`party-mode`、`tdd`、`writing-plans`
|
|
72
72
|
|
|
73
73
|
## CLI 命令
|
|
74
74
|
|
package/package.json
CHANGED
|
@@ -72,6 +72,7 @@ export async function runInstallZCodePlugin(args = []) {
|
|
|
72
72
|
const { dryRun, force } = parseArgs(args);
|
|
73
73
|
const pluginSrc = join(templateRoot, ".zcode");
|
|
74
74
|
const skillsSrc = join(templateRoot, "skills");
|
|
75
|
+
const specSrc = join(templateRoot, ".cowork-flow", "spec");
|
|
75
76
|
const { version } = await readPackageInfo();
|
|
76
77
|
|
|
77
78
|
if (!(await pathExists(pluginSrc))) {
|
|
@@ -80,6 +81,9 @@ export async function runInstallZCodePlugin(args = []) {
|
|
|
80
81
|
if (!(await pathExists(skillsSrc))) {
|
|
81
82
|
throw new Error(`Skills source missing at ${skillsSrc}. Reinstall cowork-flow.`);
|
|
82
83
|
}
|
|
84
|
+
if (!(await pathExists(specSrc))) {
|
|
85
|
+
throw new Error(`Spec source missing at ${specSrc}. Reinstall cowork-flow.`);
|
|
86
|
+
}
|
|
83
87
|
|
|
84
88
|
const cacheRoot = await getZCodeCacheDir();
|
|
85
89
|
const destDir = join(cacheRoot, version);
|
|
@@ -94,6 +98,7 @@ export async function runInstallZCodePlugin(args = []) {
|
|
|
94
98
|
console.log(`[dry-run] Would install ZCode plugin:`);
|
|
95
99
|
console.log(` Plugin: ${pluginSrc} -> ${destDir}`);
|
|
96
100
|
console.log(` Skills: ${skillsSrc} -> ${join(destDir, 'skills')}`);
|
|
101
|
+
console.log(` Spec: ${specSrc} -> ${join(destDir, 'scaffold', '.cowork-flow', 'spec')}`);
|
|
97
102
|
return;
|
|
98
103
|
}
|
|
99
104
|
|
|
@@ -110,6 +115,12 @@ export async function runInstallZCodePlugin(args = []) {
|
|
|
110
115
|
const scaffoldVersionPath = join(destDir, "scaffold", ".cowork-flow", ".version");
|
|
111
116
|
await writeFile(scaffoldVersionPath, `${version}\n`, "utf8");
|
|
112
117
|
|
|
118
|
+
// Materialize scaffold specs from the main template instead of maintaining
|
|
119
|
+
// a second static copy under template/.zcode/scaffold/.
|
|
120
|
+
const scaffoldSpecDest = join(destDir, "scaffold", ".cowork-flow", "spec");
|
|
121
|
+
await rm(scaffoldSpecDest, { recursive: true, force: true });
|
|
122
|
+
await cp(specSrc, scaffoldSpecDest, { recursive: true, force: true });
|
|
123
|
+
|
|
113
124
|
// Sync canonical scripts from main template (single source of truth).
|
|
114
125
|
// The .zcode/hooks/runtime/scripts/ copy is stale; overwrite with the
|
|
115
126
|
// authoritative version from template/.cowork-flow/scripts/.
|
|
@@ -38,8 +38,11 @@ Load context before checking:
|
|
|
38
38
|
2. Read `<task>/decision-anchor.md`.
|
|
39
39
|
3. Read the plan file linked from this task (check `<task>/task.json` `relatedFiles` for a plan path, or search `.cowork-flow/plans/` for a plan file referencing this task directory). Use the plan's Verify commands and Expected results as the checklist for each step.
|
|
40
40
|
4. Read `<task>/check.jsonl`.
|
|
41
|
-
5. Read `
|
|
42
|
-
6.
|
|
41
|
+
5. Read each JSONL `file` entry from `<task>/check.jsonl`.
|
|
42
|
+
6. Read `<task>/quality-review.jsonl` if present and verify machine warning,
|
|
43
|
+
checklist, and Definition of Done evidence.
|
|
44
|
+
7. Read `git diff`.
|
|
45
|
+
8. Include `test_intent_review` in the review output for meaningful behavior
|
|
43
46
|
breaks coverage and shallow tests rejection.
|
|
44
47
|
|
|
45
48
|
Authoritative internal protocols:
|
|
@@ -59,3 +62,11 @@ Rules:
|
|
|
59
62
|
- Report findings, changed files, and exact verification commands.
|
|
60
63
|
- Include test intent findings that map tests back to decision-anchor acceptance or
|
|
61
64
|
regression behavior.
|
|
65
|
+
- Run the deterministic coding gate before accepting quality:
|
|
66
|
+
`./.cowork-flow/run python .cowork-flow/scripts/common/gates/validate_coding_standards.py --validate --repo-root .`.
|
|
67
|
+
This gate enforces machine-decidable UTF-8/IO checks; backend/frontend
|
|
68
|
+
natural-language markdown remains checklist context, not dynamic hard validators.
|
|
69
|
+
- Treat machine warning output as review evidence: fix real issues or require
|
|
70
|
+
acknowledged warning records in `<task>/quality-review.jsonl`.
|
|
71
|
+
- Verify `<task>/quality-review.jsonl` covers Definition of Done, checklist
|
|
72
|
+
sources, affected files, and exact verification commands before acceptance.
|
|
@@ -36,19 +36,22 @@ the task.
|
|
|
36
36
|
Load context before editing:
|
|
37
37
|
1. Read the task directory from the bound runtime context.
|
|
38
38
|
2. Read `<task>/decision-anchor.md`.
|
|
39
|
-
3. Read .
|
|
39
|
+
3. Read `<task>/info.md` if present.
|
|
40
40
|
4. Read `<task>/implement.jsonl`.
|
|
41
41
|
5. Read each JSONL `file` entry.
|
|
42
42
|
6. Read the plan file linked from this task (check `<task>/task.json` `relatedFiles` for a plan path, or search `.cowork-flow/plans/` for a plan file referencing this task directory). Follow the plan steps in order — each step has Files, Action, Verify, and Expected fields.
|
|
43
|
-
7.
|
|
44
|
-
|
|
43
|
+
7. Read quality source entries from context; backend/frontend natural-language
|
|
44
|
+
specs are review checklists, not dynamic hard validators.
|
|
45
|
+
8. For behavior-change tasks, write the failing test before implementation and record any red/green evidence in `<task>/check.jsonl`, or record a documented exemption there.
|
|
46
|
+
9. Before reporting completion, write `<task>/quality-review.jsonl` with
|
|
47
|
+
checklist, machine warning, and Definition of Done evidence.
|
|
45
48
|
|
|
46
49
|
Authoritative internal protocols:
|
|
47
50
|
- `.cowork-flow/spec/protocols/tdd.md`
|
|
48
51
|
- `.cowork-flow/spec/protocols/decision-review.md`
|
|
49
52
|
- `.cowork-flow/spec/protocols/spec-maintenance.md`
|
|
50
53
|
|
|
51
|
-
Apply their output contracts exactly. Implementation reports and `
|
|
54
|
+
Apply their output contracts exactly. Implementation reports and TDD entries in `check.jsonl`
|
|
52
55
|
must preserve `acceptanceId`, `redCommand`, `redExitCode`, `greenCommand`,
|
|
53
56
|
`greenExitCode`, and `specUpdates`.
|
|
54
57
|
|
|
@@ -62,3 +65,11 @@ Rules:
|
|
|
62
65
|
include redCommand, redExitCode, greenCommand, greenExitCode, and
|
|
63
66
|
decision-anchor acceptance ID mapping.
|
|
64
67
|
- Report changed files and exact verification commands.
|
|
68
|
+
- Before editing source files, run the deterministic coding gate:
|
|
69
|
+
`./.cowork-flow/run python .cowork-flow/scripts/common/gates/validate_coding_standards.py --validate --repo-root .`.
|
|
70
|
+
This gate enforces machine-decidable UTF-8/IO checks; backend/frontend
|
|
71
|
+
natural-language markdown remains checklist context, not dynamic hard validators.
|
|
72
|
+
- Fix machine warning findings or record acknowledged warning evidence in
|
|
73
|
+
`<task>/quality-review.jsonl`.
|
|
74
|
+
- Record Definition of Done and quality checklist evidence in
|
|
75
|
+
`<task>/quality-review.jsonl`.
|
|
@@ -29,8 +29,11 @@ Load context before checking:
|
|
|
29
29
|
2. Read `<task>/decision-anchor.md`.
|
|
30
30
|
3. Read the plan file linked from this task (check `<task>/task.json` `relatedFiles` for a plan path, or search `.cowork-flow/plans/` for a plan file referencing this task directory). Use the plan's Verify commands and Expected results as the checklist for each step.
|
|
31
31
|
4. Read `<task>/check.jsonl`.
|
|
32
|
-
5. Read `
|
|
33
|
-
6.
|
|
32
|
+
5. Read each JSONL `file` entry from `<task>/check.jsonl`.
|
|
33
|
+
6. Read `<task>/quality-review.jsonl` if present and verify machine warning,
|
|
34
|
+
checklist, and Definition of Done evidence.
|
|
35
|
+
7. Read `git diff`.
|
|
36
|
+
8. Include `test_intent_review` in the review output for meaningful behavior breaks coverage and shallow tests rejection.
|
|
34
37
|
|
|
35
38
|
Authoritative internal protocols:
|
|
36
39
|
- `.cowork-flow/spec/protocols/review.md`
|
|
@@ -47,13 +50,15 @@ Rules:
|
|
|
47
50
|
- MUST NOT commit, archive, or mutate cowork-flow task state.
|
|
48
51
|
- Report findings, changed files, and exact verification commands.
|
|
49
52
|
- Include test intent findings that map tests back to decision-anchor acceptance criteria or regression behavior.
|
|
50
|
-
- BEFORE declaring decision-anchor acceptance passed, run the
|
|
53
|
+
- BEFORE declaring decision-anchor acceptance passed, run the deterministic coding gate and
|
|
51
54
|
treat a nonzero exit as a hard blocker:
|
|
52
55
|
./.cowork-flow/run python .cowork-flow/scripts/common/gates/validate_coding_standards.py --validate --repo-root .
|
|
53
|
-
This
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
This gate enforces machine-decidable UTF-8/IO checks. Backend/frontend
|
|
57
|
+
natural-language markdown remains checklist context, not dynamic hard validators.
|
|
58
|
+
- Treat machine warning output as review evidence: fix real issues or require
|
|
59
|
+
an acknowledged warning record in `<task>/quality-review.jsonl`.
|
|
60
|
+
- Verify `<task>/quality-review.jsonl` covers Definition of Done, checklist
|
|
61
|
+
sources, affected files, and exact verification commands before acceptance.
|
|
57
62
|
"""
|
|
58
63
|
|
|
59
64
|
[features]
|
|
@@ -31,14 +31,18 @@ Load context before editing:
|
|
|
31
31
|
4. Read `<task>/implement.jsonl`.
|
|
32
32
|
5. Read each JSONL `file` entry.
|
|
33
33
|
6. Read the plan file linked from this task (check `<task>/task.json` `relatedFiles` for a plan path, or search `.cowork-flow/plans/` for a plan file referencing this task directory). Follow the plan steps in order — each step has Files, Action, Verify, and Expected fields.
|
|
34
|
-
7.
|
|
34
|
+
7. Read quality source entries from context; backend/frontend natural-language
|
|
35
|
+
specs are review checklists, not dynamic hard validators.
|
|
36
|
+
8. For behavior-change tasks, write the failing test before implementation and record any red/green evidence in `<task>/check.jsonl`, or record a documented exemption there.
|
|
37
|
+
9. Before reporting completion, write `<task>/quality-review.jsonl` with
|
|
38
|
+
checklist, machine warning, and Definition of Done evidence.
|
|
35
39
|
|
|
36
40
|
Authoritative internal protocols:
|
|
37
41
|
- `.cowork-flow/spec/protocols/tdd.md`
|
|
38
42
|
- `.cowork-flow/spec/protocols/decision-review.md`
|
|
39
43
|
- `.cowork-flow/spec/protocols/spec-maintenance.md`
|
|
40
44
|
|
|
41
|
-
Apply their output contracts exactly. Implementation reports and `
|
|
45
|
+
Apply their output contracts exactly. Implementation reports and TDD entries in `check.jsonl`
|
|
42
46
|
must preserve `acceptanceId`, `redCommand`, `redExitCode`, `greenCommand`,
|
|
43
47
|
`greenExitCode`, and `specUpdates`.
|
|
44
48
|
|
|
@@ -49,12 +53,15 @@ Rules:
|
|
|
49
53
|
- Keep edits inside requested scope.
|
|
50
54
|
- Do not treat shallow tests as TDD evidence; red-green-refactor proof must include redCommand, redExitCode, greenCommand, greenExitCode, and decision-anchor acceptance ID mapping.
|
|
51
55
|
- Report changed files and exact verification commands.
|
|
52
|
-
- BEFORE editing any source file, run the
|
|
56
|
+
- BEFORE editing any source file, run the deterministic coding gate and treat a
|
|
53
57
|
nonzero exit as a hard blocker:
|
|
54
58
|
./.cowork-flow/run python .cowork-flow/scripts/common/gates/validate_coding_standards.py --validate --repo-root .
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
59
|
+
This gate enforces machine-decidable UTF-8/IO checks. Backend/frontend
|
|
60
|
+
natural-language markdown remains checklist context, not dynamic hard validators.
|
|
61
|
+
- Run or inspect machine warning output, fix real issues, and record any
|
|
62
|
+
acknowledged warning in `<task>/quality-review.jsonl`.
|
|
63
|
+
- Record Definition of Done coverage and quality checklist evidence in
|
|
64
|
+
`<task>/quality-review.jsonl`.
|
|
58
65
|
"""
|
|
59
66
|
|
|
60
67
|
[features]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.0.
|
|
1
|
+
0.0.43
|
|
@@ -25,6 +25,7 @@ BATCH_STEPS = (
|
|
|
25
25
|
"init_check_context",
|
|
26
26
|
"await_check_result",
|
|
27
27
|
"complete_task",
|
|
28
|
+
"archive_task",
|
|
28
29
|
"commit_task",
|
|
29
30
|
)
|
|
30
31
|
STEP_PHASES = {
|
|
@@ -33,12 +34,14 @@ STEP_PHASES = {
|
|
|
33
34
|
"review_task": "review",
|
|
34
35
|
"await_check_result": "check",
|
|
35
36
|
"complete_task": "complete",
|
|
37
|
+
"archive_task": "archive",
|
|
36
38
|
"commit_task": "commit",
|
|
37
39
|
}
|
|
38
40
|
LIFECYCLE_STATUSES = {
|
|
39
41
|
"start_task": "in_progress",
|
|
40
42
|
"review_task": "review",
|
|
41
43
|
"complete_task": "completed",
|
|
44
|
+
"archive_task": "completed",
|
|
42
45
|
}
|
|
43
46
|
RUNTIME_ROLES = {
|
|
44
47
|
"init_implement_context": "implement",
|
|
@@ -322,6 +325,9 @@ class BatchExecutionService:
|
|
|
322
325
|
f"commit could not be verified: {commit_id}",
|
|
323
326
|
)
|
|
324
327
|
return
|
|
328
|
+
if action_type == "archive_task":
|
|
329
|
+
self._verify_archive_result(task_name, result)
|
|
330
|
+
return
|
|
325
331
|
raise BatchExecutionError(
|
|
326
332
|
"BATCH-ACTION-UNKNOWN-001",
|
|
327
333
|
f"unsupported Batch action: {action_type}",
|
|
@@ -364,6 +370,21 @@ class BatchExecutionService:
|
|
|
364
370
|
f"expected {expected_status}",
|
|
365
371
|
)
|
|
366
372
|
|
|
373
|
+
def _verify_archive_result(
|
|
374
|
+
self,
|
|
375
|
+
task_name: str,
|
|
376
|
+
result: dict,
|
|
377
|
+
) -> None:
|
|
378
|
+
destination = self._optional_text(result, "archive_destination")
|
|
379
|
+
if destination is None:
|
|
380
|
+
return
|
|
381
|
+
archive_path = self.repo_root / destination
|
|
382
|
+
if not archive_path.exists():
|
|
383
|
+
raise BatchExecutionError(
|
|
384
|
+
"BATCH-ARCHIVE-VERIFY-001",
|
|
385
|
+
f"archive destination does not exist: {destination}",
|
|
386
|
+
)
|
|
387
|
+
|
|
367
388
|
def _verify_runtime_initialized(
|
|
368
389
|
self,
|
|
369
390
|
state: dict,
|
|
@@ -9,6 +9,10 @@ from dataclasses import dataclass
|
|
|
9
9
|
from pathlib import Path
|
|
10
10
|
|
|
11
11
|
from common.core.files import read_text_utf8
|
|
12
|
+
from common.core.quality_sources import (
|
|
13
|
+
quality_review_artifact_entry,
|
|
14
|
+
quality_source_entries,
|
|
15
|
+
)
|
|
12
16
|
from common.core.skill_registry import SkillRegistryError, load_skill_registry
|
|
13
17
|
from common.core.paths import (
|
|
14
18
|
DIR_AGENTS,
|
|
@@ -457,13 +461,15 @@ def get_check_context(repo_root: Path, dev_type: str) -> list[dict]:
|
|
|
457
461
|
"reason": "Workflow review, completion, and archive routing",
|
|
458
462
|
},
|
|
459
463
|
]
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
464
|
+
if dev_type == "spec":
|
|
465
|
+
entries.extend(
|
|
466
|
+
{
|
|
467
|
+
"file": spec_file,
|
|
468
|
+
"reason": f"Verify {Path(spec_file).name} compliance",
|
|
469
|
+
}
|
|
470
|
+
for spec_file in discover_spec_files(repo_root, dev_type)
|
|
471
|
+
)
|
|
472
|
+
entries.extend(quality_source_entries(repo_root, dev_type))
|
|
467
473
|
return entries
|
|
468
474
|
|
|
469
475
|
|
|
@@ -519,8 +525,12 @@ class TaskContextService:
|
|
|
519
525
|
"task directory does not exist",
|
|
520
526
|
)
|
|
521
527
|
|
|
528
|
+
implement_entries = self._implement_entries(dev_type)
|
|
529
|
+
implement_entries.append(
|
|
530
|
+
quality_review_artifact_entry(task_dir, self.repo_root)
|
|
531
|
+
)
|
|
522
532
|
entries_by_file = {
|
|
523
|
-
"implement.jsonl":
|
|
533
|
+
"implement.jsonl": implement_entries,
|
|
524
534
|
"check.jsonl": get_check_context(self.repo_root, dev_type),
|
|
525
535
|
"debug.jsonl": get_debug_context(dev_type, self.repo_root),
|
|
526
536
|
}
|
|
@@ -96,7 +96,7 @@ def _print_blockers(blockers: list[str]) -> None:
|
|
|
96
96
|
|
|
97
97
|
def _implementation(task_path: str) -> None:
|
|
98
98
|
print("Next action: execute implementation plan")
|
|
99
|
-
print(f"TDD reminder: for behavior changes, write
|
|
99
|
+
print(f"TDD reminder: for behavior changes, write the failing test first and record any red/green evidence in {task_path}/check.jsonl.")
|
|
100
100
|
print(
|
|
101
101
|
f"Command: ./.cowork-flow/run subagent init --role implement "
|
|
102
102
|
f"--agent-type cowork-implement --execution-task-dir {task_path} "
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Resolve precise quality context sources for review/check stages."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import re
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
QUALITY_REVIEW_FILE = "quality-review.jsonl"
|
|
11
|
+
SPEC_ROOT = ".cowork-flow/spec"
|
|
12
|
+
REFERENCE_DOD = f"{SPEC_ROOT}/references/definition-of-done.md"
|
|
13
|
+
REFERENCE_TESTING = f"{SPEC_ROOT}/references/testing-checklist.md"
|
|
14
|
+
REFERENCE_SECURITY = f"{SPEC_ROOT}/references/security-checklist.md"
|
|
15
|
+
|
|
16
|
+
_MARKDOWN_LINK_RE = re.compile(r"\[[^\]]+\]\(([^)#]+)(?:#[^)]+)?\)")
|
|
17
|
+
_SECURITY_HINT_RE = re.compile(
|
|
18
|
+
r"(?i)(auth|credential|jwt|oauth|password|permission|secret|security|session|token|\.env)"
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def quality_source_entries(
|
|
23
|
+
repo_root: Path,
|
|
24
|
+
dev_type: str,
|
|
25
|
+
*,
|
|
26
|
+
paths: tuple[str, ...] = (),
|
|
27
|
+
) -> list[dict]:
|
|
28
|
+
"""Return narrow quality/spec sources a checker should read."""
|
|
29
|
+
entries: list[dict] = []
|
|
30
|
+
seen: set[str] = set()
|
|
31
|
+
for source in quality_source_paths(repo_root, dev_type, paths=paths):
|
|
32
|
+
if source in seen:
|
|
33
|
+
continue
|
|
34
|
+
seen.add(source)
|
|
35
|
+
entries.append(
|
|
36
|
+
{
|
|
37
|
+
"file": source,
|
|
38
|
+
"reason": _quality_source_reason(source),
|
|
39
|
+
}
|
|
40
|
+
)
|
|
41
|
+
return entries
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def quality_source_paths(
|
|
45
|
+
repo_root: Path,
|
|
46
|
+
dev_type: str,
|
|
47
|
+
*,
|
|
48
|
+
paths: tuple[str, ...] = (),
|
|
49
|
+
) -> list[str]:
|
|
50
|
+
"""Return existing repo-relative quality source paths."""
|
|
51
|
+
root = Path(repo_root)
|
|
52
|
+
sources: list[str] = []
|
|
53
|
+
for category in _categories_for(dev_type):
|
|
54
|
+
index = f"{SPEC_ROOT}/{category}/index.md"
|
|
55
|
+
sources.extend(_existing_paths(root, (index,)))
|
|
56
|
+
sources.extend(_linked_markdown_sources(root, index, category))
|
|
57
|
+
|
|
58
|
+
sources.extend(_existing_paths(root, (REFERENCE_DOD, REFERENCE_TESTING)))
|
|
59
|
+
if _security_relevant(paths):
|
|
60
|
+
sources.extend(_existing_paths(root, (REFERENCE_SECURITY,)))
|
|
61
|
+
return sources
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def quality_review_artifact_entry(task_dir: Path, repo_root: Path) -> dict:
|
|
65
|
+
"""Return the task-local quality-review artifact context entry."""
|
|
66
|
+
task_path = _repo_relative_task_path(task_dir, repo_root)
|
|
67
|
+
return {
|
|
68
|
+
"file": f"{task_path}/{QUALITY_REVIEW_FILE}",
|
|
69
|
+
"reason": "Task-local quality review evidence required before complete",
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _categories_for(dev_type: str) -> tuple[str, ...]:
|
|
74
|
+
if dev_type == "backend":
|
|
75
|
+
return ("backend",)
|
|
76
|
+
if dev_type == "frontend":
|
|
77
|
+
return ("frontend",)
|
|
78
|
+
if dev_type == "fullstack":
|
|
79
|
+
return ("backend", "frontend")
|
|
80
|
+
return ()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _linked_markdown_sources(
|
|
84
|
+
repo_root: Path,
|
|
85
|
+
index_path: str,
|
|
86
|
+
category: str,
|
|
87
|
+
) -> list[str]:
|
|
88
|
+
index = repo_root / index_path
|
|
89
|
+
if not index.is_file():
|
|
90
|
+
return []
|
|
91
|
+
try:
|
|
92
|
+
text = index.read_text(encoding="utf-8")
|
|
93
|
+
except (OSError, UnicodeDecodeError):
|
|
94
|
+
return []
|
|
95
|
+
|
|
96
|
+
sources: list[str] = []
|
|
97
|
+
for match in _MARKDOWN_LINK_RE.finditer(text):
|
|
98
|
+
target = match.group(1).strip()
|
|
99
|
+
if "://" in target or target.startswith("#"):
|
|
100
|
+
continue
|
|
101
|
+
normalized = _normalize_markdown_target(category, target)
|
|
102
|
+
if normalized is not None:
|
|
103
|
+
sources.extend(_existing_paths(repo_root, (normalized,)))
|
|
104
|
+
return sources
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _normalize_markdown_target(category: str, target: str) -> str | None:
|
|
108
|
+
without_anchor = target.split("#", 1)[0].strip()
|
|
109
|
+
if not without_anchor.endswith(".md"):
|
|
110
|
+
return None
|
|
111
|
+
if without_anchor.startswith("./"):
|
|
112
|
+
without_anchor = without_anchor[2:]
|
|
113
|
+
if without_anchor.startswith("../") or without_anchor.startswith("/"):
|
|
114
|
+
return None
|
|
115
|
+
return f"{SPEC_ROOT}/{category}/{without_anchor}"
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _existing_paths(repo_root: Path, paths: tuple[str, ...]) -> list[str]:
|
|
119
|
+
return [path for path in paths if (repo_root / path).is_file()]
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _security_relevant(paths: tuple[str, ...]) -> bool:
|
|
123
|
+
return any(_SECURITY_HINT_RE.search(path) is not None for path in paths)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _quality_source_reason(source: str) -> str:
|
|
127
|
+
if source == REFERENCE_DOD:
|
|
128
|
+
return "Verify Definition of Done evidence"
|
|
129
|
+
if source == REFERENCE_TESTING:
|
|
130
|
+
return "Verify test depth and anti-shallow-test coverage"
|
|
131
|
+
if source == REFERENCE_SECURITY:
|
|
132
|
+
return "Verify security-sensitive changes"
|
|
133
|
+
return f"Verify {Path(source).name} quality rules"
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _repo_relative_task_path(task_dir: Path, repo_root: Path) -> str:
|
|
137
|
+
try:
|
|
138
|
+
return Path(task_dir).resolve().relative_to(Path(repo_root).resolve()).as_posix()
|
|
139
|
+
except ValueError:
|
|
140
|
+
return Path(task_dir).as_posix()
|
|
@@ -43,7 +43,7 @@ def validate_exemption_rationalization(reason: str) -> list[dict]:
|
|
|
43
43
|
f"'{pattern}'. Replace with a concrete behavior test or "
|
|
44
44
|
f"specific verification command."
|
|
45
45
|
),
|
|
46
|
-
"file": "<task>/
|
|
46
|
+
"file": "<task>/check.jsonl",
|
|
47
47
|
"fix_hint": (
|
|
48
48
|
"Remove rationalization from exemption reason. "
|
|
49
49
|
"Either write a failing behavior test for this AC "
|
|
@@ -25,6 +25,7 @@ STAGE_GATES = {
|
|
|
25
25
|
"test_intent",
|
|
26
26
|
"runtime_rules",
|
|
27
27
|
"coding_standards",
|
|
28
|
+
"quality_machine_checks",
|
|
28
29
|
"complexity",
|
|
29
30
|
),
|
|
30
31
|
"task_complete": (
|
|
@@ -33,12 +34,14 @@ STAGE_GATES = {
|
|
|
33
34
|
"test_intent",
|
|
34
35
|
"runtime_rules",
|
|
35
36
|
"coding_standards",
|
|
37
|
+
"quality_machine_checks",
|
|
38
|
+
"quality_review",
|
|
36
39
|
"complexity",
|
|
37
40
|
),
|
|
38
41
|
}
|
|
39
42
|
|
|
40
43
|
|
|
41
|
-
def
|
|
44
|
+
def _core_validator_bindings() -> tuple[ValidatorBinding, ...]:
|
|
42
45
|
return (
|
|
43
46
|
ValidatorBinding(
|
|
44
47
|
key="runtime_rules",
|
|
@@ -73,6 +76,30 @@ def _validator_bindings() -> tuple[ValidatorBinding, ...]:
|
|
|
73
76
|
function="validate_coding_standards",
|
|
74
77
|
positional=("repo_root", "task_dir"),
|
|
75
78
|
),
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _quality_validator_bindings() -> tuple[ValidatorBinding, ...]:
|
|
83
|
+
return (
|
|
84
|
+
ValidatorBinding(
|
|
85
|
+
key="quality_machine_checks",
|
|
86
|
+
module="validate_coding_standards",
|
|
87
|
+
function="validate_machine_checks",
|
|
88
|
+
positional=("repo_root", "task_dir"),
|
|
89
|
+
),
|
|
90
|
+
ValidatorBinding(
|
|
91
|
+
key="quality_review",
|
|
92
|
+
module="quality_review",
|
|
93
|
+
function="validate_quality_review",
|
|
94
|
+
positional=("repo_root", "task_dir"),
|
|
95
|
+
),
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _validator_bindings() -> tuple[ValidatorBinding, ...]:
|
|
100
|
+
return (
|
|
101
|
+
*_core_validator_bindings(),
|
|
102
|
+
*_quality_validator_bindings(),
|
|
76
103
|
ValidatorBinding(
|
|
77
104
|
key="complexity",
|
|
78
105
|
module="validate_coding_standards",
|
|
@@ -82,7 +109,7 @@ def _validator_bindings() -> tuple[ValidatorBinding, ...]:
|
|
|
82
109
|
)
|
|
83
110
|
|
|
84
111
|
|
|
85
|
-
def
|
|
112
|
+
def _core_gate_definitions() -> tuple[GateDefinition, ...]:
|
|
86
113
|
return (
|
|
87
114
|
GateDefinition(
|
|
88
115
|
id="runtime_rules",
|
|
@@ -103,6 +130,7 @@ def _gate_definitions() -> tuple[GateDefinition, ...]:
|
|
|
103
130
|
validator_key="tdd_evidence",
|
|
104
131
|
required=True,
|
|
105
132
|
block_message="TDD evidence gate blocked lifecycle transition",
|
|
133
|
+
warning_message="TDD evidence advisories",
|
|
106
134
|
),
|
|
107
135
|
GateDefinition(
|
|
108
136
|
id="test_intent",
|
|
@@ -117,6 +145,31 @@ def _gate_definitions() -> tuple[GateDefinition, ...]:
|
|
|
117
145
|
required=True,
|
|
118
146
|
block_message="Coding standards gate blocked lifecycle transition",
|
|
119
147
|
),
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _quality_gate_definitions() -> tuple[GateDefinition, ...]:
|
|
152
|
+
return (
|
|
153
|
+
GateDefinition(
|
|
154
|
+
id="quality_machine_checks",
|
|
155
|
+
validator_key="quality_machine_checks",
|
|
156
|
+
required=False,
|
|
157
|
+
block_message="",
|
|
158
|
+
warning_message="Quality machine-check warnings",
|
|
159
|
+
),
|
|
160
|
+
GateDefinition(
|
|
161
|
+
id="quality_review",
|
|
162
|
+
validator_key="quality_review",
|
|
163
|
+
required=True,
|
|
164
|
+
block_message="Quality review gate blocked lifecycle transition",
|
|
165
|
+
),
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _gate_definitions() -> tuple[GateDefinition, ...]:
|
|
170
|
+
return (
|
|
171
|
+
*_core_gate_definitions(),
|
|
172
|
+
*_quality_gate_definitions(),
|
|
120
173
|
GateDefinition(
|
|
121
174
|
id="complexity",
|
|
122
175
|
validator_key="complexity",
|