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