prizmkit 1.1.86 → 1.1.87
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/scripts/generate-bootstrap-prompt.py +462 -4
- package/bundled/dev-pipeline/templates/bootstrap-prompt.md +1 -1
- package/bundled/dev-pipeline/templates/bootstrap-tier1.md +14 -0
- package/bundled/dev-pipeline/templates/bootstrap-tier2.md +18 -3
- package/bundled/dev-pipeline/templates/bootstrap-tier3.md +18 -3
- package/bundled/dev-pipeline/templates/sections/phase-commit-full.md +2 -2
- package/bundled/dev-pipeline/templates/sections/phase-commit.md +2 -2
- package/bundled/dev-pipeline/templates/sections/phase-prizmkit-test.md +260 -0
- package/bundled/dev-pipeline/templates/sections/phase-review-agent.md +5 -1
- package/bundled/dev-pipeline/templates/sections/phase-review-full.md +5 -1
- package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +429 -0
- package/bundled/dev-pipeline-windows/scripts/generate-bootstrap-prompt.py +462 -4
- package/bundled/dev-pipeline-windows/templates/bootstrap-prompt.md +1 -1
- package/bundled/dev-pipeline-windows/templates/bootstrap-tier1.md +14 -0
- package/bundled/dev-pipeline-windows/templates/bootstrap-tier2.md +18 -3
- package/bundled/dev-pipeline-windows/templates/bootstrap-tier3.md +18 -3
- package/bundled/dev-pipeline-windows/templates/sections/phase-commit-full.md +2 -2
- package/bundled/dev-pipeline-windows/templates/sections/phase-commit.md +2 -2
- package/bundled/dev-pipeline-windows/templates/sections/phase-prizmkit-test.md +186 -0
- package/bundled/dev-pipeline-windows/templates/sections/phase-review-agent.md +5 -1
- package/bundled/dev-pipeline-windows/templates/sections/phase-review-full.md +5 -1
- package/bundled/skills/_metadata.json +1 -1
- package/bundled/skills/prizmkit-code-review/SKILL.md +7 -2
- package/bundled/skills/prizmkit-code-review/references/reviewer-agent-prompt.md +6 -2
- package/bundled/skills/prizmkit-test/SKILL.md +106 -14
- package/bundled/skills/prizmkit-test/references/boundary-coverage-protocol.md +220 -0
- package/bundled/skills/prizmkit-test/references/examples.md +82 -9
- package/bundled/skills/prizmkit-test/references/test-generation-steps.md +68 -12
- package/bundled/skills/prizmkit-test/references/test-report-template.md +73 -9
- package/bundled/skills/prizmkit-test/scripts/validate_boundary_report.py +347 -0
- package/bundled/skills-windows/prizmkit-code-review/SKILL.md +7 -2
- package/bundled/skills-windows/prizmkit-code-review/references/reviewer-agent-prompt.md +6 -2
- package/bundled/skills-windows/prizmkit-test/SKILL.md +106 -14
- package/bundled/skills-windows/prizmkit-test/references/boundary-coverage-protocol.md +220 -0
- package/bundled/skills-windows/prizmkit-test/references/examples.md +82 -9
- package/bundled/skills-windows/prizmkit-test/references/service-boundary-test-catalog.md +4 -4
- package/bundled/skills-windows/prizmkit-test/references/test-generation-steps.md +68 -12
- package/bundled/skills-windows/prizmkit-test/references/test-report-template.md +73 -9
- package/bundled/skills-windows/prizmkit-test/scripts/validate_boundary_report.py +347 -0
- package/package.json +1 -1
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
"""Tests for generate-bootstrap-prompt.py core functions."""
|
|
2
2
|
|
|
3
|
+
import os
|
|
3
4
|
import re
|
|
5
|
+
import time
|
|
6
|
+
from pathlib import Path
|
|
4
7
|
|
|
5
8
|
from generate_bootstrap_prompt import (
|
|
6
9
|
compute_feature_slug,
|
|
@@ -12,9 +15,75 @@ from generate_bootstrap_prompt import (
|
|
|
12
15
|
determine_pipeline_mode,
|
|
13
16
|
process_conditional_blocks,
|
|
14
17
|
process_mode_blocks,
|
|
18
|
+
assemble_sections,
|
|
19
|
+
generate_checkpoint_definition,
|
|
20
|
+
merge_checkpoint_state,
|
|
15
21
|
)
|
|
16
22
|
|
|
17
23
|
|
|
24
|
+
def scoped_report_text(verdict="PASS", boundary_missing=0,
|
|
25
|
+
happy_path_only=0, completion_gate="yes",
|
|
26
|
+
validator_result="passed"):
|
|
27
|
+
"""Build a scoped report with a complete boundary contract."""
|
|
28
|
+
return "\n".join([
|
|
29
|
+
"# Test Report",
|
|
30
|
+
"",
|
|
31
|
+
"## Scope",
|
|
32
|
+
"- Mode: this-change",
|
|
33
|
+
"- Artifact Dir: .prizmkit/specs/123-scope-test/",
|
|
34
|
+
"- In-Scope Source Files:",
|
|
35
|
+
" - src/feature.js",
|
|
36
|
+
"- In-Scope Test Files:",
|
|
37
|
+
" - tests/feature.test.js",
|
|
38
|
+
"",
|
|
39
|
+
"## Boundary Matrix",
|
|
40
|
+
"| 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 |",
|
|
41
|
+
"|--------|-----------|--------------|------------|--------------------|-------------------------------|-------------------|-------------------------|-------------|-------------------|-------------------|-------------------|-------------|--------------|",
|
|
42
|
+
"| feature | POST /feature | API | covered | covered | covered | covered | covered | covered | covered | covered | covered | none | boundary-covered |",
|
|
43
|
+
"",
|
|
44
|
+
"## Boundary Completion Gate",
|
|
45
|
+
"- Interfaces total: 1",
|
|
46
|
+
"- Boundary-covered: 1",
|
|
47
|
+
"- Generated-needs-review: 0",
|
|
48
|
+
"- Skipped-with-reason: 0",
|
|
49
|
+
f"- Boundary-missing: {boundary_missing}",
|
|
50
|
+
f"- Happy-path-only: {happy_path_only}",
|
|
51
|
+
f"- Completion gate passed: {completion_gate}",
|
|
52
|
+
"",
|
|
53
|
+
"## Boundary Validation",
|
|
54
|
+
"- Validator command: `python3 validate_boundary_report.py`",
|
|
55
|
+
f"- Validator result: {validator_result}",
|
|
56
|
+
"- Validator output: Boundary report validation: PASS",
|
|
57
|
+
"",
|
|
58
|
+
"## Verdict",
|
|
59
|
+
verdict,
|
|
60
|
+
"",
|
|
61
|
+
"## Final Status",
|
|
62
|
+
"- All tests passing: yes",
|
|
63
|
+
f"- Boundary completion gate passed: {completion_gate}",
|
|
64
|
+
"- Ready for commit: yes",
|
|
65
|
+
"- Needs human review: None",
|
|
66
|
+
"",
|
|
67
|
+
])
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def write_fresh_scoped_report(project_root, report_path, report_text=None):
|
|
71
|
+
"""Write marker, in-scope files, and a fresh scoped report."""
|
|
72
|
+
source = project_root / "src" / "feature.js"
|
|
73
|
+
test = project_root / "tests" / "feature.test.js"
|
|
74
|
+
marker = project_root / ".prizmkit" / "specs" / "123-scope-test" / ".prizmkit-test-started"
|
|
75
|
+
source.parent.mkdir(parents=True, exist_ok=True)
|
|
76
|
+
test.parent.mkdir(parents=True, exist_ok=True)
|
|
77
|
+
marker.parent.mkdir(parents=True, exist_ok=True)
|
|
78
|
+
report_path.parent.mkdir(parents=True, exist_ok=True)
|
|
79
|
+
source.write_text("export const feature = true;\n", encoding="utf-8")
|
|
80
|
+
test.write_text("test('feature', () => {});\n", encoding="utf-8")
|
|
81
|
+
marker.write_text("started\n", encoding="utf-8")
|
|
82
|
+
time.sleep(0.01)
|
|
83
|
+
report_path.write_text(report_text or scoped_report_text(), encoding="utf-8")
|
|
84
|
+
return source, test, marker
|
|
85
|
+
|
|
86
|
+
|
|
18
87
|
# ---------------------------------------------------------------------------
|
|
19
88
|
# compute_feature_slug
|
|
20
89
|
# ---------------------------------------------------------------------------
|
|
@@ -222,3 +291,363 @@ class TestProcessModeBlocks:
|
|
|
222
291
|
tpl = "{{IF_CRITIC_ENABLED}}critic{{END_IF_CRITIC_ENABLED}}"
|
|
223
292
|
result = process_mode_blocks(tpl, "standard", init_done=True)
|
|
224
293
|
assert "critic" not in result
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
# ---------------------------------------------------------------------------
|
|
298
|
+
# section assembly / checkpoint integration
|
|
299
|
+
# ---------------------------------------------------------------------------
|
|
300
|
+
|
|
301
|
+
class TestScopedFeatureTestGate:
|
|
302
|
+
def test_standard_sections_insert_prizmkit_test_between_implement_and_review(self):
|
|
303
|
+
sections_dir = Path(__file__).resolve().parents[1] / "templates" / "sections"
|
|
304
|
+
sections = assemble_sections(
|
|
305
|
+
"standard",
|
|
306
|
+
str(sections_dir),
|
|
307
|
+
init_done=True,
|
|
308
|
+
is_resume=False,
|
|
309
|
+
critic_enabled=False,
|
|
310
|
+
browser_enabled=False,
|
|
311
|
+
)
|
|
312
|
+
names = [name for name, _ in sections]
|
|
313
|
+
|
|
314
|
+
assert "phase-prizmkit-test" in names
|
|
315
|
+
assert names.index("phase-implement") < names.index("phase-prizmkit-test") < names.index("phase-review")
|
|
316
|
+
|
|
317
|
+
rendered = "\n".join(content for _, content in sections)
|
|
318
|
+
assert "/prizmkit-test scope=this-change artifact_dir=.prizmkit/specs/{{FEATURE_SLUG}}/" in rendered
|
|
319
|
+
assert ".prizmkit/bugfix/" in rendered # explicitly forbidden in the gate wording
|
|
320
|
+
assert ".prizmkit/refactor/" in rendered # explicitly forbidden in the gate wording
|
|
321
|
+
|
|
322
|
+
def test_lite_sections_include_prizmkit_test_before_commit(self):
|
|
323
|
+
sections_dir = Path(__file__).resolve().parents[1] / "templates" / "sections"
|
|
324
|
+
sections = assemble_sections(
|
|
325
|
+
"lite",
|
|
326
|
+
str(sections_dir),
|
|
327
|
+
init_done=True,
|
|
328
|
+
is_resume=False,
|
|
329
|
+
critic_enabled=False,
|
|
330
|
+
browser_enabled=False,
|
|
331
|
+
)
|
|
332
|
+
names = [name for name, _ in sections]
|
|
333
|
+
|
|
334
|
+
assert "phase-prizmkit-test" in names
|
|
335
|
+
assert "phase-review" not in names
|
|
336
|
+
assert names.index("phase-implement") < names.index("phase-prizmkit-test") < names.index("phase-commit")
|
|
337
|
+
|
|
338
|
+
def test_checkpoint_orders_prizmkit_test_between_implement_and_review(self):
|
|
339
|
+
sections_dir = Path(__file__).resolve().parents[1] / "templates" / "sections"
|
|
340
|
+
sections = assemble_sections(
|
|
341
|
+
"standard",
|
|
342
|
+
str(sections_dir),
|
|
343
|
+
init_done=True,
|
|
344
|
+
is_resume=False,
|
|
345
|
+
critic_enabled=False,
|
|
346
|
+
browser_enabled=False,
|
|
347
|
+
)
|
|
348
|
+
checkpoint = generate_checkpoint_definition(
|
|
349
|
+
sections,
|
|
350
|
+
pipeline_mode="standard",
|
|
351
|
+
workflow_type="feature-pipeline",
|
|
352
|
+
item_id="F-123",
|
|
353
|
+
item_slug="123-scope-test",
|
|
354
|
+
session_id="F-123-session",
|
|
355
|
+
init_done=True,
|
|
356
|
+
)
|
|
357
|
+
skills = [step["skill"] for step in checkpoint["steps"]]
|
|
358
|
+
|
|
359
|
+
assert skills.index("prizmkit-implement") < skills.index("prizmkit-test") < skills.index("prizmkit-code-review")
|
|
360
|
+
test_step = next(step for step in checkpoint["steps"] if step["skill"] == "prizmkit-test")
|
|
361
|
+
assert test_step["required_artifacts"] == [
|
|
362
|
+
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
363
|
+
".prizmkit/test/*/test-report.md",
|
|
364
|
+
]
|
|
365
|
+
|
|
366
|
+
def test_merge_resets_completed_prizmkit_test_without_report_artifact(self, tmp_path):
|
|
367
|
+
existing = {
|
|
368
|
+
"steps": [
|
|
369
|
+
{
|
|
370
|
+
"id": "S01",
|
|
371
|
+
"skill": "prizmkit-test",
|
|
372
|
+
"status": "completed",
|
|
373
|
+
"required_artifacts": [
|
|
374
|
+
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
375
|
+
".prizmkit/test/*/test-report.md",
|
|
376
|
+
],
|
|
377
|
+
},
|
|
378
|
+
{"id": "S02", "skill": "prizmkit-code-review", "status": "completed", "required_artifacts": []},
|
|
379
|
+
],
|
|
380
|
+
}
|
|
381
|
+
fresh = {
|
|
382
|
+
"steps": [
|
|
383
|
+
{"id": "S01", "skill": "prizmkit-test", "status": "pending", "required_artifacts": [
|
|
384
|
+
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
385
|
+
".prizmkit/test/*/test-report.md",
|
|
386
|
+
]},
|
|
387
|
+
{"id": "S02", "skill": "prizmkit-code-review", "status": "pending", "required_artifacts": []},
|
|
388
|
+
],
|
|
389
|
+
}
|
|
390
|
+
pointer = tmp_path / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
|
|
391
|
+
pointer.parent.mkdir(parents=True, exist_ok=True)
|
|
392
|
+
pointer.write_text(".prizmkit/test/missing/test-report.md\n", encoding="utf-8")
|
|
393
|
+
|
|
394
|
+
merged = merge_checkpoint_state(existing, fresh, str(tmp_path))
|
|
395
|
+
|
|
396
|
+
assert [step["status"] for step in merged["steps"]] == ["pending", "pending"]
|
|
397
|
+
|
|
398
|
+
def test_merge_keeps_completed_prizmkit_test_only_for_scoped_pass_report(self, tmp_path):
|
|
399
|
+
report = tmp_path / ".prizmkit" / "test" / "run" / "test-report.md"
|
|
400
|
+
write_fresh_scoped_report(tmp_path, report)
|
|
401
|
+
pointer = tmp_path / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
|
|
402
|
+
pointer.parent.mkdir(parents=True, exist_ok=True)
|
|
403
|
+
pointer.write_text(".prizmkit/test/run/test-report.md\n", encoding="utf-8")
|
|
404
|
+
existing = {
|
|
405
|
+
"steps": [
|
|
406
|
+
{"id": "S01", "skill": "prizmkit-test", "status": "completed", "required_artifacts": [
|
|
407
|
+
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
408
|
+
".prizmkit/test/*/test-report.md",
|
|
409
|
+
]},
|
|
410
|
+
{"id": "S02", "skill": "prizmkit-code-review", "status": "completed", "required_artifacts": []},
|
|
411
|
+
],
|
|
412
|
+
}
|
|
413
|
+
fresh = {
|
|
414
|
+
"steps": [
|
|
415
|
+
{"id": "S01", "skill": "prizmkit-test", "status": "pending", "required_artifacts": [
|
|
416
|
+
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
417
|
+
".prizmkit/test/*/test-report.md",
|
|
418
|
+
]},
|
|
419
|
+
{"id": "S02", "skill": "prizmkit-code-review", "status": "pending", "required_artifacts": []},
|
|
420
|
+
],
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
merged = merge_checkpoint_state(existing, fresh, str(tmp_path))
|
|
424
|
+
|
|
425
|
+
assert [step["status"] for step in merged["steps"]] == ["completed", "completed"]
|
|
426
|
+
|
|
427
|
+
def test_merge_resets_completed_prizmkit_test_for_needs_fixes_report(self, tmp_path):
|
|
428
|
+
report = tmp_path / ".prizmkit" / "test" / "run" / "test-report.md"
|
|
429
|
+
report.parent.mkdir(parents=True)
|
|
430
|
+
report.write_text(scoped_report_text(verdict="NEEDS_FIXES"), encoding="utf-8")
|
|
431
|
+
pointer = tmp_path / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
|
|
432
|
+
pointer.parent.mkdir(parents=True, exist_ok=True)
|
|
433
|
+
pointer.write_text(".prizmkit/test/run/test-report.md\n", encoding="utf-8")
|
|
434
|
+
existing = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "completed", "required_artifacts": [
|
|
435
|
+
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
436
|
+
".prizmkit/test/*/test-report.md",
|
|
437
|
+
]}]}
|
|
438
|
+
fresh = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "pending", "required_artifacts": [
|
|
439
|
+
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
440
|
+
".prizmkit/test/*/test-report.md",
|
|
441
|
+
]}]}
|
|
442
|
+
|
|
443
|
+
merged = merge_checkpoint_state(existing, fresh, str(tmp_path))
|
|
444
|
+
|
|
445
|
+
assert merged["steps"][0]["status"] == "pending"
|
|
446
|
+
|
|
447
|
+
def test_merge_resets_completed_prizmkit_test_for_absolute_report_outside_project(self, tmp_path):
|
|
448
|
+
project_root = tmp_path / "project"
|
|
449
|
+
external_root = tmp_path / "external"
|
|
450
|
+
in_project_report = project_root / ".prizmkit" / "test" / "run" / "test-report.md"
|
|
451
|
+
external_report = external_root / "report.md"
|
|
452
|
+
pointer = project_root / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
|
|
453
|
+
for path in (in_project_report.parent, external_report.parent, pointer.parent):
|
|
454
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
455
|
+
report_text = "\n".join([
|
|
456
|
+
"# Test Report",
|
|
457
|
+
"",
|
|
458
|
+
"## Scope",
|
|
459
|
+
"- Mode: this-change",
|
|
460
|
+
"- Artifact Dir: .prizmkit/specs/123-scope-test/",
|
|
461
|
+
"",
|
|
462
|
+
"## Verdict",
|
|
463
|
+
"PASS",
|
|
464
|
+
"",
|
|
465
|
+
])
|
|
466
|
+
in_project_report.write_text(report_text, encoding="utf-8")
|
|
467
|
+
external_report.write_text(report_text, encoding="utf-8")
|
|
468
|
+
pointer.write_text(f"{external_report}\n", encoding="utf-8")
|
|
469
|
+
existing = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "completed", "required_artifacts": [
|
|
470
|
+
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
471
|
+
".prizmkit/test/*/test-report.md",
|
|
472
|
+
]}]}
|
|
473
|
+
fresh = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "pending", "required_artifacts": [
|
|
474
|
+
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
475
|
+
".prizmkit/test/*/test-report.md",
|
|
476
|
+
]}]}
|
|
477
|
+
|
|
478
|
+
merged = merge_checkpoint_state(existing, fresh, str(project_root))
|
|
479
|
+
|
|
480
|
+
assert merged["steps"][0]["status"] == "pending"
|
|
481
|
+
|
|
482
|
+
def test_merge_resets_completed_prizmkit_test_for_sibling_workspace_report(self, tmp_path):
|
|
483
|
+
project_root = tmp_path / "project"
|
|
484
|
+
sibling_root = tmp_path / "project-sibling"
|
|
485
|
+
in_project_report = project_root / ".prizmkit" / "test" / "run" / "test-report.md"
|
|
486
|
+
sibling_report = sibling_root / ".prizmkit" / "test" / "run" / "test-report.md"
|
|
487
|
+
pointer = project_root / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
|
|
488
|
+
for path in (in_project_report.parent, sibling_report.parent, pointer.parent):
|
|
489
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
490
|
+
report_text = scoped_report_text()
|
|
491
|
+
in_project_report.write_text(report_text, encoding="utf-8")
|
|
492
|
+
sibling_report.write_text(report_text, encoding="utf-8")
|
|
493
|
+
pointer.write_text(f"{sibling_report}\n", encoding="utf-8")
|
|
494
|
+
existing = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "completed", "required_artifacts": [
|
|
495
|
+
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
496
|
+
".prizmkit/test/*/test-report.md",
|
|
497
|
+
]}]}
|
|
498
|
+
fresh = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "pending", "required_artifacts": [
|
|
499
|
+
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
500
|
+
".prizmkit/test/*/test-report.md",
|
|
501
|
+
]}]}
|
|
502
|
+
|
|
503
|
+
merged = merge_checkpoint_state(existing, fresh, str(project_root))
|
|
504
|
+
|
|
505
|
+
assert merged["steps"][0]["status"] == "pending"
|
|
506
|
+
|
|
507
|
+
def test_merge_resets_completed_prizmkit_test_for_minimal_boundary_matrix(self, tmp_path):
|
|
508
|
+
report = tmp_path / ".prizmkit" / "test" / "run" / "test-report.md"
|
|
509
|
+
report.parent.mkdir(parents=True)
|
|
510
|
+
report.write_text(
|
|
511
|
+
"\n".join([
|
|
512
|
+
"# Test Report",
|
|
513
|
+
"",
|
|
514
|
+
"## Scope",
|
|
515
|
+
"- Mode: this-change",
|
|
516
|
+
"- Artifact Dir: .prizmkit/specs/123-scope-test/",
|
|
517
|
+
"",
|
|
518
|
+
"## Boundary Matrix",
|
|
519
|
+
"| Interface | Happy Path | N/A Reasons | Final Status |",
|
|
520
|
+
"|-----------|------------|-------------|--------------|",
|
|
521
|
+
"| POST /feature | covered | none | boundary-covered |",
|
|
522
|
+
"",
|
|
523
|
+
"## Boundary Completion Gate",
|
|
524
|
+
"- Interfaces total: 1",
|
|
525
|
+
"- Boundary-covered: 1",
|
|
526
|
+
"- Boundary-missing: 0",
|
|
527
|
+
"- Happy-path-only: 0",
|
|
528
|
+
"- Completion gate passed: yes",
|
|
529
|
+
"",
|
|
530
|
+
"## Boundary Validation",
|
|
531
|
+
"- Validator result: skipped",
|
|
532
|
+
"",
|
|
533
|
+
"## Verdict",
|
|
534
|
+
"PASS",
|
|
535
|
+
"",
|
|
536
|
+
]),
|
|
537
|
+
encoding="utf-8",
|
|
538
|
+
)
|
|
539
|
+
pointer = tmp_path / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
|
|
540
|
+
pointer.parent.mkdir(parents=True, exist_ok=True)
|
|
541
|
+
pointer.write_text(".prizmkit/test/run/test-report.md\n", encoding="utf-8")
|
|
542
|
+
existing = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "completed", "required_artifacts": [
|
|
543
|
+
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
544
|
+
".prizmkit/test/*/test-report.md",
|
|
545
|
+
]}]}
|
|
546
|
+
fresh = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "pending", "required_artifacts": [
|
|
547
|
+
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
548
|
+
".prizmkit/test/*/test-report.md",
|
|
549
|
+
]}]}
|
|
550
|
+
|
|
551
|
+
merged = merge_checkpoint_state(existing, fresh, str(tmp_path))
|
|
552
|
+
|
|
553
|
+
assert merged["steps"][0]["status"] == "pending"
|
|
554
|
+
|
|
555
|
+
def test_merge_resets_completed_prizmkit_test_for_missing_boundary_metrics(self, tmp_path):
|
|
556
|
+
report = tmp_path / ".prizmkit" / "test" / "run" / "test-report.md"
|
|
557
|
+
report_text = scoped_report_text().replace("- Boundary-missing: 0\n", "").replace("- Happy-path-only: 0\n", "")
|
|
558
|
+
write_fresh_scoped_report(tmp_path, report, report_text)
|
|
559
|
+
pointer = tmp_path / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
|
|
560
|
+
pointer.parent.mkdir(parents=True, exist_ok=True)
|
|
561
|
+
pointer.write_text(".prizmkit/test/run/test-report.md\n", encoding="utf-8")
|
|
562
|
+
existing = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "completed", "required_artifacts": [
|
|
563
|
+
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
564
|
+
".prizmkit/test/*/test-report.md",
|
|
565
|
+
]}]}
|
|
566
|
+
fresh = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "pending", "required_artifacts": [
|
|
567
|
+
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
568
|
+
".prizmkit/test/*/test-report.md",
|
|
569
|
+
]}]}
|
|
570
|
+
|
|
571
|
+
merged = merge_checkpoint_state(existing, fresh, str(tmp_path))
|
|
572
|
+
|
|
573
|
+
assert merged["steps"][0]["status"] == "pending"
|
|
574
|
+
|
|
575
|
+
def test_merge_resets_completed_prizmkit_test_when_marker_newer_than_report(self, tmp_path):
|
|
576
|
+
report = tmp_path / ".prizmkit" / "test" / "run" / "test-report.md"
|
|
577
|
+
_source, _test, marker = write_fresh_scoped_report(tmp_path, report)
|
|
578
|
+
now = time.time()
|
|
579
|
+
os.utime(report, (now, now))
|
|
580
|
+
os.utime(marker, (now + 10, now + 10))
|
|
581
|
+
pointer = tmp_path / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
|
|
582
|
+
pointer.write_text(".prizmkit/test/run/test-report.md\n", encoding="utf-8")
|
|
583
|
+
existing = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "completed", "required_artifacts": [
|
|
584
|
+
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
585
|
+
".prizmkit/test/*/test-report.md",
|
|
586
|
+
]}]}
|
|
587
|
+
fresh = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "pending", "required_artifacts": [
|
|
588
|
+
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
589
|
+
".prizmkit/test/*/test-report.md",
|
|
590
|
+
]}]}
|
|
591
|
+
|
|
592
|
+
merged = merge_checkpoint_state(existing, fresh, str(tmp_path))
|
|
593
|
+
|
|
594
|
+
assert merged["steps"][0]["status"] == "pending"
|
|
595
|
+
|
|
596
|
+
def test_merge_resets_completed_prizmkit_test_when_in_scope_file_newer_than_report(self, tmp_path):
|
|
597
|
+
report = tmp_path / ".prizmkit" / "test" / "run" / "test-report.md"
|
|
598
|
+
source, _test, _marker = write_fresh_scoped_report(tmp_path, report)
|
|
599
|
+
now = time.time()
|
|
600
|
+
os.utime(report, (now, now))
|
|
601
|
+
os.utime(source, (now + 10, now + 10))
|
|
602
|
+
pointer = tmp_path / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
|
|
603
|
+
pointer.write_text(".prizmkit/test/run/test-report.md\n", encoding="utf-8")
|
|
604
|
+
existing = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "completed", "required_artifacts": [
|
|
605
|
+
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
606
|
+
".prizmkit/test/*/test-report.md",
|
|
607
|
+
]}]}
|
|
608
|
+
fresh = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "pending", "required_artifacts": [
|
|
609
|
+
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
610
|
+
".prizmkit/test/*/test-report.md",
|
|
611
|
+
]}]}
|
|
612
|
+
|
|
613
|
+
merged = merge_checkpoint_state(existing, fresh, str(tmp_path))
|
|
614
|
+
|
|
615
|
+
assert merged["steps"][0]["status"] == "pending"
|
|
616
|
+
|
|
617
|
+
def test_merge_resets_completed_prizmkit_test_without_in_scope_tests(self, tmp_path):
|
|
618
|
+
report = tmp_path / ".prizmkit" / "test" / "run" / "test-report.md"
|
|
619
|
+
report_text = scoped_report_text().replace("- In-Scope Test Files:\n - tests/feature.test.js\n", "- In-Scope Test Files:\n")
|
|
620
|
+
write_fresh_scoped_report(tmp_path, report, report_text)
|
|
621
|
+
pointer = tmp_path / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
|
|
622
|
+
pointer.write_text(".prizmkit/test/run/test-report.md\n", encoding="utf-8")
|
|
623
|
+
existing = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "completed", "required_artifacts": [
|
|
624
|
+
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
625
|
+
".prizmkit/test/*/test-report.md",
|
|
626
|
+
]}]}
|
|
627
|
+
fresh = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "pending", "required_artifacts": [
|
|
628
|
+
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
629
|
+
".prizmkit/test/*/test-report.md",
|
|
630
|
+
]}]}
|
|
631
|
+
|
|
632
|
+
merged = merge_checkpoint_state(existing, fresh, str(tmp_path))
|
|
633
|
+
|
|
634
|
+
assert merged["steps"][0]["status"] == "pending"
|
|
635
|
+
|
|
636
|
+
def test_merge_resets_completed_prizmkit_test_without_in_scope_sources(self, tmp_path):
|
|
637
|
+
report = tmp_path / ".prizmkit" / "test" / "run" / "test-report.md"
|
|
638
|
+
report_text = scoped_report_text().replace("- In-Scope Source Files:\n - src/feature.js\n", "- In-Scope Source Files:\n")
|
|
639
|
+
write_fresh_scoped_report(tmp_path, report, report_text)
|
|
640
|
+
pointer = tmp_path / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
|
|
641
|
+
pointer.write_text(".prizmkit/test/run/test-report.md\n", encoding="utf-8")
|
|
642
|
+
existing = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "completed", "required_artifacts": [
|
|
643
|
+
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
644
|
+
".prizmkit/test/*/test-report.md",
|
|
645
|
+
]}]}
|
|
646
|
+
fresh = {"steps": [{"id": "S01", "skill": "prizmkit-test", "status": "pending", "required_artifacts": [
|
|
647
|
+
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
648
|
+
".prizmkit/test/*/test-report.md",
|
|
649
|
+
]}]}
|
|
650
|
+
|
|
651
|
+
merged = merge_checkpoint_state(existing, fresh, str(tmp_path))
|
|
652
|
+
|
|
653
|
+
assert merged["steps"][0]["status"] == "pending"
|