prizmkit 1.1.140 → 1.1.142
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/bundled/VERSION.json +3 -3
- package/bundled/dev-pipeline/README.md +1 -1
- package/bundled/dev-pipeline/prizmkit_runtime/checkpoint_state.py +165 -125
- package/bundled/dev-pipeline/prizmkit_runtime/runner_classification.py +13 -1
- package/bundled/dev-pipeline/prizmkit_runtime/runner_models.py +0 -6
- package/bundled/dev-pipeline/prizmkit_runtime/runners.py +5 -3
- package/bundled/dev-pipeline/prizmkit_runtime/test_result.py +160 -0
- package/bundled/dev-pipeline/scripts/check-session-status.py +11 -1
- package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +23 -806
- package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +6 -8
- package/bundled/dev-pipeline/scripts/generate-recovery-prompt.py +1 -1
- package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +6 -8
- package/bundled/dev-pipeline/scripts/prompt_framework.py +68 -56
- package/bundled/dev-pipeline/scripts/update-bug-status.py +21 -2
- package/bundled/dev-pipeline/scripts/update-checkpoint.py +52 -131
- package/bundled/dev-pipeline/scripts/update-feature-status.py +21 -2
- package/bundled/dev-pipeline/scripts/update-refactor-status.py +21 -2
- package/bundled/dev-pipeline/templates/bootstrap-prompt.md +3 -3
- package/bundled/dev-pipeline/templates/bootstrap-tier3.md +8 -19
- package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +6 -6
- package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +5 -5
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-commit-report.md +1 -1
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-diagnose-plan.md +1 -1
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-manual-verification.md +1 -1
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-review.md +1 -1
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-test.md +13 -10
- package/bundled/dev-pipeline/templates/sections/bugfix-reminders.md +1 -1
- package/bundled/dev-pipeline/templates/sections/checkpoint-system.md +18 -9
- package/bundled/dev-pipeline/templates/sections/headless-commit-authorization.md +1 -1
- package/bundled/dev-pipeline/templates/sections/phase-commit-full.md +1 -1
- package/bundled/dev-pipeline/templates/sections/phase-commit.md +1 -1
- package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-agent-suffix.md +2 -2
- package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-base.md +1 -1
- package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-lite-suffix.md +1 -1
- package/bundled/dev-pipeline/templates/sections/phase-implement-lite.md +1 -1
- package/bundled/dev-pipeline/templates/sections/phase-prizmkit-test.md +14 -26
- package/bundled/dev-pipeline/templates/sections/phase-review-full.md +2 -2
- package/bundled/dev-pipeline/templates/sections/phase-specify-plan-full.md +3 -3
- package/bundled/dev-pipeline/templates/sections/refactor-phase-review.md +1 -1
- package/bundled/dev-pipeline/templates/sections/refactor-phase-test.md +13 -10
- package/bundled/dev-pipeline/templates/sections/refactor-reminders.md +1 -1
- package/bundled/dev-pipeline/templates/session-status-schema.json +1 -1
- package/bundled/dev-pipeline/tests/test_checkpoint_state.py +494 -413
- package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +144 -684
- package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +20 -41
- package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +24 -24
- package/bundled/dev-pipeline/tests/test_python_runner_parity.py +176 -36
- package/bundled/skills/_metadata.json +6 -6
- package/bundled/skills/prizmkit/SKILL.md +13 -18
- package/bundled/skills/prizmkit/references/workflow-state-protocol.md +73 -80
- package/bundled/skills/prizmkit-code-review/SKILL.md +7 -7
- package/bundled/skills/prizmkit-code-review/references/workflow-state-protocol.md +73 -80
- package/bundled/skills/prizmkit-committer/SKILL.md +8 -7
- package/bundled/skills/prizmkit-committer/references/workflow-state-protocol.md +73 -80
- package/bundled/skills/prizmkit-implement/SKILL.md +3 -2
- package/bundled/skills/prizmkit-implement/references/workflow-state-protocol.md +73 -80
- package/bundled/skills/prizmkit-plan/SKILL.md +2 -1
- package/bundled/skills/prizmkit-plan/references/workflow-state-protocol.md +73 -80
- package/bundled/skills/prizmkit-retrospective/SKILL.md +8 -6
- package/bundled/skills/prizmkit-retrospective/references/workflow-state-protocol.md +73 -80
- package/bundled/skills/prizmkit-test/SKILL.md +217 -189
- package/bundled/skills/prizmkit-test/references/external-contract-mock-guidance.md +119 -0
- package/bundled/skills/prizmkit-test/references/independent-test-review.md +269 -0
- package/bundled/skills/prizmkit-test/references/test-coverage-model.md +193 -0
- package/bundled/skills/prizmkit-test/references/test-report-template.md +164 -79
- package/bundled/skills/prizmkit-workflow/SKILL.md +28 -36
- package/bundled/skills/prizmkit-workflow/references/workflow-state-protocol.md +73 -80
- package/package.json +1 -1
- package/bundled/skills/prizmkit-test/assets/authoritative-records.schema.json +0 -477
- package/bundled/skills/prizmkit-test/assets/behavior-risk-matrix.schema.json +0 -116
- package/bundled/skills/prizmkit-test/assets/evidence-manifest.schema.json +0 -111
- package/bundled/skills/prizmkit-test/assets/evidence-package-template.json +0 -66
- package/bundled/skills/prizmkit-test/references/boundary-coverage-protocol.md +0 -104
- package/bundled/skills/prizmkit-test/references/contract-mock-protocol.md +0 -65
- package/bundled/skills/prizmkit-test/references/evidence-protocol.md +0 -208
- package/bundled/skills/prizmkit-test/references/evidence-request-protocol.md +0 -80
- package/bundled/skills/prizmkit-test/references/examples.md +0 -100
- package/bundled/skills/prizmkit-test/references/service-boundary-test-catalog.md +0 -228
- package/bundled/skills/prizmkit-test/references/test-generation-steps.md +0 -118
- package/bundled/skills/prizmkit-test/references/trusted-evidence-execution.md +0 -107
- package/bundled/skills/prizmkit-test/references/workflow-state-protocol.md +0 -181
- package/bundled/skills/prizmkit-test/scripts/build_test_evidence.py +0 -1930
- package/bundled/skills/prizmkit-test/scripts/validate_test_evidence.py +0 -1847
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"""Validation for prizmkit-test terminal result and report artifacts."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import re
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
TEST_RESULTS = {"TEST_PASS", "TEST_NEEDS_FIXES", "TEST_BLOCKED"}
|
|
11
|
+
INDEPENDENT_REVIEW_STATUSES = {"completed", "downgraded", "not_applicable"}
|
|
12
|
+
_RESULT_LINE_PATTERN = re.compile(
|
|
13
|
+
r"(?m)^- Result:\s*(TEST_PASS|TEST_NEEDS_FIXES|TEST_BLOCKED)\s*$"
|
|
14
|
+
)
|
|
15
|
+
_BARE_RESULT_LINE_PATTERN = re.compile(
|
|
16
|
+
r"(?m)^(TEST_PASS|TEST_NEEDS_FIXES|TEST_BLOCKED)\s*$"
|
|
17
|
+
)
|
|
18
|
+
_RESULT_KEYS = {
|
|
19
|
+
"schema_version",
|
|
20
|
+
"result",
|
|
21
|
+
"report",
|
|
22
|
+
"main_review_rounds",
|
|
23
|
+
"independent_review",
|
|
24
|
+
"repair_rounds",
|
|
25
|
+
"production_changed",
|
|
26
|
+
"review_required",
|
|
27
|
+
"review_scope",
|
|
28
|
+
"unresolved_items",
|
|
29
|
+
}
|
|
30
|
+
_INDEPENDENT_REVIEW_KEYS = {
|
|
31
|
+
"status",
|
|
32
|
+
"responses",
|
|
33
|
+
"downgrade_reason",
|
|
34
|
+
"final_state_rechecked",
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def load_test_result(path: str | Path) -> dict[str, Any] | None:
|
|
39
|
+
"""Load one valid terminal projection, or return None."""
|
|
40
|
+
try:
|
|
41
|
+
value = json.loads(Path(path).read_text(encoding="utf-8"))
|
|
42
|
+
except (OSError, ValueError, TypeError):
|
|
43
|
+
return None
|
|
44
|
+
if not isinstance(value, dict):
|
|
45
|
+
return None
|
|
46
|
+
if set(value) != _RESULT_KEYS or value.get("schema_version") != 1:
|
|
47
|
+
return None
|
|
48
|
+
if value.get("result") not in TEST_RESULTS or value.get("report") != "test-report.md":
|
|
49
|
+
return None
|
|
50
|
+
|
|
51
|
+
rounds = value.get("main_review_rounds")
|
|
52
|
+
repairs = value.get("repair_rounds")
|
|
53
|
+
if not _integer_in_range(rounds, 0, 10) or not _integer_in_range(repairs, 0, 3):
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
independent = value.get("independent_review")
|
|
57
|
+
if not isinstance(independent, dict) or set(independent) != _INDEPENDENT_REVIEW_KEYS:
|
|
58
|
+
return None
|
|
59
|
+
status = independent.get("status")
|
|
60
|
+
responses = independent.get("responses")
|
|
61
|
+
if status not in INDEPENDENT_REVIEW_STATUSES or not _integer_in_range(responses, 0, 5):
|
|
62
|
+
return None
|
|
63
|
+
if not isinstance(independent.get("final_state_rechecked"), bool):
|
|
64
|
+
return None
|
|
65
|
+
downgrade_reason = independent.get("downgrade_reason")
|
|
66
|
+
if status == "downgraded":
|
|
67
|
+
if not isinstance(downgrade_reason, str) or not downgrade_reason.strip():
|
|
68
|
+
return None
|
|
69
|
+
if responses != 0 or independent["final_state_rechecked"]:
|
|
70
|
+
return None
|
|
71
|
+
elif downgrade_reason is not None:
|
|
72
|
+
return None
|
|
73
|
+
if status == "completed":
|
|
74
|
+
if responses < 1 or not independent["final_state_rechecked"]:
|
|
75
|
+
return None
|
|
76
|
+
if status == "not_applicable":
|
|
77
|
+
if responses != 0 or independent["final_state_rechecked"]:
|
|
78
|
+
return None
|
|
79
|
+
|
|
80
|
+
if not isinstance(value.get("production_changed"), bool):
|
|
81
|
+
return None
|
|
82
|
+
if not isinstance(value.get("review_required"), bool):
|
|
83
|
+
return None
|
|
84
|
+
expected_scope = "delta" if value["review_required"] else None
|
|
85
|
+
if value.get("review_scope") != expected_scope:
|
|
86
|
+
return None
|
|
87
|
+
unresolved = value.get("unresolved_items")
|
|
88
|
+
if not isinstance(unresolved, list) or any(
|
|
89
|
+
not isinstance(item, str) or not item.strip() for item in unresolved
|
|
90
|
+
):
|
|
91
|
+
return None
|
|
92
|
+
|
|
93
|
+
if value["result"] == "TEST_PASS":
|
|
94
|
+
if value["review_required"] or unresolved or rounds < 1:
|
|
95
|
+
return None
|
|
96
|
+
return value
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def valid_test_result(path: str | Path, *, required_result: str | None = None) -> bool:
|
|
100
|
+
"""Return whether the terminal projection is valid and has the required result."""
|
|
101
|
+
value = load_test_result(path)
|
|
102
|
+
return value is not None and (
|
|
103
|
+
required_result is None or value.get("result") == required_result
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def valid_test_artifact_pair(
|
|
108
|
+
report_path: str | Path,
|
|
109
|
+
result_path: str | Path,
|
|
110
|
+
*,
|
|
111
|
+
required_result: str | None = None,
|
|
112
|
+
) -> bool:
|
|
113
|
+
"""Validate one sibling report/result pair and their terminal result."""
|
|
114
|
+
report = Path(report_path)
|
|
115
|
+
result = Path(result_path)
|
|
116
|
+
if not report.is_file() or not result.is_file():
|
|
117
|
+
return False
|
|
118
|
+
if report.resolve().parent != result.resolve().parent:
|
|
119
|
+
return False
|
|
120
|
+
projection = load_test_result(result)
|
|
121
|
+
if projection is None:
|
|
122
|
+
return False
|
|
123
|
+
if required_result is not None and projection.get("result") != required_result:
|
|
124
|
+
return False
|
|
125
|
+
try:
|
|
126
|
+
report_text = report.read_text(encoding="utf-8")
|
|
127
|
+
except OSError:
|
|
128
|
+
return False
|
|
129
|
+
final_result = _report_final_result(report_text)
|
|
130
|
+
return (
|
|
131
|
+
report_text.startswith("# Test Report")
|
|
132
|
+
and final_result == projection["result"]
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _report_final_result(report: str) -> str | None:
|
|
137
|
+
"""Extract one unambiguous result from a supported terminal section."""
|
|
138
|
+
declared_results: list[str] = []
|
|
139
|
+
for match in re.finditer(
|
|
140
|
+
r"(?ms)^## (?P<title>Final Result|Terminal Result|Final Decision)\s*$\n"
|
|
141
|
+
r"(?P<body>.*?)(?=^## |\Z)",
|
|
142
|
+
report,
|
|
143
|
+
):
|
|
144
|
+
body = match.group("body")
|
|
145
|
+
result_lines = _RESULT_LINE_PATTERN.findall(body)
|
|
146
|
+
if match.group("title") == "Final Decision":
|
|
147
|
+
result_lines.extend(_BARE_RESULT_LINE_PATTERN.findall(body))
|
|
148
|
+
declared_results.extend(result_lines)
|
|
149
|
+
|
|
150
|
+
if not declared_results or len(set(declared_results)) != 1:
|
|
151
|
+
return None
|
|
152
|
+
return declared_results[0]
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _integer_in_range(value: object, minimum: int, maximum: int) -> bool:
|
|
156
|
+
return (
|
|
157
|
+
isinstance(value, int)
|
|
158
|
+
and not isinstance(value, bool)
|
|
159
|
+
and minimum <= value <= maximum
|
|
160
|
+
)
|
|
@@ -150,7 +150,9 @@ def determine_status(data):
|
|
|
150
150
|
"""Determine the single-line status string from the parsed data.
|
|
151
151
|
|
|
152
152
|
Returns one of: success, partial_resumable, partial_not_resumable,
|
|
153
|
-
failed,
|
|
153
|
+
failed, crashed, timed_out, infra_error, commit_missing, docs_missing,
|
|
154
|
+
merge_conflict, context_overflow, stalled_context_continuation,
|
|
155
|
+
workflow_blocked, workflow_skipped.
|
|
154
156
|
"""
|
|
155
157
|
status = data.get("status", "")
|
|
156
158
|
|
|
@@ -174,12 +176,20 @@ def determine_status(data):
|
|
|
174
176
|
return "crashed"
|
|
175
177
|
elif status == "timed_out":
|
|
176
178
|
return "timed_out"
|
|
179
|
+
elif status == "infra_error":
|
|
180
|
+
return "infra_error"
|
|
181
|
+
elif status == "stalled_context_continuation":
|
|
182
|
+
return "stalled_context_continuation"
|
|
177
183
|
elif status == "commit_missing":
|
|
178
184
|
return "commit_missing"
|
|
179
185
|
elif status == "docs_missing":
|
|
180
186
|
return "docs_missing"
|
|
181
187
|
elif status == "merge_conflict":
|
|
182
188
|
return "merge_conflict"
|
|
189
|
+
elif status == "workflow_blocked":
|
|
190
|
+
return "workflow_blocked"
|
|
191
|
+
elif status == "workflow_skipped":
|
|
192
|
+
return "workflow_skipped"
|
|
183
193
|
else:
|
|
184
194
|
# Unknown status value — treat as crashed
|
|
185
195
|
return "crashed"
|