cowork-flow 0.0.40 → 0.0.42

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 (60) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/src/commands/install-zcode-plugin.js +11 -0
  4. package/template/.claude/agents/cowork-check.md +13 -2
  5. package/template/.claude/agents/cowork-implement.md +14 -2
  6. package/template/.codex/agents/cowork-check.toml +12 -7
  7. package/template/.codex/agents/cowork-implement.toml +12 -5
  8. package/template/.cowork-flow/.version +1 -1
  9. package/template/.cowork-flow/scripts/application/task_context.py +18 -8
  10. package/template/.cowork-flow/scripts/common/core/quality_sources.py +140 -0
  11. package/template/.cowork-flow/scripts/common/gates/gates.py +54 -2
  12. package/template/.cowork-flow/scripts/common/gates/quality_review.py +332 -0
  13. package/template/.cowork-flow/scripts/common/gates/validate_coding_standards.py +13 -5
  14. package/template/.cowork-flow/spec/protocols/decision-review.md +28 -1
  15. package/template/.cowork-flow/spec/protocols/review.md +20 -1
  16. package/template/.cowork-flow/spec/protocols/spec-maintenance.md +19 -0
  17. package/template/.cowork-flow/spec/protocols/tdd.md +13 -0
  18. package/template/.cowork-flow/spec/runtime/host-assets.json +0 -2
  19. package/template/.cowork-flow/spec/runtime/skill-registry.json +29 -1
  20. package/template/.opencode/agents/cowork-check.md +6 -0
  21. package/template/.opencode/agents/cowork-implement.md +14 -2
  22. package/template/.zcode/.zcode-plugin/plugin.json +1 -1
  23. package/template/skills/doubt-review/SKILL.md +125 -0
  24. package/template/.zcode/scaffold/.cowork-flow/spec/backend/database-guidelines.md +0 -23
  25. package/template/.zcode/scaffold/.cowork-flow/spec/backend/directory-structure.md +0 -27
  26. package/template/.zcode/scaffold/.cowork-flow/spec/backend/encoding-guidelines.md +0 -27
  27. package/template/.zcode/scaffold/.cowork-flow/spec/backend/error-handling.md +0 -20
  28. package/template/.zcode/scaffold/.cowork-flow/spec/backend/index.md +0 -40
  29. package/template/.zcode/scaffold/.cowork-flow/spec/backend/logging-guidelines.md +0 -20
  30. package/template/.zcode/scaffold/.cowork-flow/spec/backend/quality-guidelines.md +0 -25
  31. package/template/.zcode/scaffold/.cowork-flow/spec/contracts/capabilities.md +0 -34
  32. package/template/.zcode/scaffold/.cowork-flow/spec/contracts/decision-anchor.md +0 -74
  33. package/template/.zcode/scaffold/.cowork-flow/spec/contracts/index.md +0 -11
  34. package/template/.zcode/scaffold/.cowork-flow/spec/contracts/party-mode-v2-board.md +0 -68
  35. package/template/.zcode/scaffold/.cowork-flow/spec/contracts/subagent-dispatch.md +0 -83
  36. package/template/.zcode/scaffold/.cowork-flow/spec/contracts/workflow-state-templates.md +0 -72
  37. package/template/.zcode/scaffold/.cowork-flow/spec/frontend/component-guidelines.md +0 -19
  38. package/template/.zcode/scaffold/.cowork-flow/spec/frontend/directory-structure.md +0 -18
  39. package/template/.zcode/scaffold/.cowork-flow/spec/frontend/hook-guidelines.md +0 -17
  40. package/template/.zcode/scaffold/.cowork-flow/spec/frontend/index.md +0 -39
  41. package/template/.zcode/scaffold/.cowork-flow/spec/frontend/quality-guidelines.md +0 -25
  42. package/template/.zcode/scaffold/.cowork-flow/spec/frontend/state-management.md +0 -18
  43. package/template/.zcode/scaffold/.cowork-flow/spec/frontend/type-safety.md +0 -18
  44. package/template/.zcode/scaffold/.cowork-flow/spec/game/asset-pipeline.md +0 -53
  45. package/template/.zcode/scaffold/.cowork-flow/spec/game/engine-guidelines.md +0 -43
  46. package/template/.zcode/scaffold/.cowork-flow/spec/game/index.md +0 -23
  47. package/template/.zcode/scaffold/.cowork-flow/spec/game/multiplayer-guidelines.md +0 -44
  48. package/template/.zcode/scaffold/.cowork-flow/spec/game/performance-guidelines.md +0 -58
  49. package/template/.zcode/scaffold/.cowork-flow/spec/guides/code-reuse-thinking-guide.md +0 -26
  50. package/template/.zcode/scaffold/.cowork-flow/spec/guides/cross-layer-thinking-guide.md +0 -21
  51. package/template/.zcode/scaffold/.cowork-flow/spec/guides/index.md +0 -31
  52. package/template/.zcode/scaffold/.cowork-flow/spec/guides/pre-implementation-checklist.md +0 -15
  53. package/template/.zcode/scaffold/.cowork-flow/spec/index.md +0 -15
  54. package/template/.zcode/scaffold/.cowork-flow/spec/runtime/contract-registry.json +0 -68
  55. package/template/.zcode/scaffold/.cowork-flow/spec/runtime/index.md +0 -10
  56. package/template/.zcode/scaffold/.cowork-flow/spec/runtime/rules.json +0 -176
  57. package/template/.zcode/scaffold/.cowork-flow/spec/schemas/adapter.schema.json +0 -128
  58. package/template/.zcode/scaffold/.cowork-flow/spec/schemas/index.md +0 -10
  59. package/template/.zcode/scaffold/.cowork-flow/spec/schemas/party-mode-v2-actions.schema.json +0 -90
  60. 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`、`party-mode`、`writing-plans`
71
+ 分发动作:`batch-mode`、`brainstorming`、`break-loop`、`cowork-flow`、`doubt-review`、`game-design`、`party-mode`、`writing-plans`
72
72
 
73
73
  ## CLI 命令
74
74
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cowork-flow",
3
- "version": "0.0.40",
3
+ "version": "0.0.42",
4
4
  "description": "CLI for installing and maintaining cowork-flow collaboration templates.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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 `git diff`.
42
- 6. Include `test_intent_review` in the review output for meaningful behavior
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,12 +36,16 @@ 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. For behavior-change tasks, create or verify `<task>/tdd.jsonl` before
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, create or verify `<task>/tdd.jsonl` before
44
46
  implementation or record a documented exemption.
47
+ 9. Before reporting completion, write `<task>/quality-review.jsonl` with
48
+ checklist, machine warning, and Definition of Done evidence.
45
49
 
46
50
  Authoritative internal protocols:
47
51
  - `.cowork-flow/spec/protocols/tdd.md`
@@ -62,3 +66,11 @@ Rules:
62
66
  include redCommand, redExitCode, greenCommand, greenExitCode, and
63
67
  decision-anchor acceptance ID mapping.
64
68
  - Report changed files and exact verification commands.
69
+ - Before editing source files, run the deterministic coding gate:
70
+ `./.cowork-flow/run python .cowork-flow/scripts/common/gates/validate_coding_standards.py --validate --repo-root .`.
71
+ This gate enforces machine-decidable UTF-8/IO checks; backend/frontend
72
+ natural-language markdown remains checklist context, not dynamic hard validators.
73
+ - Fix machine warning findings or record acknowledged warning evidence in
74
+ `<task>/quality-review.jsonl`.
75
+ - Record Definition of Done and quality checklist evidence in
76
+ `<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 `git diff`.
33
- 6. Include `test_intent_review` in the review output for meaningful behavior breaks coverage and shallow tests rejection.
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 spec-driven coding gate and
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 naturally enforces every rule the user added to
54
- .cowork-flow/spec/backend/*.md and spec/frontend/*.md — they are not just
55
- documentation but active validators. On violation, either fix the code or
56
- explicitly discard the rule from the spec; do not skip the gate.
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,7 +31,11 @@ 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. For behavior-change tasks, create or verify `<task>/tdd.jsonl` before implementation or record a documented exemption.
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, create or verify `<task>/tdd.jsonl` before implementation or record a documented exemption.
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`
@@ -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 spec-driven coding gate and treat a
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
- Parse every *.md under .cowork-flow/spec/backend and spec/frontend; rules
56
- declared there activate validators dynamically. On violation, fix the code
57
- (do not disable the rule) and re-run until exit code 0.
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.40
1
+ 0.0.42
@@ -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
- entries.extend(
461
- {
462
- "file": spec_file,
463
- "reason": f"Verify {Path(spec_file).name} compliance",
464
- }
465
- for spec_file in discover_spec_files(repo_root, dev_type)
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": self._implement_entries(dev_type),
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
  }
@@ -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()
@@ -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 _validator_bindings() -> tuple[ValidatorBinding, ...]:
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 _gate_definitions() -> tuple[GateDefinition, ...]:
112
+ def _core_gate_definitions() -> tuple[GateDefinition, ...]:
86
113
  return (
87
114
  GateDefinition(
88
115
  id="runtime_rules",
@@ -117,6 +144,31 @@ def _gate_definitions() -> tuple[GateDefinition, ...]:
117
144
  required=True,
118
145
  block_message="Coding standards gate blocked lifecycle transition",
119
146
  ),
147
+ )
148
+
149
+
150
+ def _quality_gate_definitions() -> tuple[GateDefinition, ...]:
151
+ return (
152
+ GateDefinition(
153
+ id="quality_machine_checks",
154
+ validator_key="quality_machine_checks",
155
+ required=False,
156
+ block_message="",
157
+ warning_message="Quality machine-check warnings",
158
+ ),
159
+ GateDefinition(
160
+ id="quality_review",
161
+ validator_key="quality_review",
162
+ required=True,
163
+ block_message="Quality review gate blocked lifecycle transition",
164
+ ),
165
+ )
166
+
167
+
168
+ def _gate_definitions() -> tuple[GateDefinition, ...]:
169
+ return (
170
+ *_core_gate_definitions(),
171
+ *_quality_gate_definitions(),
120
172
  GateDefinition(
121
173
  id="complexity",
122
174
  validator_key="complexity",