prizmkit 1.1.139 → 1.1.141
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/prizmkit_runtime/checkpoint_state.py +41 -121
- package/bundled/dev-pipeline/prizmkit_runtime/runner_models.py +0 -6
- package/bundled/dev-pipeline/prizmkit_runtime/runners.py +1 -2
- package/bundled/dev-pipeline/prizmkit_runtime/test_result.py +150 -0
- package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +21 -804
- package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +4 -6
- package/bundled/dev-pipeline/scripts/generate-recovery-prompt.py +1 -1
- package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +4 -6
- package/bundled/dev-pipeline/scripts/prompt_framework.py +56 -55
- package/bundled/dev-pipeline/scripts/update-bug-status.py +1 -2
- package/bundled/dev-pipeline/scripts/update-checkpoint.py +14 -96
- package/bundled/dev-pipeline/scripts/update-feature-status.py +1 -2
- package/bundled/dev-pipeline/scripts/update-refactor-status.py +1 -2
- package/bundled/dev-pipeline/templates/bootstrap-prompt.md +3 -3
- package/bundled/dev-pipeline/templates/bootstrap-tier3.md +7 -18
- 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-test.md +13 -10
- package/bundled/dev-pipeline/templates/sections/bugfix-reminders.md +1 -1
- package/bundled/dev-pipeline/templates/sections/checkpoint-system.md +14 -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-specify-plan-full.md +3 -3
- 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/tests/test_checkpoint_state.py +287 -384
- package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +119 -684
- package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +17 -40
- package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +20 -23
- package/bundled/dev-pipeline/tests/test_python_runner_parity.py +29 -32
- package/bundled/skills/_metadata.json +6 -6
- package/bundled/skills/prizmkit/SKILL.md +8 -13
- package/bundled/skills/prizmkit/references/workflow-state-protocol.md +61 -80
- package/bundled/skills/prizmkit-code-review/SKILL.md +3 -3
- package/bundled/skills/prizmkit-code-review/references/workflow-state-protocol.md +61 -80
- package/bundled/skills/prizmkit-committer/SKILL.md +3 -3
- package/bundled/skills/prizmkit-committer/references/workflow-state-protocol.md +61 -80
- package/bundled/skills/prizmkit-implement/references/workflow-state-protocol.md +61 -80
- package/bundled/skills/prizmkit-plan/references/workflow-state-protocol.md +61 -80
- package/bundled/skills/prizmkit-retrospective/SKILL.md +2 -2
- package/bundled/skills/prizmkit-retrospective/references/workflow-state-protocol.md +61 -80
- package/bundled/skills/prizmkit-test/SKILL.md +217 -187
- 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 +15 -23
- package/bundled/skills/prizmkit-workflow/references/workflow-state-protocol.md +61 -80
- package/package.json +1 -1
- package/bundled/skills/prizmkit-test/assets/authoritative-records.schema.json +0 -476
- 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 -110
- package/bundled/skills/prizmkit-test/references/workflow-state-protocol.md +0 -181
- package/bundled/skills/prizmkit-test/scripts/build_test_evidence.py +0 -1851
- package/bundled/skills/prizmkit-test/scripts/validate_test_evidence.py +0 -1793
|
@@ -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,7 +26,6 @@ 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,
|
|
@@ -112,197 +109,6 @@ def test_rendered_prompt_validation_rejects_unresolved_control_placeholders():
|
|
|
112
109
|
assert errors == ["Unreplaced placeholders: {{INIT_SCRIPT_PATH}}"]
|
|
113
110
|
|
|
114
111
|
|
|
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
112
|
|
|
307
113
|
# ---------------------------------------------------------------------------
|
|
308
114
|
# compute_feature_slug
|
|
@@ -601,544 +407,172 @@ class TestProcessModeBlocks:
|
|
|
601
407
|
|
|
602
408
|
class TestScopedFeatureTestGate:
|
|
603
409
|
@staticmethod
|
|
604
|
-
def
|
|
605
|
-
|
|
606
|
-
"
|
|
607
|
-
|
|
410
|
+
def _artifacts(slug="123-scope-test"):
|
|
411
|
+
return [
|
|
412
|
+
f".prizmkit/specs/{slug}/test-report.md",
|
|
413
|
+
f".prizmkit/specs/{slug}/test-result.json",
|
|
414
|
+
]
|
|
415
|
+
|
|
416
|
+
@staticmethod
|
|
417
|
+
def _write_result(root, result="TEST_PASS", *, unresolved=None,
|
|
418
|
+
review_required=False, review_scope=None):
|
|
419
|
+
artifact = root / ".prizmkit" / "specs" / "123-scope-test"
|
|
420
|
+
artifact.mkdir(parents=True, exist_ok=True)
|
|
421
|
+
(artifact / "test-report.md").write_text(
|
|
422
|
+
f"# Test Report\n\n## Final Result\n\n- Result: {result}\n",
|
|
423
|
+
encoding="utf-8",
|
|
424
|
+
)
|
|
425
|
+
payload = {
|
|
426
|
+
"schema_version": 1,
|
|
427
|
+
"result": result,
|
|
428
|
+
"report": "test-report.md",
|
|
429
|
+
"main_review_rounds": 1,
|
|
430
|
+
"independent_review": {
|
|
431
|
+
"status": "downgraded",
|
|
432
|
+
"responses": 0,
|
|
433
|
+
"downgrade_reason": "read-only continuation capability unavailable",
|
|
434
|
+
"final_state_rechecked": False,
|
|
435
|
+
},
|
|
436
|
+
"repair_rounds": 0,
|
|
437
|
+
"production_changed": False,
|
|
438
|
+
"review_required": review_required,
|
|
439
|
+
"review_scope": review_scope,
|
|
440
|
+
"unresolved_items": unresolved or [],
|
|
441
|
+
}
|
|
442
|
+
(artifact / "test-result.json").write_text(
|
|
443
|
+
json.dumps(payload), encoding="utf-8",
|
|
608
444
|
)
|
|
609
445
|
|
|
610
446
|
def test_standard_sections_insert_prizmkit_test_after_review(self):
|
|
611
447
|
sections_dir = Path(__file__).resolve().parents[1] / "templates" / "sections"
|
|
612
448
|
sections = assemble_sections(
|
|
613
|
-
"standard",
|
|
614
|
-
|
|
615
|
-
init_done=True,
|
|
616
|
-
is_resume=False,
|
|
617
|
-
browser_enabled=False,
|
|
449
|
+
"standard", str(sections_dir), init_done=True,
|
|
450
|
+
is_resume=False, browser_enabled=False,
|
|
618
451
|
)
|
|
619
452
|
names = [name for name, _ in sections]
|
|
453
|
+
rendered = "\n".join(content for _, content in sections)
|
|
620
454
|
|
|
621
|
-
assert "phase-prizmkit-test" in names
|
|
622
455
|
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
456
|
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
|
|
457
|
+
assert "test-report.md" in rendered
|
|
458
|
+
assert "test-result.json" in rendered
|
|
459
|
+
assert "TEST_NEEDS_FIXES" in rendered
|
|
460
|
+
assert ".prizmkit/test/evidence" not in rendered
|
|
461
|
+
assert "TEST_FAIL" not in rendered
|
|
643
462
|
|
|
644
463
|
def test_standard_sections_include_central_checkpoint_helper(self):
|
|
645
464
|
sections_dir = Path(__file__).resolve().parents[1] / "templates" / "sections"
|
|
646
465
|
sections = assemble_sections(
|
|
647
|
-
"standard",
|
|
648
|
-
|
|
649
|
-
init_done=True,
|
|
650
|
-
is_resume=False,
|
|
651
|
-
browser_enabled=False,
|
|
466
|
+
"standard", str(sections_dir), init_done=True,
|
|
467
|
+
is_resume=False, browser_enabled=False,
|
|
652
468
|
)
|
|
653
469
|
rendered = "\n".join(content for _, content in sections)
|
|
654
470
|
|
|
655
471
|
assert "### Checkpoint Update Helper" in rendered
|
|
656
472
|
assert "{{CHECKPOINT_PYTHON_CMD}}" in rendered
|
|
657
|
-
assert "python3 .prizmkit/dev-pipeline/scripts/update-checkpoint.py" not in rendered
|
|
658
473
|
assert "--checkpoint-path {{CHECKPOINT_PATH}}" in rendered
|
|
659
|
-
assert "--
|
|
474
|
+
assert "--stage-result <optional-domain-result>" in rendered
|
|
475
|
+
assert "--status completed --stage-result REVIEW_PASS" in rendered
|
|
476
|
+
assert "--status completed --stage-result TEST_PASS" in rendered
|
|
660
477
|
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]
|
|
478
|
+
assert "authoritative_evidence_paths" not in rendered
|
|
673
479
|
|
|
674
|
-
|
|
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")
|
|
677
|
-
|
|
678
|
-
def test_checkpoint_orders_prizmkit_test_after_review(self):
|
|
480
|
+
def test_checkpoint_orders_test_after_review_with_terminal_artifacts(self):
|
|
679
481
|
sections_dir = Path(__file__).resolve().parents[1] / "templates" / "sections"
|
|
680
482
|
sections = assemble_sections(
|
|
681
|
-
"standard",
|
|
682
|
-
|
|
683
|
-
init_done=True,
|
|
684
|
-
is_resume=False,
|
|
685
|
-
browser_enabled=False,
|
|
483
|
+
"standard", str(sections_dir), init_done=True,
|
|
484
|
+
is_resume=False, browser_enabled=False,
|
|
686
485
|
)
|
|
687
486
|
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,
|
|
487
|
+
sections, pipeline_mode="standard", workflow_type="feature-pipeline",
|
|
488
|
+
item_id="F-123", item_slug="123-scope-test",
|
|
489
|
+
session_id="F-123-session", init_done=True,
|
|
695
490
|
)
|
|
696
491
|
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
492
|
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
493
|
|
|
846
|
-
assert
|
|
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
|
-
|
|
912
|
-
merged = merge_checkpoint_state(existing, fresh, str(tmp_path))
|
|
913
|
-
|
|
914
|
-
assert merged["steps"][0]["status"] == "pending"
|
|
915
|
-
|
|
916
|
-
def test_merge_resets_completed_prizmkit_test_when_marker_newer_than_report(self, tmp_path):
|
|
917
|
-
report = tmp_path / ".prizmkit" / "test" / "run" / "test-report.md"
|
|
918
|
-
_source, _test, marker = write_fresh_scoped_report(tmp_path, report)
|
|
919
|
-
now = time.time()
|
|
920
|
-
os.utime(report, (now, now))
|
|
921
|
-
os.utime(marker, (now + 10, now + 10))
|
|
922
|
-
pointer = tmp_path / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
|
|
923
|
-
pointer.write_text(".prizmkit/test/run/test-report.md\n", encoding="utf-8")
|
|
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
|
-
]}]}
|
|
932
|
-
|
|
933
|
-
merged = merge_checkpoint_state(existing, fresh, str(tmp_path))
|
|
934
|
-
|
|
935
|
-
assert merged["steps"][0]["status"] == "pending"
|
|
494
|
+
assert skills.index("prizmkit-code-review") < skills.index("prizmkit-test")
|
|
495
|
+
assert test_step["required_artifacts"] == self._artifacts()
|
|
936
496
|
|
|
937
|
-
def
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
".prizmkit/test/*/test-report.md",
|
|
948
|
-
]}]}
|
|
949
|
-
fresh = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "pending", "required_artifacts": [
|
|
950
|
-
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
951
|
-
".prizmkit/test/*/test-report.md",
|
|
952
|
-
]}]}
|
|
497
|
+
def test_merge_keeps_only_consistent_test_pass_artifacts(self, tmp_path):
|
|
498
|
+
self._write_result(tmp_path)
|
|
499
|
+
existing = {"steps": [{
|
|
500
|
+
"id": "S01", "skill": "prizmkit-test", "status": "completed",
|
|
501
|
+
"stage_result": "TEST_PASS", "required_artifacts": self._artifacts(),
|
|
502
|
+
}]}
|
|
503
|
+
fresh = {"steps": [{
|
|
504
|
+
"id": "S01", "skill": "prizmkit-test", "status": "pending",
|
|
505
|
+
"required_artifacts": self._artifacts(),
|
|
506
|
+
}]}
|
|
953
507
|
|
|
954
508
|
merged = merge_checkpoint_state(existing, fresh, str(tmp_path))
|
|
955
509
|
|
|
956
|
-
assert merged["steps"][0]["status"] == "
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
"
|
|
966
|
-
".
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
]
|
|
510
|
+
assert merged["steps"][0]["status"] == "completed"
|
|
511
|
+
assert merged["steps"][0]["stage_result"] == "TEST_PASS"
|
|
512
|
+
|
|
513
|
+
def test_merge_resets_missing_or_non_pass_test_artifacts(self, tmp_path):
|
|
514
|
+
existing = {"steps": [{
|
|
515
|
+
"id": "S01", "skill": "prizmkit-test", "status": "completed",
|
|
516
|
+
"stage_result": "TEST_PASS", "required_artifacts": self._artifacts(),
|
|
517
|
+
}]}
|
|
518
|
+
fresh = {"steps": [{
|
|
519
|
+
"id": "S01", "skill": "prizmkit-test", "status": "pending",
|
|
520
|
+
"required_artifacts": self._artifacts(),
|
|
521
|
+
}]}
|
|
522
|
+
missing = merge_checkpoint_state(existing, fresh, str(tmp_path))
|
|
523
|
+
assert missing["steps"][0]["status"] == "pending"
|
|
524
|
+
|
|
525
|
+
self._write_result(tmp_path, "TEST_NEEDS_FIXES", unresolved=["known correction"])
|
|
526
|
+
needs_fixes = merge_checkpoint_state(existing, fresh, str(tmp_path))
|
|
527
|
+
assert needs_fixes["steps"][0]["status"] == "pending"
|
|
528
|
+
|
|
529
|
+
def test_merge_rejects_malformed_or_inconsistent_terminal_projection(self, tmp_path):
|
|
530
|
+
self._write_result(tmp_path)
|
|
531
|
+
result_path = tmp_path / ".prizmkit/specs/123-scope-test/test-result.json"
|
|
532
|
+
payload = json.loads(result_path.read_text(encoding="utf-8"))
|
|
533
|
+
payload["review_required"] = True
|
|
534
|
+
payload["review_scope"] = None
|
|
535
|
+
result_path.write_text(json.dumps(payload), encoding="utf-8")
|
|
536
|
+
existing = {"steps": [{
|
|
537
|
+
"id": "S01", "skill": "prizmkit-test", "status": "completed",
|
|
538
|
+
"stage_result": "TEST_PASS", "required_artifacts": self._artifacts(),
|
|
539
|
+
}]}
|
|
540
|
+
fresh = {"steps": [{
|
|
541
|
+
"id": "S01", "skill": "prizmkit-test", "status": "pending",
|
|
542
|
+
"required_artifacts": self._artifacts(),
|
|
543
|
+
}]}
|
|
972
544
|
|
|
973
545
|
merged = merge_checkpoint_state(existing, fresh, str(tmp_path))
|
|
974
546
|
|
|
975
547
|
assert merged["steps"][0]["status"] == "pending"
|
|
976
548
|
|
|
977
|
-
def
|
|
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):
|
|
549
|
+
def test_feature_result_routing_stops_test_non_pass_without_outer_repair(self):
|
|
1073
550
|
review = route_feature_repair("REVIEW_NEEDS_FIXES", repair_round=0)
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
)
|
|
1077
|
-
production = route_feature_repair("TEST_FAIL", "production", repair_round=1)
|
|
551
|
+
needs_fixes = route_feature_repair("TEST_NEEDS_FIXES", repair_round=1)
|
|
552
|
+
blocked = route_feature_repair("TEST_BLOCKED", repair_round=1)
|
|
1078
553
|
|
|
1079
554
|
assert review["route"] == [
|
|
1080
555
|
"prizmkit-implement", "prizmkit-code-review", "prizmkit-test",
|
|
1081
556
|
]
|
|
1082
|
-
assert test_infra["route"] == ["prizmkit-implement", "prizmkit-test"]
|
|
1083
|
-
assert production["route"] == [
|
|
1084
|
-
"prizmkit-implement", "prizmkit-code-review", "prizmkit-test",
|
|
1085
|
-
]
|
|
1086
557
|
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"] == []
|
|
558
|
+
assert needs_fixes["blocked"] is True
|
|
559
|
+
assert needs_fixes["route"] == []
|
|
560
|
+
assert needs_fixes["blocked_reason"] == "test_needs_fixes"
|
|
1095
561
|
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
562
|
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
|
-
}
|
|
563
|
+
assert blocked["terminal_status"] == "WORKFLOW_BLOCKED"
|
|
1109
564
|
|
|
565
|
+
def test_feature_checkpoint_metadata_has_no_evidence_paths(self):
|
|
1110
566
|
metadata = feature_checkpoint_metadata(
|
|
1111
567
|
".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",
|
|
568
|
+
stage="test", status="TEST_NEEDS_FIXES",
|
|
569
|
+
stage_result="TEST_NEEDS_FIXES", repair_round=0,
|
|
570
|
+
next_stage=None, resume_from=None,
|
|
1120
571
|
)
|
|
1121
572
|
|
|
1122
|
-
assert metadata ==
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
"orchestrator": "prizmkit-l4",
|
|
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
|
-
}
|
|
573
|
+
assert metadata["stage_result"] == "TEST_NEEDS_FIXES"
|
|
574
|
+
assert "authoritative_evidence_paths" not in metadata
|
|
575
|
+
assert metadata["artifact_dir"] == ".prizmkit/specs/123-scope-test"
|
|
1142
576
|
|
|
1143
577
|
# ---------------------------------------------------------------------------
|
|
1144
578
|
# Active compaction harness rollback
|
|
@@ -1605,7 +1039,8 @@ class TestFeaturePromptUnifiedFullGuidance:
|
|
|
1605
1039
|
assert "### Plan Self-Check — Orchestrator Direct" in prompt
|
|
1606
1040
|
assert "### Scoped Feature Test Gate — PrizmKit Test" in prompt
|
|
1607
1041
|
assert "## Test Failure Recovery Protocol" in prompt
|
|
1608
|
-
assert "
|
|
1042
|
+
assert "test-report.md" in prompt
|
|
1043
|
+
assert "test-result.json" in prompt
|
|
1609
1044
|
assert "### Browser Verification — MANDATORY" not in prompt
|
|
1610
1045
|
assert "### TERMINAL STOP — Commit Completed" in prompt
|
|
1611
1046
|
assert "# Dev-Pipeline Session Bootstrap — Tier 1" not in prompt
|
|
@@ -1847,7 +1282,8 @@ class TestHeadlessPromptCleanupF033:
|
|
|
1847
1282
|
assert forbidden not in prompt
|
|
1848
1283
|
if "Feature ID" in prompt:
|
|
1849
1284
|
assert "/prizmkit-test scope=this-change artifact_dir=.prizmkit/specs/123-prompt-cleanup/" in prompt
|
|
1850
|
-
assert "
|
|
1285
|
+
assert "test-report.md" in prompt
|
|
1286
|
+
assert "test-result.json" in prompt
|
|
1851
1287
|
assert "{{RUNTIME_HELPER_CMD}}" not in prompt
|
|
1852
1288
|
assert "prizmkit-runtime-helper.py" in prompt
|
|
1853
1289
|
# Anti-Bash guardrails for bugfix/refactor branches (F-039)
|
|
@@ -1874,7 +1310,7 @@ class TestHeadlessPromptCleanupF033:
|
|
|
1874
1310
|
assert "which playwright-cli" not in prompt
|
|
1875
1311
|
assert "lsof -ti" not in prompt
|
|
1876
1312
|
assert "sed -nE" not in prompt
|
|
1877
|
-
assert ".prizmkit-test-started" in prompt
|
|
1313
|
+
assert ".prizmkit-test-started" not in prompt
|
|
1878
1314
|
assert "{{RUNTIME_HELPER_CMD}}" not in prompt
|
|
1879
1315
|
assert "prizmkit-runtime-helper.py" in prompt
|
|
1880
1316
|
|
|
@@ -1991,7 +1427,6 @@ class TestFeatureBootstrapShellExtraction:
|
|
|
1991
1427
|
assert "{{RUNTIME_HELPER_CMD}}" not in prompt
|
|
1992
1428
|
assert "python" in prompt.lower()
|
|
1993
1429
|
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
1430
|
assert "text final-verdict .prizmkit/specs/123-prompt-cleanup/review-report.md" in prompt
|
|
1996
1431
|
|
|
1997
1432
|
def test_rendered_section_feature_prompts_use_runtime_helper_not_extracted_bash(self, tmp_path):
|