cowork-flow 0.0.41 → 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.
- 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 +14 -2
- package/template/.codex/agents/cowork-check.toml +12 -7
- package/template/.codex/agents/cowork-implement.toml +12 -5
- package/template/.cowork-flow/.version +1 -1
- package/template/.cowork-flow/scripts/application/task_context.py +18 -8
- package/template/.cowork-flow/scripts/common/core/quality_sources.py +140 -0
- package/template/.cowork-flow/scripts/common/gates/gates.py +54 -2
- package/template/.cowork-flow/scripts/common/gates/quality_review.py +332 -0
- package/template/.cowork-flow/scripts/common/gates/validate_coding_standards.py +13 -5
- package/template/.cowork-flow/spec/protocols/review.md +20 -1
- package/template/.opencode/agents/cowork-check.md +6 -0
- package/template/.opencode/agents/cowork-implement.md +14 -2
- package/template/.zcode/.zcode-plugin/plugin.json +1 -1
- 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/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,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.
|
|
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 `
|
|
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,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.
|
|
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
|
|
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.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
|
-
|
|
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
|
}
|
|
@@ -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
|
|
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",
|
|
@@ -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",
|