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
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
"""Tests for generate-bootstrap-prompt.py core functions."""
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
-
import hashlib
|
|
5
4
|
import importlib.util
|
|
6
5
|
import os
|
|
7
6
|
import re
|
|
8
|
-
import time
|
|
9
7
|
from argparse import Namespace
|
|
10
8
|
from pathlib import Path
|
|
11
9
|
|
|
@@ -28,12 +26,12 @@ from generate_bootstrap_prompt import (
|
|
|
28
26
|
merge_checkpoint_state,
|
|
29
27
|
load_active_agent_prompts,
|
|
30
28
|
validate_rendered,
|
|
31
|
-
validate_feature_test_evidence,
|
|
32
29
|
route_feature_repair,
|
|
33
30
|
feature_checkpoint_metadata,
|
|
34
31
|
main as generate_bootstrap_main,
|
|
35
32
|
)
|
|
36
33
|
from continuation import append_continuation_handoff, checkpoint_cursor
|
|
34
|
+
from prompt_framework import pipeline_checkpoint_metadata
|
|
37
35
|
from utils import resolve_prompt_platform
|
|
38
36
|
|
|
39
37
|
REMOVED_MAX_LOG_ENV = "MAX_" + "LOG_SIZE"
|
|
@@ -112,197 +110,6 @@ def test_rendered_prompt_validation_rejects_unresolved_control_placeholders():
|
|
|
112
110
|
assert errors == ["Unreplaced placeholders: {{INIT_SCRIPT_PATH}}"]
|
|
113
111
|
|
|
114
112
|
|
|
115
|
-
def scoped_report_text(verdict="PASS", boundary_missing=0,
|
|
116
|
-
happy_path_only=0, completion_gate="yes",
|
|
117
|
-
validator_result="passed"):
|
|
118
|
-
"""Build a scoped report with a complete boundary contract."""
|
|
119
|
-
return "\n".join([
|
|
120
|
-
"# Test Report",
|
|
121
|
-
"",
|
|
122
|
-
"## Scope",
|
|
123
|
-
"- Mode: this-change",
|
|
124
|
-
"- Artifact Dir: .prizmkit/specs/123-scope-test/",
|
|
125
|
-
"- In-Scope Source Files:",
|
|
126
|
-
" - src/feature.js",
|
|
127
|
-
"- In-Scope Test Files:",
|
|
128
|
-
" - tests/feature.test.js",
|
|
129
|
-
"",
|
|
130
|
-
"## Boundary Matrix",
|
|
131
|
-
"| Module | Interface | Service Type | Happy Path | Request Validation | Auth / Permission / Ownership | Domain Invariants | Collection / Pagination | Date / Time | State Transitions | Dependency Failure | Response Contract | N/A Reasons | Final Status |",
|
|
132
|
-
"|--------|-----------|--------------|------------|--------------------|-------------------------------|-------------------|-------------------------|-------------|-------------------|-------------------|-------------------|-------------|--------------|",
|
|
133
|
-
"| feature | POST /feature | API | covered | covered | covered | covered | covered | covered | covered | covered | covered | none | boundary-covered |",
|
|
134
|
-
"",
|
|
135
|
-
"## Boundary Completion Gate",
|
|
136
|
-
"- Interfaces total: 1",
|
|
137
|
-
"- Boundary-covered: 1",
|
|
138
|
-
"- Generated-needs-review: 0",
|
|
139
|
-
"- Skipped-with-reason: 0",
|
|
140
|
-
f"- Boundary-missing: {boundary_missing}",
|
|
141
|
-
f"- Happy-path-only: {happy_path_only}",
|
|
142
|
-
f"- Completion gate passed: {completion_gate}",
|
|
143
|
-
"",
|
|
144
|
-
"## Boundary Validation",
|
|
145
|
-
"- Validator command: `python3 validate_boundary_report.py`",
|
|
146
|
-
f"- Validator result: {validator_result}",
|
|
147
|
-
"- Validator output: Boundary report validation: PASS",
|
|
148
|
-
"",
|
|
149
|
-
"## Verdict",
|
|
150
|
-
verdict,
|
|
151
|
-
"",
|
|
152
|
-
"## Final Status",
|
|
153
|
-
"- All tests passing: yes",
|
|
154
|
-
f"- Boundary completion gate passed: {completion_gate}",
|
|
155
|
-
"- Ready for commit: yes",
|
|
156
|
-
"- Needs human review: None",
|
|
157
|
-
"",
|
|
158
|
-
])
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
def write_fresh_scoped_report(project_root, report_path, report_text=None):
|
|
162
|
-
"""Write marker, in-scope files, and a fresh derived report."""
|
|
163
|
-
source = project_root / "src" / "feature.js"
|
|
164
|
-
test = project_root / "tests" / "feature.test.js"
|
|
165
|
-
marker = project_root / ".prizmkit" / "specs" / "123-scope-test" / ".prizmkit-test-started"
|
|
166
|
-
source.parent.mkdir(parents=True, exist_ok=True)
|
|
167
|
-
test.parent.mkdir(parents=True, exist_ok=True)
|
|
168
|
-
marker.parent.mkdir(parents=True, exist_ok=True)
|
|
169
|
-
report_path.parent.mkdir(parents=True, exist_ok=True)
|
|
170
|
-
source.write_text("export const feature = true;\n", encoding="utf-8")
|
|
171
|
-
test.write_text("test('feature', () => {});\n", encoding="utf-8")
|
|
172
|
-
marker.write_text("started\n", encoding="utf-8")
|
|
173
|
-
time.sleep(0.01)
|
|
174
|
-
report_path.write_text(report_text or scoped_report_text(), encoding="utf-8")
|
|
175
|
-
return source, test, marker
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
def write_fresh_structured_evidence(project_root):
|
|
179
|
-
"""Create the smallest protocol-shaped, validator-attested pass package."""
|
|
180
|
-
source, test, marker = write_fresh_scoped_report(
|
|
181
|
-
project_root,
|
|
182
|
-
project_root / ".prizmkit" / "test" / "unused" / "test-report.md",
|
|
183
|
-
)
|
|
184
|
-
evidence_base = project_root / ".prizmkit" / "test" / "evidence"
|
|
185
|
-
environment = {}
|
|
186
|
-
plan_inputs = {"feature": "123-scope-test"}
|
|
187
|
-
categories = {
|
|
188
|
-
"source": [{"path": "src/feature.js", "sha256": hashlib.sha256(source.read_bytes()).hexdigest()}],
|
|
189
|
-
"tests": [{"path": "tests/feature.test.js", "sha256": hashlib.sha256(test.read_bytes()).hexdigest()}],
|
|
190
|
-
"contracts": [],
|
|
191
|
-
"lockfiles": [],
|
|
192
|
-
}
|
|
193
|
-
target_hashes = {
|
|
194
|
-
category: hashlib.sha256(json.dumps(
|
|
195
|
-
sorted(records, key=lambda item: item["path"]),
|
|
196
|
-
sort_keys=True, separators=(",", ":"),
|
|
197
|
-
).encode()).hexdigest()
|
|
198
|
-
for category, records in categories.items()
|
|
199
|
-
}
|
|
200
|
-
target_hashes["environment"] = hashlib.sha256(b"{}").hexdigest()
|
|
201
|
-
target_hashes["plan"] = hashlib.sha256(json.dumps(
|
|
202
|
-
plan_inputs, sort_keys=True, separators=(",", ":"),
|
|
203
|
-
).encode()).hexdigest()
|
|
204
|
-
inventory = {
|
|
205
|
-
"categories": categories,
|
|
206
|
-
"plan_inputs": plan_inputs,
|
|
207
|
-
}
|
|
208
|
-
scope = {"feature": "123-scope-test"}
|
|
209
|
-
zero_hash = "0" * 64
|
|
210
|
-
identity = {
|
|
211
|
-
"baseline_commit": "baseline",
|
|
212
|
-
"working_diff_sha256": zero_hash,
|
|
213
|
-
"scope_sha256": hashlib.sha256(json.dumps(
|
|
214
|
-
scope, sort_keys=True, separators=(",", ":"),
|
|
215
|
-
).encode()).hexdigest(),
|
|
216
|
-
}
|
|
217
|
-
evidence_id = hashlib.sha256(json.dumps(
|
|
218
|
-
identity, sort_keys=True, separators=(",", ":"),
|
|
219
|
-
).encode()).hexdigest()
|
|
220
|
-
evidence_root = evidence_base / evidence_id
|
|
221
|
-
evidence_root.mkdir(parents=True, exist_ok=True)
|
|
222
|
-
(evidence_root / "environment.json").write_text("{}\n", encoding="utf-8")
|
|
223
|
-
(evidence_root / "target-inventory.json").write_text(
|
|
224
|
-
json.dumps(inventory), encoding="utf-8",
|
|
225
|
-
)
|
|
226
|
-
(evidence_root / "scope.json").write_text(json.dumps(scope), encoding="utf-8")
|
|
227
|
-
verdict = {
|
|
228
|
-
"verdict": "TEST_PASS",
|
|
229
|
-
"testing_domain_only": True,
|
|
230
|
-
"authorizes_commit_or_release": False,
|
|
231
|
-
"real_environment_validated": False,
|
|
232
|
-
"repairs_business_defects": False,
|
|
233
|
-
"code_evidence_replayable": True,
|
|
234
|
-
"blockers": [],
|
|
235
|
-
"reproduced_failures": [],
|
|
236
|
-
}
|
|
237
|
-
(evidence_root / "verdict.json").write_text(json.dumps(verdict), encoding="utf-8")
|
|
238
|
-
report = scoped_report_text()
|
|
239
|
-
(evidence_root / "test-report.md").write_text(report, encoding="utf-8")
|
|
240
|
-
stages = []
|
|
241
|
-
stage_names = [
|
|
242
|
-
"CHANGE_CLASSIFY", "SCOPE_DISCOVER", "CONTRACT_MODEL", "TEST_PLAN",
|
|
243
|
-
"INFRA_READY", "TEST_BUILD", "EXECUTE_PROVE", "EVIDENCE_PACKAGE",
|
|
244
|
-
"EVIDENCE_VALIDATE",
|
|
245
|
-
]
|
|
246
|
-
for name in stage_names:
|
|
247
|
-
stages.append({"name": name, "status": "complete", "outputs": []})
|
|
248
|
-
manifest = {
|
|
249
|
-
"protocol_version": "1.0",
|
|
250
|
-
"evidence_id": evidence_id,
|
|
251
|
-
"evidence_id_inputs": identity,
|
|
252
|
-
"sensitivity": "project-controlled",
|
|
253
|
-
"environment_claim": "mocked-code-level-only",
|
|
254
|
-
"compatibility": "legacy-test-report-interface-not-supported",
|
|
255
|
-
"change_class": "lightweight",
|
|
256
|
-
"baseline_commit": "baseline",
|
|
257
|
-
"working_diff_sha256": zero_hash,
|
|
258
|
-
"target_hashes": target_hashes,
|
|
259
|
-
"stages": stages,
|
|
260
|
-
"files": [],
|
|
261
|
-
"final_verdict": "TEST_PASS",
|
|
262
|
-
}
|
|
263
|
-
file_records = []
|
|
264
|
-
for path in sorted(evidence_root.rglob("*")):
|
|
265
|
-
if path.is_file():
|
|
266
|
-
relative = path.relative_to(evidence_root).as_posix()
|
|
267
|
-
file_records.append({
|
|
268
|
-
"path": relative,
|
|
269
|
-
"sha256": hashlib.sha256(path.read_bytes()).hexdigest(),
|
|
270
|
-
"produced_by": "EVIDENCE_PACKAGE",
|
|
271
|
-
})
|
|
272
|
-
payload = sorted(
|
|
273
|
-
({"path": item["path"], "sha256": item["sha256"]}
|
|
274
|
-
for item in file_records
|
|
275
|
-
if item["path"] not in {"verdict.json", "test-report.md"}),
|
|
276
|
-
key=lambda item: item["path"],
|
|
277
|
-
)
|
|
278
|
-
validation = {
|
|
279
|
-
"validator": "validate_test_evidence.py",
|
|
280
|
-
"validator_sha256": zero_hash,
|
|
281
|
-
"payload_sha256": hashlib.sha256(json.dumps(
|
|
282
|
-
payload, sort_keys=True, separators=(",", ":"),
|
|
283
|
-
).encode()).hexdigest(),
|
|
284
|
-
"result": "passed",
|
|
285
|
-
"verdict": "TEST_PASS",
|
|
286
|
-
"semantics": "integrity-and-protocol-validation-not-hostile-producer-proof",
|
|
287
|
-
"replay_receipt_ids": [],
|
|
288
|
-
}
|
|
289
|
-
(evidence_root / "validation.json").write_text(json.dumps(validation), encoding="utf-8")
|
|
290
|
-
file_records.append({
|
|
291
|
-
"path": "validation.json",
|
|
292
|
-
"sha256": hashlib.sha256((evidence_root / "validation.json").read_bytes()).hexdigest(),
|
|
293
|
-
"produced_by": "EVIDENCE_VALIDATE",
|
|
294
|
-
})
|
|
295
|
-
manifest["files"] = file_records
|
|
296
|
-
(evidence_root / "manifest.json").write_text(json.dumps(manifest), encoding="utf-8")
|
|
297
|
-
pointer = project_root / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
|
|
298
|
-
pointer.parent.mkdir(parents=True, exist_ok=True)
|
|
299
|
-
pointer.write_text(str(evidence_root / "test-report.md"), encoding="utf-8")
|
|
300
|
-
report_path = evidence_root / "test-report.md"
|
|
301
|
-
now = time.time()
|
|
302
|
-
os.utime(marker, (now, now))
|
|
303
|
-
os.utime(report_path, (now + 1, now + 1))
|
|
304
|
-
return evidence_root
|
|
305
|
-
|
|
306
113
|
|
|
307
114
|
# ---------------------------------------------------------------------------
|
|
308
115
|
# compute_feature_slug
|
|
@@ -601,544 +408,196 @@ class TestProcessModeBlocks:
|
|
|
601
408
|
|
|
602
409
|
class TestScopedFeatureTestGate:
|
|
603
410
|
@staticmethod
|
|
604
|
-
def
|
|
605
|
-
|
|
606
|
-
"
|
|
607
|
-
|
|
411
|
+
def _artifacts(slug="123-scope-test"):
|
|
412
|
+
return [
|
|
413
|
+
f".prizmkit/specs/{slug}/test-report.md",
|
|
414
|
+
f".prizmkit/specs/{slug}/test-result.json",
|
|
415
|
+
]
|
|
416
|
+
|
|
417
|
+
@staticmethod
|
|
418
|
+
def _write_result(root, result="TEST_PASS", *, unresolved=None,
|
|
419
|
+
review_required=False, review_scope=None):
|
|
420
|
+
artifact = root / ".prizmkit" / "specs" / "123-scope-test"
|
|
421
|
+
artifact.mkdir(parents=True, exist_ok=True)
|
|
422
|
+
(artifact / "test-report.md").write_text(
|
|
423
|
+
f"# Test Report\n\n## Final Result\n\n- Result: {result}\n",
|
|
424
|
+
encoding="utf-8",
|
|
425
|
+
)
|
|
426
|
+
payload = {
|
|
427
|
+
"schema_version": 1,
|
|
428
|
+
"result": result,
|
|
429
|
+
"report": "test-report.md",
|
|
430
|
+
"main_review_rounds": 1,
|
|
431
|
+
"independent_review": {
|
|
432
|
+
"status": "downgraded",
|
|
433
|
+
"responses": 0,
|
|
434
|
+
"downgrade_reason": "read-only continuation capability unavailable",
|
|
435
|
+
"final_state_rechecked": False,
|
|
436
|
+
},
|
|
437
|
+
"repair_rounds": 0,
|
|
438
|
+
"production_changed": False,
|
|
439
|
+
"review_required": review_required,
|
|
440
|
+
"review_scope": review_scope,
|
|
441
|
+
"unresolved_items": unresolved or [],
|
|
442
|
+
}
|
|
443
|
+
(artifact / "test-result.json").write_text(
|
|
444
|
+
json.dumps(payload), encoding="utf-8",
|
|
608
445
|
)
|
|
609
446
|
|
|
610
447
|
def test_standard_sections_insert_prizmkit_test_after_review(self):
|
|
611
448
|
sections_dir = Path(__file__).resolve().parents[1] / "templates" / "sections"
|
|
612
449
|
sections = assemble_sections(
|
|
613
|
-
"standard",
|
|
614
|
-
|
|
615
|
-
init_done=True,
|
|
616
|
-
is_resume=False,
|
|
617
|
-
browser_enabled=False,
|
|
450
|
+
"standard", str(sections_dir), init_done=True,
|
|
451
|
+
is_resume=False, browser_enabled=False,
|
|
618
452
|
)
|
|
619
453
|
names = [name for name, _ in sections]
|
|
454
|
+
rendered = "\n".join(content for _, content in sections)
|
|
620
455
|
|
|
621
|
-
assert "phase-prizmkit-test" in names
|
|
622
456
|
assert names.index("phase-implement") < names.index("phase-review") < names.index("phase-prizmkit-test")
|
|
623
|
-
|
|
624
|
-
rendered = "\n".join(content for _, content in sections)
|
|
625
|
-
assert "prizmkit-test-gate.py" not in rendered
|
|
626
|
-
assert "{{RUNTIME_HELPER_CMD}} artifact touch .prizmkit/specs/{{FEATURE_SLUG}}/.prizmkit-test-started" in rendered
|
|
627
|
-
assert "mkdir -p .prizmkit/specs/{{FEATURE_SLUG}}" not in rendered
|
|
628
|
-
assert "when present" not in rendered
|
|
629
|
-
assert "Boundary Matrix" in rendered
|
|
630
|
-
assert "Boundary Completion Gate" in rendered
|
|
631
|
-
assert "Boundary-missing: 0" in rendered
|
|
632
|
-
assert "Happy-path-only: 0" in rendered
|
|
633
|
-
assert "Validator result: passed" in rendered
|
|
634
457
|
assert "/prizmkit-test scope=this-change artifact_dir=.prizmkit/specs/{{FEATURE_SLUG}}/" in rendered
|
|
635
|
-
assert ".
|
|
636
|
-
assert ".
|
|
637
|
-
assert "
|
|
638
|
-
assert "
|
|
639
|
-
assert "
|
|
640
|
-
assert "Run the full test suite" not in rendered
|
|
641
|
-
assert "before code review" not in rendered
|
|
642
|
-
assert "Precondition: the `prizmkit-test` checkpoint is completed" not in rendered
|
|
458
|
+
assert "test-report.md" in rendered
|
|
459
|
+
assert "test-result.json" in rendered
|
|
460
|
+
assert "TEST_NEEDS_FIXES" in rendered
|
|
461
|
+
assert ".prizmkit/test/evidence" not in rendered
|
|
462
|
+
assert "TEST_FAIL" not in rendered
|
|
643
463
|
|
|
644
464
|
def test_standard_sections_include_central_checkpoint_helper(self):
|
|
645
465
|
sections_dir = Path(__file__).resolve().parents[1] / "templates" / "sections"
|
|
646
466
|
sections = assemble_sections(
|
|
647
|
-
"standard",
|
|
648
|
-
|
|
649
|
-
init_done=True,
|
|
650
|
-
is_resume=False,
|
|
651
|
-
browser_enabled=False,
|
|
467
|
+
"standard", str(sections_dir), init_done=True,
|
|
468
|
+
is_resume=False, browser_enabled=False,
|
|
652
469
|
)
|
|
653
470
|
rendered = "\n".join(content for _, content in sections)
|
|
654
471
|
|
|
655
472
|
assert "### Checkpoint Update Helper" in rendered
|
|
656
473
|
assert "{{CHECKPOINT_PYTHON_CMD}}" in rendered
|
|
657
|
-
assert "python3 .prizmkit/dev-pipeline/scripts/update-checkpoint.py" not in rendered
|
|
658
474
|
assert "--checkpoint-path {{CHECKPOINT_PATH}}" in rendered
|
|
659
|
-
assert "--
|
|
475
|
+
assert "--stage-result <optional-domain-result>" in rendered
|
|
476
|
+
assert "--status completed --stage-result PLAN_READY" in rendered
|
|
477
|
+
assert "--status completed --stage-result IMPLEMENTED" in rendered
|
|
478
|
+
assert "--status completed --stage-result REVIEW_PASS" in rendered
|
|
479
|
+
assert "--status completed --stage-result TEST_PASS" in rendered
|
|
480
|
+
assert "--status completed --stage-result RETRO_COMPLETE" in rendered
|
|
481
|
+
assert "--status in_progress --stage-result COMMIT_PENDING" in rendered
|
|
482
|
+
assert "--status completed --stage-result COMMITTED" in rendered
|
|
660
483
|
assert "Do not hand-edit `workflow-checkpoint.json` directly." in rendered
|
|
661
|
-
assert "
|
|
662
|
-
|
|
663
|
-
def test_lite_sections_include_review_before_prizmkit_test_and_commit(self):
|
|
664
|
-
sections_dir = Path(__file__).resolve().parents[1] / "templates" / "sections"
|
|
665
|
-
sections = assemble_sections(
|
|
666
|
-
"lite",
|
|
667
|
-
str(sections_dir),
|
|
668
|
-
init_done=True,
|
|
669
|
-
is_resume=False,
|
|
670
|
-
browser_enabled=False,
|
|
671
|
-
)
|
|
672
|
-
names = [name for name, _ in sections]
|
|
673
|
-
|
|
674
|
-
assert "phase-prizmkit-test" in names
|
|
675
|
-
assert "phase-review" in names
|
|
676
|
-
assert names.index("phase-implement") < names.index("phase-review") < names.index("phase-prizmkit-test") < names.index("phase-commit")
|
|
484
|
+
assert "authoritative_evidence_paths" not in rendered
|
|
677
485
|
|
|
678
|
-
def
|
|
486
|
+
def test_checkpoint_orders_test_after_review_with_terminal_artifacts(self):
|
|
679
487
|
sections_dir = Path(__file__).resolve().parents[1] / "templates" / "sections"
|
|
680
488
|
sections = assemble_sections(
|
|
681
|
-
"standard",
|
|
682
|
-
|
|
683
|
-
init_done=True,
|
|
684
|
-
is_resume=False,
|
|
685
|
-
browser_enabled=False,
|
|
489
|
+
"standard", str(sections_dir), init_done=True,
|
|
490
|
+
is_resume=False, browser_enabled=False,
|
|
686
491
|
)
|
|
687
492
|
checkpoint = generate_checkpoint_definition(
|
|
688
|
-
sections,
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
item_id="F-123",
|
|
692
|
-
item_slug="123-scope-test",
|
|
693
|
-
session_id="F-123-session",
|
|
694
|
-
init_done=True,
|
|
493
|
+
sections, pipeline_mode="standard", workflow_type="feature-pipeline",
|
|
494
|
+
item_id="F-123", item_slug="123-scope-test",
|
|
495
|
+
session_id="F-123-session", init_done=True,
|
|
695
496
|
)
|
|
696
497
|
skills = [step["skill"] for step in checkpoint["steps"]]
|
|
697
|
-
|
|
698
|
-
assert skills.index("prizmkit-implement") < skills.index("prizmkit-code-review") < skills.index("prizmkit-test")
|
|
699
498
|
test_step = next(step for step in checkpoint["steps"] if step["skill"] == "prizmkit-test")
|
|
700
|
-
assert test_step["required_artifacts"] == [
|
|
701
|
-
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
702
|
-
".prizmkit/test/evidence/*/manifest.json",
|
|
703
|
-
".prizmkit/test/evidence/*/verdict.json",
|
|
704
|
-
".prizmkit/test/evidence/*/validation.json",
|
|
705
|
-
]
|
|
706
|
-
|
|
707
|
-
def test_merge_resets_completed_prizmkit_test_without_report_artifact(self, tmp_path):
|
|
708
|
-
existing = {
|
|
709
|
-
"steps": [
|
|
710
|
-
{
|
|
711
|
-
"id": "S01",
|
|
712
|
-
"skill": "prizmkit-test",
|
|
713
|
-
"status": "completed",
|
|
714
|
-
"required_artifacts": [
|
|
715
|
-
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
716
|
-
".prizmkit/test/*/test-report.md",
|
|
717
|
-
],
|
|
718
|
-
},
|
|
719
|
-
{"id": "S02", "skill": "prizmkit-code-review", "status": "completed", "required_artifacts": []},
|
|
720
|
-
],
|
|
721
|
-
}
|
|
722
|
-
fresh = {
|
|
723
|
-
"steps": [
|
|
724
|
-
{"id": "S01", "skill": "prizmkit-test", "status": "pending", "required_artifacts": [
|
|
725
|
-
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
726
|
-
".prizmkit/test/*/test-report.md",
|
|
727
|
-
]},
|
|
728
|
-
{"id": "S02", "skill": "prizmkit-code-review", "status": "pending", "required_artifacts": []},
|
|
729
|
-
],
|
|
730
|
-
}
|
|
731
|
-
pointer = tmp_path / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
|
|
732
|
-
pointer.parent.mkdir(parents=True, exist_ok=True)
|
|
733
|
-
pointer.write_text(".prizmkit/test/missing/test-report.md\n", encoding="utf-8")
|
|
734
|
-
|
|
735
|
-
merged = merge_checkpoint_state(existing, fresh, str(tmp_path))
|
|
736
|
-
|
|
737
|
-
assert [step["status"] for step in merged["steps"]] == ["pending", "pending"]
|
|
738
|
-
|
|
739
|
-
def test_merge_keeps_completed_prizmkit_test_only_for_scoped_pass_report(self, tmp_path, monkeypatch):
|
|
740
|
-
self._trust_fixture_validator(monkeypatch)
|
|
741
|
-
evidence_root = write_fresh_structured_evidence(tmp_path)
|
|
742
|
-
pointer = tmp_path / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
|
|
743
|
-
pointer.parent.mkdir(parents=True, exist_ok=True)
|
|
744
|
-
pointer.write_text(str(evidence_root / "test-report.md") + "\n", encoding="utf-8")
|
|
745
|
-
existing = {
|
|
746
|
-
"steps": [
|
|
747
|
-
{"id": "S01", "skill": "prizmkit-test", "status": "completed", "stage_result": "TEST_PASS", "required_artifacts": [
|
|
748
|
-
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
749
|
-
".prizmkit/test/*/test-report.md",
|
|
750
|
-
]},
|
|
751
|
-
{"id": "S02", "skill": "prizmkit-code-review", "status": "completed", "stage_result": "REVIEW_PASS", "required_artifacts": []},
|
|
752
|
-
],
|
|
753
|
-
}
|
|
754
|
-
fresh = {
|
|
755
|
-
"steps": [
|
|
756
|
-
{"id": "S01", "skill": "prizmkit-test", "status": "pending", "required_artifacts": [
|
|
757
|
-
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
758
|
-
".prizmkit/test/*/test-report.md",
|
|
759
|
-
]},
|
|
760
|
-
{"id": "S02", "skill": "prizmkit-code-review", "status": "pending", "required_artifacts": []},
|
|
761
|
-
],
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
merged = merge_checkpoint_state(existing, fresh, str(tmp_path))
|
|
765
|
-
|
|
766
|
-
assert [step["status"] for step in merged["steps"]] == ["completed", "completed"]
|
|
767
|
-
|
|
768
|
-
def test_merge_resets_completed_prizmkit_test_for_needs_fixes_report(self, tmp_path):
|
|
769
|
-
report = tmp_path / ".prizmkit" / "test" / "run" / "test-report.md"
|
|
770
|
-
report.parent.mkdir(parents=True)
|
|
771
|
-
report.write_text(scoped_report_text(verdict="NEEDS_FIXES"), encoding="utf-8")
|
|
772
|
-
pointer = tmp_path / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
|
|
773
|
-
pointer.parent.mkdir(parents=True, exist_ok=True)
|
|
774
|
-
pointer.write_text(".prizmkit/test/run/test-report.md\n", encoding="utf-8")
|
|
775
|
-
existing = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "completed", "required_artifacts": [
|
|
776
|
-
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
777
|
-
".prizmkit/test/*/test-report.md",
|
|
778
|
-
]}]}
|
|
779
|
-
fresh = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "pending", "required_artifacts": [
|
|
780
|
-
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
781
|
-
".prizmkit/test/*/test-report.md",
|
|
782
|
-
]}]}
|
|
783
|
-
|
|
784
|
-
merged = merge_checkpoint_state(existing, fresh, str(tmp_path))
|
|
785
|
-
|
|
786
|
-
assert merged["steps"][0]["status"] == "pending"
|
|
787
|
-
|
|
788
|
-
def test_merge_resets_completed_prizmkit_test_for_absolute_report_outside_project(self, tmp_path):
|
|
789
|
-
project_root = tmp_path / "project"
|
|
790
|
-
external_root = tmp_path / "external"
|
|
791
|
-
in_project_report = project_root / ".prizmkit" / "test" / "run" / "test-report.md"
|
|
792
|
-
external_report = external_root / "report.md"
|
|
793
|
-
pointer = project_root / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
|
|
794
|
-
for path in (in_project_report.parent, external_report.parent, pointer.parent):
|
|
795
|
-
path.mkdir(parents=True, exist_ok=True)
|
|
796
|
-
report_text = "\n".join([
|
|
797
|
-
"# Test Report",
|
|
798
|
-
"",
|
|
799
|
-
"## Scope",
|
|
800
|
-
"- Mode: this-change",
|
|
801
|
-
"- Artifact Dir: .prizmkit/specs/123-scope-test/",
|
|
802
|
-
"",
|
|
803
|
-
"## Verdict",
|
|
804
|
-
"PASS",
|
|
805
|
-
"",
|
|
806
|
-
])
|
|
807
|
-
in_project_report.write_text(report_text, encoding="utf-8")
|
|
808
|
-
external_report.write_text(report_text, encoding="utf-8")
|
|
809
|
-
pointer.write_text(f"{external_report}\n", encoding="utf-8")
|
|
810
|
-
existing = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "completed", "required_artifacts": [
|
|
811
|
-
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
812
|
-
".prizmkit/test/*/test-report.md",
|
|
813
|
-
]}]}
|
|
814
|
-
fresh = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "pending", "required_artifacts": [
|
|
815
|
-
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
816
|
-
".prizmkit/test/*/test-report.md",
|
|
817
|
-
]}]}
|
|
818
|
-
|
|
819
|
-
merged = merge_checkpoint_state(existing, fresh, str(project_root))
|
|
820
|
-
|
|
821
|
-
assert merged["steps"][0]["status"] == "pending"
|
|
822
|
-
|
|
823
|
-
def test_merge_resets_completed_prizmkit_test_for_sibling_workspace_report(self, tmp_path):
|
|
824
|
-
project_root = tmp_path / "project"
|
|
825
|
-
sibling_root = tmp_path / "project-sibling"
|
|
826
|
-
in_project_report = project_root / ".prizmkit" / "test" / "run" / "test-report.md"
|
|
827
|
-
sibling_report = sibling_root / ".prizmkit" / "test" / "run" / "test-report.md"
|
|
828
|
-
pointer = project_root / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
|
|
829
|
-
for path in (in_project_report.parent, sibling_report.parent, pointer.parent):
|
|
830
|
-
path.mkdir(parents=True, exist_ok=True)
|
|
831
|
-
report_text = scoped_report_text()
|
|
832
|
-
in_project_report.write_text(report_text, encoding="utf-8")
|
|
833
|
-
sibling_report.write_text(report_text, encoding="utf-8")
|
|
834
|
-
pointer.write_text(f"{sibling_report}\n", encoding="utf-8")
|
|
835
|
-
existing = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "completed", "required_artifacts": [
|
|
836
|
-
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
837
|
-
".prizmkit/test/*/test-report.md",
|
|
838
|
-
]}]}
|
|
839
|
-
fresh = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "pending", "required_artifacts": [
|
|
840
|
-
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
841
|
-
".prizmkit/test/*/test-report.md",
|
|
842
|
-
]}]}
|
|
843
|
-
|
|
844
|
-
merged = merge_checkpoint_state(existing, fresh, str(project_root))
|
|
845
|
-
|
|
846
|
-
assert merged["steps"][0]["status"] == "pending"
|
|
847
|
-
|
|
848
|
-
def test_merge_resets_completed_prizmkit_test_for_minimal_boundary_matrix(self, tmp_path):
|
|
849
|
-
report = tmp_path / ".prizmkit" / "test" / "run" / "test-report.md"
|
|
850
|
-
report.parent.mkdir(parents=True)
|
|
851
|
-
report.write_text(
|
|
852
|
-
"\n".join([
|
|
853
|
-
"# Test Report",
|
|
854
|
-
"",
|
|
855
|
-
"## Scope",
|
|
856
|
-
"- Mode: this-change",
|
|
857
|
-
"- Artifact Dir: .prizmkit/specs/123-scope-test/",
|
|
858
|
-
"",
|
|
859
|
-
"## Boundary Matrix",
|
|
860
|
-
"| Interface | Happy Path | N/A Reasons | Final Status |",
|
|
861
|
-
"|-----------|------------|-------------|--------------|",
|
|
862
|
-
"| POST /feature | covered | none | boundary-covered |",
|
|
863
|
-
"",
|
|
864
|
-
"## Boundary Completion Gate",
|
|
865
|
-
"- Interfaces total: 1",
|
|
866
|
-
"- Boundary-covered: 1",
|
|
867
|
-
"- Boundary-missing: 0",
|
|
868
|
-
"- Happy-path-only: 0",
|
|
869
|
-
"- Completion gate passed: yes",
|
|
870
|
-
"",
|
|
871
|
-
"## Boundary Validation",
|
|
872
|
-
"- Validator result: skipped",
|
|
873
|
-
"",
|
|
874
|
-
"## Verdict",
|
|
875
|
-
"PASS",
|
|
876
|
-
"",
|
|
877
|
-
]),
|
|
878
|
-
encoding="utf-8",
|
|
879
|
-
)
|
|
880
|
-
pointer = tmp_path / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
|
|
881
|
-
pointer.parent.mkdir(parents=True, exist_ok=True)
|
|
882
|
-
pointer.write_text(".prizmkit/test/run/test-report.md\n", encoding="utf-8")
|
|
883
|
-
existing = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "completed", "required_artifacts": [
|
|
884
|
-
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
885
|
-
".prizmkit/test/*/test-report.md",
|
|
886
|
-
]}]}
|
|
887
|
-
fresh = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "pending", "required_artifacts": [
|
|
888
|
-
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
889
|
-
".prizmkit/test/*/test-report.md",
|
|
890
|
-
]}]}
|
|
891
|
-
|
|
892
|
-
merged = merge_checkpoint_state(existing, fresh, str(tmp_path))
|
|
893
|
-
|
|
894
|
-
assert merged["steps"][0]["status"] == "pending"
|
|
895
|
-
|
|
896
|
-
def test_merge_resets_completed_prizmkit_test_for_missing_boundary_metrics(self, tmp_path):
|
|
897
|
-
report = tmp_path / ".prizmkit" / "test" / "run" / "test-report.md"
|
|
898
|
-
report_text = scoped_report_text().replace("- Boundary-missing: 0\n", "").replace("- Happy-path-only: 0\n", "")
|
|
899
|
-
write_fresh_scoped_report(tmp_path, report, report_text)
|
|
900
|
-
pointer = tmp_path / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
|
|
901
|
-
pointer.parent.mkdir(parents=True, exist_ok=True)
|
|
902
|
-
pointer.write_text(".prizmkit/test/run/test-report.md\n", encoding="utf-8")
|
|
903
|
-
existing = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "completed", "required_artifacts": [
|
|
904
|
-
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
905
|
-
".prizmkit/test/*/test-report.md",
|
|
906
|
-
]}]}
|
|
907
|
-
fresh = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "pending", "required_artifacts": [
|
|
908
|
-
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
909
|
-
".prizmkit/test/*/test-report.md",
|
|
910
|
-
]}]}
|
|
911
499
|
|
|
912
|
-
|
|
500
|
+
assert skills.index("prizmkit-code-review") < skills.index("prizmkit-test")
|
|
501
|
+
assert test_step["required_artifacts"] == self._artifacts()
|
|
913
502
|
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
existing = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "completed", "required_artifacts": [
|
|
925
|
-
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
926
|
-
".prizmkit/test/*/test-report.md",
|
|
927
|
-
]}]}
|
|
928
|
-
fresh = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "pending", "required_artifacts": [
|
|
929
|
-
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
930
|
-
".prizmkit/test/*/test-report.md",
|
|
931
|
-
]}]}
|
|
503
|
+
def test_merge_keeps_only_consistent_test_pass_artifacts(self, tmp_path):
|
|
504
|
+
self._write_result(tmp_path)
|
|
505
|
+
existing = {"steps": [{
|
|
506
|
+
"id": "S01", "skill": "prizmkit-test", "status": "completed",
|
|
507
|
+
"stage_result": "TEST_PASS", "required_artifacts": self._artifacts(),
|
|
508
|
+
}]}
|
|
509
|
+
fresh = {"steps": [{
|
|
510
|
+
"id": "S01", "skill": "prizmkit-test", "status": "pending",
|
|
511
|
+
"required_artifacts": self._artifacts(),
|
|
512
|
+
}]}
|
|
932
513
|
|
|
933
514
|
merged = merge_checkpoint_state(existing, fresh, str(tmp_path))
|
|
934
515
|
|
|
935
|
-
assert merged["steps"][0]["status"] == "
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
]
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
]
|
|
516
|
+
assert merged["steps"][0]["status"] == "completed"
|
|
517
|
+
assert merged["steps"][0]["stage_result"] == "TEST_PASS"
|
|
518
|
+
|
|
519
|
+
def test_merge_resets_missing_or_non_pass_test_artifacts(self, tmp_path):
|
|
520
|
+
existing = {"steps": [{
|
|
521
|
+
"id": "S01", "skill": "prizmkit-test", "status": "completed",
|
|
522
|
+
"stage_result": "TEST_PASS", "required_artifacts": self._artifacts(),
|
|
523
|
+
}]}
|
|
524
|
+
fresh = {"steps": [{
|
|
525
|
+
"id": "S01", "skill": "prizmkit-test", "status": "pending",
|
|
526
|
+
"required_artifacts": self._artifacts(),
|
|
527
|
+
}]}
|
|
528
|
+
missing = merge_checkpoint_state(existing, fresh, str(tmp_path))
|
|
529
|
+
assert missing["steps"][0]["status"] == "pending"
|
|
530
|
+
|
|
531
|
+
self._write_result(tmp_path, "TEST_NEEDS_FIXES", unresolved=["known correction"])
|
|
532
|
+
needs_fixes = merge_checkpoint_state(existing, fresh, str(tmp_path))
|
|
533
|
+
assert needs_fixes["steps"][0]["status"] == "pending"
|
|
534
|
+
|
|
535
|
+
def test_merge_rejects_malformed_or_inconsistent_terminal_projection(self, tmp_path):
|
|
536
|
+
self._write_result(tmp_path)
|
|
537
|
+
result_path = tmp_path / ".prizmkit/specs/123-scope-test/test-result.json"
|
|
538
|
+
payload = json.loads(result_path.read_text(encoding="utf-8"))
|
|
539
|
+
payload["review_required"] = True
|
|
540
|
+
payload["review_scope"] = None
|
|
541
|
+
result_path.write_text(json.dumps(payload), encoding="utf-8")
|
|
542
|
+
existing = {"steps": [{
|
|
543
|
+
"id": "S01", "skill": "prizmkit-test", "status": "completed",
|
|
544
|
+
"stage_result": "TEST_PASS", "required_artifacts": self._artifacts(),
|
|
545
|
+
}]}
|
|
546
|
+
fresh = {"steps": [{
|
|
547
|
+
"id": "S01", "skill": "prizmkit-test", "status": "pending",
|
|
548
|
+
"required_artifacts": self._artifacts(),
|
|
549
|
+
}]}
|
|
953
550
|
|
|
954
551
|
merged = merge_checkpoint_state(existing, fresh, str(tmp_path))
|
|
955
552
|
|
|
956
553
|
assert merged["steps"][0]["status"] == "pending"
|
|
957
554
|
|
|
958
|
-
def
|
|
959
|
-
report = tmp_path / ".prizmkit" / "test" / "run" / "test-report.md"
|
|
960
|
-
report_text = scoped_report_text().replace("- In-Scope Test Files:\n - tests/feature.test.js\n", "- In-Scope Test Files:\n")
|
|
961
|
-
write_fresh_scoped_report(tmp_path, report, report_text)
|
|
962
|
-
pointer = tmp_path / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
|
|
963
|
-
pointer.write_text(".prizmkit/test/run/test-report.md\n", encoding="utf-8")
|
|
964
|
-
existing = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "completed", "required_artifacts": [
|
|
965
|
-
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
966
|
-
".prizmkit/test/*/test-report.md",
|
|
967
|
-
]}]}
|
|
968
|
-
fresh = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "pending", "required_artifacts": [
|
|
969
|
-
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
970
|
-
".prizmkit/test/*/test-report.md",
|
|
971
|
-
]}]}
|
|
972
|
-
|
|
973
|
-
merged = merge_checkpoint_state(existing, fresh, str(tmp_path))
|
|
974
|
-
|
|
975
|
-
assert merged["steps"][0]["status"] == "pending"
|
|
976
|
-
|
|
977
|
-
def test_structured_evidence_accepts_current_matching_package(self, tmp_path, monkeypatch):
|
|
978
|
-
self._trust_fixture_validator(monkeypatch)
|
|
979
|
-
evidence_root = write_fresh_structured_evidence(tmp_path)
|
|
980
|
-
|
|
981
|
-
evidence = validate_feature_test_evidence(
|
|
982
|
-
str(tmp_path), ".prizmkit/specs/123-scope-test",
|
|
983
|
-
)
|
|
984
|
-
|
|
985
|
-
assert evidence is not None
|
|
986
|
-
assert evidence["verdict"] == "TEST_PASS"
|
|
987
|
-
assert evidence["manifest_path"] == str(
|
|
988
|
-
(evidence_root / "manifest.json").relative_to(tmp_path),
|
|
989
|
-
)
|
|
990
|
-
assert evidence["validation_path"].endswith("/validation.json")
|
|
991
|
-
|
|
992
|
-
def test_structured_evidence_requires_authoritative_validator(self, tmp_path, monkeypatch):
|
|
993
|
-
write_fresh_structured_evidence(tmp_path)
|
|
994
|
-
monkeypatch.setattr(
|
|
995
|
-
"generate_bootstrap_prompt._evidence_validator_passes",
|
|
996
|
-
lambda _project_root, _evidence_root, _validation: False,
|
|
997
|
-
)
|
|
998
|
-
|
|
999
|
-
assert validate_feature_test_evidence(
|
|
1000
|
-
str(tmp_path), ".prizmkit/specs/123-scope-test",
|
|
1001
|
-
) is None
|
|
1002
|
-
|
|
1003
|
-
def test_structured_evidence_rejects_legacy_markdown_only_pass(self, tmp_path):
|
|
1004
|
-
report = tmp_path / ".prizmkit" / "test" / "run" / "test-report.md"
|
|
1005
|
-
write_fresh_scoped_report(tmp_path, report)
|
|
1006
|
-
pointer = tmp_path / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
|
|
1007
|
-
pointer.write_text(str(report), encoding="utf-8")
|
|
1008
|
-
|
|
1009
|
-
assert validate_feature_test_evidence(
|
|
1010
|
-
str(tmp_path), ".prizmkit/specs/123-scope-test",
|
|
1011
|
-
) is None
|
|
1012
|
-
|
|
1013
|
-
def test_structured_evidence_rejects_missing_validation(self, tmp_path):
|
|
1014
|
-
evidence_root = write_fresh_structured_evidence(tmp_path)
|
|
1015
|
-
(evidence_root / "validation.json").unlink()
|
|
1016
|
-
|
|
1017
|
-
assert validate_feature_test_evidence(
|
|
1018
|
-
str(tmp_path), ".prizmkit/specs/123-scope-test",
|
|
1019
|
-
) is None
|
|
1020
|
-
|
|
1021
|
-
def test_structured_evidence_rejects_tampered_verdict(self, tmp_path, monkeypatch):
|
|
1022
|
-
self._trust_fixture_validator(monkeypatch)
|
|
1023
|
-
evidence_root = write_fresh_structured_evidence(tmp_path)
|
|
1024
|
-
verdict_path = evidence_root / "verdict.json"
|
|
1025
|
-
verdict = json.loads(verdict_path.read_text(encoding="utf-8"))
|
|
1026
|
-
verdict["verdict"] = "TEST_BLOCKED"
|
|
1027
|
-
verdict_path.write_text(json.dumps(verdict), encoding="utf-8")
|
|
1028
|
-
|
|
1029
|
-
assert validate_feature_test_evidence(
|
|
1030
|
-
str(tmp_path), ".prizmkit/specs/123-scope-test",
|
|
1031
|
-
) is None
|
|
1032
|
-
|
|
1033
|
-
def test_structured_evidence_rejects_wrong_artifact_scope(self, tmp_path, monkeypatch):
|
|
1034
|
-
self._trust_fixture_validator(monkeypatch)
|
|
1035
|
-
evidence_root = write_fresh_structured_evidence(tmp_path)
|
|
1036
|
-
report_path = evidence_root / "test-report.md"
|
|
1037
|
-
report_path.write_text(
|
|
1038
|
-
scoped_report_text().replace(
|
|
1039
|
-
".prizmkit/specs/123-scope-test/",
|
|
1040
|
-
".prizmkit/specs/999-other/",
|
|
1041
|
-
),
|
|
1042
|
-
encoding="utf-8",
|
|
1043
|
-
)
|
|
1044
|
-
|
|
1045
|
-
assert validate_feature_test_evidence(
|
|
1046
|
-
str(tmp_path), ".prizmkit/specs/123-scope-test",
|
|
1047
|
-
) is None
|
|
1048
|
-
|
|
1049
|
-
def test_structured_evidence_rejects_stale_report(self, tmp_path, monkeypatch):
|
|
1050
|
-
self._trust_fixture_validator(monkeypatch)
|
|
1051
|
-
evidence_root = write_fresh_structured_evidence(tmp_path)
|
|
1052
|
-
report = evidence_root / "test-report.md"
|
|
1053
|
-
marker = tmp_path / ".prizmkit" / "specs" / "123-scope-test" / ".prizmkit-test-started"
|
|
1054
|
-
now = time.time()
|
|
1055
|
-
os.utime(report, (now, now))
|
|
1056
|
-
os.utime(marker, (now + 10, now + 10))
|
|
1057
|
-
|
|
1058
|
-
assert validate_feature_test_evidence(
|
|
1059
|
-
str(tmp_path), ".prizmkit/specs/123-scope-test",
|
|
1060
|
-
) is None
|
|
1061
|
-
|
|
1062
|
-
def test_structured_evidence_rejects_live_inventory_drift(self, tmp_path, monkeypatch):
|
|
1063
|
-
self._trust_fixture_validator(monkeypatch)
|
|
1064
|
-
write_fresh_structured_evidence(tmp_path)
|
|
1065
|
-
source = tmp_path / "src" / "feature.js"
|
|
1066
|
-
source.write_text("export const feature = false;\n", encoding="utf-8")
|
|
1067
|
-
|
|
1068
|
-
assert validate_feature_test_evidence(
|
|
1069
|
-
str(tmp_path), ".prizmkit/specs/123-scope-test",
|
|
1070
|
-
) is None
|
|
1071
|
-
|
|
1072
|
-
def test_feature_repair_routing_preserves_review_and_test_boundaries(self):
|
|
555
|
+
def test_feature_result_routing_stops_test_non_pass_without_outer_repair(self):
|
|
1073
556
|
review = route_feature_repair("REVIEW_NEEDS_FIXES", repair_round=0)
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
)
|
|
1077
|
-
production = route_feature_repair("TEST_FAIL", "production", repair_round=1)
|
|
557
|
+
needs_fixes = route_feature_repair("TEST_NEEDS_FIXES", repair_round=1)
|
|
558
|
+
blocked = route_feature_repair("TEST_BLOCKED", repair_round=1)
|
|
1078
559
|
|
|
1079
560
|
assert review["route"] == [
|
|
1080
561
|
"prizmkit-implement", "prizmkit-code-review", "prizmkit-test",
|
|
1081
562
|
]
|
|
1082
|
-
assert test_infra["route"] == ["prizmkit-implement", "prizmkit-test"]
|
|
1083
|
-
assert production["route"] == [
|
|
1084
|
-
"prizmkit-implement", "prizmkit-code-review", "prizmkit-test",
|
|
1085
|
-
]
|
|
1086
563
|
assert review["repair_round"] == 1
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
blocked = route_feature_repair("TEST_BLOCKED", "production", repair_round=1)
|
|
1091
|
-
exhausted = route_feature_repair("TEST_FAIL", "production", repair_round=3)
|
|
1092
|
-
|
|
1093
|
-
assert unknown["blocked"] is True
|
|
1094
|
-
assert unknown["route"] == []
|
|
564
|
+
assert needs_fixes["blocked"] is True
|
|
565
|
+
assert needs_fixes["route"] == []
|
|
566
|
+
assert needs_fixes["blocked_reason"] == "test_needs_fixes"
|
|
1095
567
|
assert blocked["blocked"] is True
|
|
1096
|
-
assert blocked["resumable"] is False
|
|
1097
|
-
assert blocked["next_stage"] is None
|
|
1098
|
-
assert blocked["terminal_status"] == "WORKFLOW_BLOCKED"
|
|
1099
568
|
assert blocked["blocked_reason"] == "test_blocked"
|
|
1100
|
-
assert
|
|
1101
|
-
assert exhausted["repair_round"] == 3
|
|
1102
|
-
|
|
1103
|
-
def test_feature_checkpoint_metadata_records_l1_handoff_fields(self):
|
|
1104
|
-
evidence = {
|
|
1105
|
-
"manifest_path": ".prizmkit/test/evidence/id/manifest.json",
|
|
1106
|
-
"verdict_path": ".prizmkit/test/evidence/id/verdict.json",
|
|
1107
|
-
"validation_path": ".prizmkit/test/evidence/id/validation.json",
|
|
1108
|
-
}
|
|
569
|
+
assert blocked["terminal_status"] == "WORKFLOW_BLOCKED"
|
|
1109
570
|
|
|
571
|
+
def test_pipeline_checkpoint_metadata_rejects_legacy_or_mismatched_status(self):
|
|
572
|
+
invalid = [
|
|
573
|
+
{"status": "TEST_PASS", "stage_result": "TEST_PASS"},
|
|
574
|
+
{"status": "failed", "stage_result": "TEST_PASS"},
|
|
575
|
+
{"status": "completed", "stage_result": "UNKNOWN_RESULT"},
|
|
576
|
+
]
|
|
577
|
+
|
|
578
|
+
for values in invalid:
|
|
579
|
+
try:
|
|
580
|
+
pipeline_checkpoint_metadata(
|
|
581
|
+
".prizmkit/specs/contract-test",
|
|
582
|
+
stage="test",
|
|
583
|
+
**values,
|
|
584
|
+
)
|
|
585
|
+
except ValueError:
|
|
586
|
+
continue
|
|
587
|
+
raise AssertionError("invalid lifecycle metadata was accepted: {}".format(values))
|
|
588
|
+
|
|
589
|
+
def test_feature_checkpoint_metadata_has_no_evidence_paths(self):
|
|
1110
590
|
metadata = feature_checkpoint_metadata(
|
|
1111
591
|
".prizmkit/specs/123-scope-test",
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
stage_result="TEST_FAIL",
|
|
1116
|
-
repair_scope="test-infrastructure",
|
|
1117
|
-
repair_round=2,
|
|
1118
|
-
next_stage="prizmkit-implement",
|
|
1119
|
-
resume_from="prizmkit-implement",
|
|
592
|
+
stage="test", status="failed",
|
|
593
|
+
stage_result="TEST_NEEDS_FIXES", repair_round=0,
|
|
594
|
+
next_stage=None, resume_from=None,
|
|
1120
595
|
)
|
|
1121
596
|
|
|
1122
|
-
assert metadata ==
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
"stage": "test",
|
|
1127
|
-
"current_stage": "test",
|
|
1128
|
-
"status": "TEST_FAIL",
|
|
1129
|
-
"stage_result": "TEST_FAIL",
|
|
1130
|
-
"repair_scope": "test-infrastructure",
|
|
1131
|
-
"repair_round": 2,
|
|
1132
|
-
"authoritative_evidence_paths": {
|
|
1133
|
-
"manifest": ".prizmkit/test/evidence/id/manifest.json",
|
|
1134
|
-
"verdict": ".prizmkit/test/evidence/id/verdict.json",
|
|
1135
|
-
"validation": ".prizmkit/test/evidence/id/validation.json",
|
|
1136
|
-
},
|
|
1137
|
-
"next_stage": "prizmkit-implement",
|
|
1138
|
-
"resume_from": "prizmkit-implement",
|
|
1139
|
-
"blocked_reason": None,
|
|
1140
|
-
"terminal_status": None,
|
|
1141
|
-
}
|
|
597
|
+
assert metadata["status"] == "failed"
|
|
598
|
+
assert metadata["stage_result"] == "TEST_NEEDS_FIXES"
|
|
599
|
+
assert "authoritative_evidence_paths" not in metadata
|
|
600
|
+
assert metadata["artifact_dir"] == ".prizmkit/specs/123-scope-test"
|
|
1142
601
|
|
|
1143
602
|
# ---------------------------------------------------------------------------
|
|
1144
603
|
# Active compaction harness rollback
|
|
@@ -1605,7 +1064,8 @@ class TestFeaturePromptUnifiedFullGuidance:
|
|
|
1605
1064
|
assert "### Plan Self-Check — Orchestrator Direct" in prompt
|
|
1606
1065
|
assert "### Scoped Feature Test Gate — PrizmKit Test" in prompt
|
|
1607
1066
|
assert "## Test Failure Recovery Protocol" in prompt
|
|
1608
|
-
assert "
|
|
1067
|
+
assert "test-report.md" in prompt
|
|
1068
|
+
assert "test-result.json" in prompt
|
|
1609
1069
|
assert "### Browser Verification — MANDATORY" not in prompt
|
|
1610
1070
|
assert "### TERMINAL STOP — Commit Completed" in prompt
|
|
1611
1071
|
assert "# Dev-Pipeline Session Bootstrap — Tier 1" not in prompt
|
|
@@ -1847,7 +1307,8 @@ class TestHeadlessPromptCleanupF033:
|
|
|
1847
1307
|
assert forbidden not in prompt
|
|
1848
1308
|
if "Feature ID" in prompt:
|
|
1849
1309
|
assert "/prizmkit-test scope=this-change artifact_dir=.prizmkit/specs/123-prompt-cleanup/" in prompt
|
|
1850
|
-
assert "
|
|
1310
|
+
assert "test-report.md" in prompt
|
|
1311
|
+
assert "test-result.json" in prompt
|
|
1851
1312
|
assert "{{RUNTIME_HELPER_CMD}}" not in prompt
|
|
1852
1313
|
assert "prizmkit-runtime-helper.py" in prompt
|
|
1853
1314
|
# Anti-Bash guardrails for bugfix/refactor branches (F-039)
|
|
@@ -1874,7 +1335,7 @@ class TestHeadlessPromptCleanupF033:
|
|
|
1874
1335
|
assert "which playwright-cli" not in prompt
|
|
1875
1336
|
assert "lsof -ti" not in prompt
|
|
1876
1337
|
assert "sed -nE" not in prompt
|
|
1877
|
-
assert ".prizmkit-test-started" in prompt
|
|
1338
|
+
assert ".prizmkit-test-started" not in prompt
|
|
1878
1339
|
assert "{{RUNTIME_HELPER_CMD}}" not in prompt
|
|
1879
1340
|
assert "prizmkit-runtime-helper.py" in prompt
|
|
1880
1341
|
|
|
@@ -1991,7 +1452,6 @@ class TestFeatureBootstrapShellExtraction:
|
|
|
1991
1452
|
assert "{{RUNTIME_HELPER_CMD}}" not in prompt
|
|
1992
1453
|
assert "python" in prompt.lower()
|
|
1993
1454
|
assert ".prizmkit/dev-pipeline/scripts/prizmkit-runtime-helper.py" in prompt
|
|
1994
|
-
assert "artifact touch .prizmkit/specs/123-prompt-cleanup/.prizmkit-test-started" in prompt
|
|
1995
1455
|
assert "text final-verdict .prizmkit/specs/123-prompt-cleanup/review-report.md" in prompt
|
|
1996
1456
|
|
|
1997
1457
|
def test_rendered_section_feature_prompts_use_runtime_helper_not_extracted_bash(self, tmp_path):
|