prizmkit 1.1.130 → 1.1.134
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/adapters/claude/command-adapter.js +0 -2
- package/bundled/dev-pipeline/.env.example +1 -1
- package/bundled/dev-pipeline/assets/skill-subagent-integration.md +11 -6
- package/bundled/dev-pipeline/prizmkit_runtime/checkpoint_state.py +445 -8
- package/bundled/dev-pipeline/prizmkit_runtime/gitops.py +2 -9
- package/bundled/dev-pipeline/prizmkit_runtime/processes.py +21 -1
- package/bundled/dev-pipeline/prizmkit_runtime/runner_classification.py +87 -66
- package/bundled/dev-pipeline/prizmkit_runtime/runner_models.py +53 -3
- package/bundled/dev-pipeline/prizmkit_runtime/runner_recovery.py +55 -25
- package/bundled/dev-pipeline/prizmkit_runtime/runner_status.py +4 -0
- package/bundled/dev-pipeline/prizmkit_runtime/runners.py +119 -18
- package/bundled/dev-pipeline/prizmkit_runtime/sessions.py +3 -2
- package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +411 -17
- package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +57 -14
- package/bundled/dev-pipeline/scripts/generate-recovery-prompt.py +5 -4
- package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +55 -2
- package/bundled/dev-pipeline/scripts/prompt_framework.py +152 -1
- package/bundled/dev-pipeline/scripts/update-bug-status.py +53 -0
- package/bundled/dev-pipeline/scripts/update-checkpoint.py +294 -69
- package/bundled/dev-pipeline/scripts/update-feature-status.py +57 -0
- package/bundled/dev-pipeline/scripts/update-refactor-status.py +53 -0
- package/bundled/dev-pipeline/templates/bootstrap-prompt.md +5 -5
- package/bundled/dev-pipeline/templates/bootstrap-tier3.md +28 -22
- package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +24 -10
- package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +20 -2
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-commit-report.md +2 -2
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-diagnose-plan.md +1 -1
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-manual-verification.md +5 -10
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-review.md +9 -19
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-test.md +21 -0
- package/bundled/dev-pipeline/templates/sections/bugfix-session-status.md +1 -1
- package/bundled/dev-pipeline/templates/sections/checkpoint-system.md +10 -10
- package/bundled/dev-pipeline/templates/sections/context-budget-rules.md +1 -1
- package/bundled/dev-pipeline/templates/sections/headless-commit-authorization.md +15 -0
- 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 +26 -23
- package/bundled/dev-pipeline/templates/sections/phase-review-agent.md +5 -5
- package/bundled/dev-pipeline/templates/sections/phase-review-full.md +15 -11
- package/bundled/dev-pipeline/templates/sections/refactor-phase-commit-report.md +1 -1
- package/bundled/dev-pipeline/templates/sections/refactor-phase-review.md +6 -6
- package/bundled/dev-pipeline/templates/sections/refactor-phase-test.md +21 -0
- package/bundled/dev-pipeline/templates/sections/refactor-reminders.md +1 -1
- package/bundled/dev-pipeline/templates/sections/subagent-timeout-recovery.md +1 -1
- package/bundled/dev-pipeline/templates/sections/task-contract.md +4 -0
- package/bundled/dev-pipeline/tests/test_checkpoint_state.py +362 -3
- package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +349 -48
- package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +129 -0
- package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +91 -0
- package/bundled/dev-pipeline/tests/test_python_runner_parity.py +567 -23
- package/bundled/dev-pipeline/tests/test_unified_cli.py +37 -10
- package/bundled/skills/_metadata.json +63 -26
- package/bundled/skills/app-planner/SKILL.md +4 -3
- package/bundled/skills/bug-fix-workflow/SKILL.md +89 -180
- package/bundled/skills/bug-planner/SKILL.md +12 -27
- package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +131 -220
- package/bundled/skills/bugfix-pipeline-launcher/references/configuration.md +36 -90
- package/bundled/skills/feature-pipeline-launcher/SKILL.md +129 -271
- package/bundled/skills/feature-pipeline-launcher/references/configuration.md +35 -76
- package/bundled/skills/feature-planner/SKILL.md +27 -22
- package/bundled/skills/feature-planner/assets/planning-guide.md +4 -4
- package/bundled/skills/feature-workflow/SKILL.md +94 -121
- package/bundled/skills/prizmkit/SKILL.md +130 -94
- package/bundled/skills/prizmkit/references/workflow-state-protocol.md +181 -0
- package/bundled/skills/prizmkit-code-review/SKILL.md +132 -86
- package/bundled/skills/prizmkit-code-review/references/independent-code-review.md +243 -0
- package/bundled/skills/prizmkit-code-review/references/review-report-template.md +42 -1
- package/bundled/skills/prizmkit-code-review/references/workflow-state-protocol.md +181 -0
- package/bundled/skills/prizmkit-code-review/scripts/render_review_report.py +93 -1
- package/bundled/skills/prizmkit-committer/SKILL.md +125 -48
- package/bundled/skills/prizmkit-committer/references/workflow-state-protocol.md +181 -0
- package/bundled/skills/prizmkit-implement/SKILL.md +99 -73
- package/bundled/skills/prizmkit-implement/references/implementation-subagent-procedure.md +1 -1
- package/bundled/skills/prizmkit-implement/references/workflow-state-protocol.md +181 -0
- package/bundled/skills/prizmkit-init/SKILL.md +5 -5
- package/bundled/skills/prizmkit-plan/SKILL.md +146 -105
- package/bundled/skills/prizmkit-plan/assets/plan-template.md +18 -0
- package/bundled/skills/prizmkit-plan/references/independent-plan-review.md +242 -0
- package/bundled/skills/prizmkit-plan/references/review-plan-spec-loop.md +4 -4
- package/bundled/skills/prizmkit-plan/references/workflow-state-protocol.md +181 -0
- package/bundled/skills/prizmkit-retrospective/SKILL.md +111 -53
- package/bundled/skills/prizmkit-retrospective/references/knowledge-injection-steps.md +7 -6
- package/bundled/skills/prizmkit-retrospective/references/structural-sync-steps.md +5 -4
- package/bundled/skills/prizmkit-retrospective/references/workflow-state-protocol.md +181 -0
- package/bundled/skills/prizmkit-test/SKILL.md +52 -6
- package/bundled/skills/prizmkit-test/assets/authoritative-records.schema.json +63 -12
- package/bundled/skills/prizmkit-test/assets/evidence-manifest.schema.json +1 -1
- package/bundled/skills/prizmkit-test/assets/evidence-package-template.json +13 -0
- package/bundled/skills/prizmkit-test/references/evidence-protocol.md +6 -1
- package/bundled/skills/prizmkit-test/references/evidence-request-protocol.md +8 -1
- package/bundled/skills/prizmkit-test/references/examples.md +1 -1
- package/bundled/skills/prizmkit-test/references/test-generation-steps.md +4 -1
- package/bundled/skills/prizmkit-test/references/test-report-template.md +1 -1
- package/bundled/skills/prizmkit-test/references/trusted-evidence-execution.md +15 -0
- package/bundled/skills/prizmkit-test/references/workflow-state-protocol.md +181 -0
- package/bundled/skills/prizmkit-test/scripts/build_test_evidence.py +744 -23
- package/bundled/skills/prizmkit-test/scripts/validate_test_evidence.py +194 -15
- package/bundled/skills/prizmkit-workflow/SKILL.md +266 -0
- package/bundled/skills/prizmkit-workflow/references/workflow-state-protocol.md +181 -0
- package/bundled/skills/recovery-workflow/SKILL.md +62 -241
- package/bundled/skills/recovery-workflow/evals/evals.json +17 -33
- package/bundled/skills/recovery-workflow/references/detection.md +28 -52
- package/bundled/skills/recovery-workflow/scripts/detect-recovery-state.py +105 -439
- package/bundled/skills/refactor-pipeline-launcher/SKILL.md +136 -275
- package/bundled/skills/refactor-pipeline-launcher/references/configuration.md +53 -88
- package/bundled/skills/refactor-planner/SKILL.md +12 -57
- package/bundled/skills/refactor-planner/references/behavior-preservation.md +111 -230
- package/bundled/skills/refactor-planner/references/error-recovery.md +37 -0
- package/bundled/skills/refactor-planner/references/planning-phases.md +11 -17
- package/bundled/skills/refactor-planner/scripts/validate-and-generate-refactor.py +1 -1
- package/bundled/skills/refactor-workflow/SKILL.md +98 -139
- package/bundled/templates/project-memory-template.md +4 -4
- package/package.json +1 -1
- package/src/index.js +4 -0
- package/src/prompts.js +10 -3
- package/src/scaffold.js +1 -1
- package/bundled/dev-pipeline/tests/test-deploy-safety.sh +0 -223
- package/bundled/skills/feature-pipeline-launcher/scripts/preflight-check.py +0 -462
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Tests for generate-bootstrap-prompt.py core functions."""
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
+
import hashlib
|
|
4
5
|
import importlib.util
|
|
5
6
|
import os
|
|
6
7
|
import re
|
|
@@ -27,6 +28,9 @@ from generate_bootstrap_prompt import (
|
|
|
27
28
|
merge_checkpoint_state,
|
|
28
29
|
load_active_agent_prompts,
|
|
29
30
|
validate_rendered,
|
|
31
|
+
validate_feature_test_evidence,
|
|
32
|
+
route_feature_repair,
|
|
33
|
+
feature_checkpoint_metadata,
|
|
30
34
|
main as generate_bootstrap_main,
|
|
31
35
|
)
|
|
32
36
|
from continuation import append_continuation_handoff, checkpoint_cursor
|
|
@@ -47,9 +51,9 @@ FORBIDDEN_THREE_STRIKE = "3-" + "strike"
|
|
|
47
51
|
FORBIDDEN_TWENTY_STEP = "20-" + "step window"
|
|
48
52
|
|
|
49
53
|
REVIEW_PROMPT_REQUIRED = (
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
54
|
+
"mandatory Main-Agent review",
|
|
55
|
+
"strict structural capability gate",
|
|
56
|
+
"skill-owned independent Reviewer",
|
|
53
57
|
"`review-report.md` is the only persisted review artifact",
|
|
54
58
|
)
|
|
55
59
|
|
|
@@ -157,7 +161,7 @@ def scoped_report_text(verdict="PASS", boundary_missing=0,
|
|
|
157
161
|
|
|
158
162
|
|
|
159
163
|
def write_fresh_scoped_report(project_root, report_path, report_text=None):
|
|
160
|
-
"""Write marker, in-scope files, and a fresh
|
|
164
|
+
"""Write marker, in-scope files, and a fresh derived report."""
|
|
161
165
|
source = project_root / "src" / "feature.js"
|
|
162
166
|
test = project_root / "tests" / "feature.test.js"
|
|
163
167
|
marker = project_root / ".prizmkit" / "specs" / "123-scope-test" / ".prizmkit-test-started"
|
|
@@ -173,6 +177,135 @@ def write_fresh_scoped_report(project_root, report_path, report_text=None):
|
|
|
173
177
|
return source, test, marker
|
|
174
178
|
|
|
175
179
|
|
|
180
|
+
def write_fresh_structured_evidence(project_root):
|
|
181
|
+
"""Create the smallest protocol-shaped, validator-attested pass package."""
|
|
182
|
+
source, test, marker = write_fresh_scoped_report(
|
|
183
|
+
project_root,
|
|
184
|
+
project_root / ".prizmkit" / "test" / "unused" / "test-report.md",
|
|
185
|
+
)
|
|
186
|
+
evidence_base = project_root / ".prizmkit" / "test" / "evidence"
|
|
187
|
+
environment = {}
|
|
188
|
+
plan_inputs = {"feature": "123-scope-test"}
|
|
189
|
+
categories = {
|
|
190
|
+
"source": [{"path": "src/feature.js", "sha256": hashlib.sha256(source.read_bytes()).hexdigest()}],
|
|
191
|
+
"tests": [{"path": "tests/feature.test.js", "sha256": hashlib.sha256(test.read_bytes()).hexdigest()}],
|
|
192
|
+
"contracts": [],
|
|
193
|
+
"lockfiles": [],
|
|
194
|
+
}
|
|
195
|
+
target_hashes = {
|
|
196
|
+
category: hashlib.sha256(json.dumps(
|
|
197
|
+
sorted(records, key=lambda item: item["path"]),
|
|
198
|
+
sort_keys=True, separators=(",", ":"),
|
|
199
|
+
).encode()).hexdigest()
|
|
200
|
+
for category, records in categories.items()
|
|
201
|
+
}
|
|
202
|
+
target_hashes["environment"] = hashlib.sha256(b"{}").hexdigest()
|
|
203
|
+
target_hashes["plan"] = hashlib.sha256(json.dumps(
|
|
204
|
+
plan_inputs, sort_keys=True, separators=(",", ":"),
|
|
205
|
+
).encode()).hexdigest()
|
|
206
|
+
inventory = {
|
|
207
|
+
"categories": categories,
|
|
208
|
+
"plan_inputs": plan_inputs,
|
|
209
|
+
}
|
|
210
|
+
scope = {"feature": "123-scope-test"}
|
|
211
|
+
zero_hash = "0" * 64
|
|
212
|
+
identity = {
|
|
213
|
+
"baseline_commit": "baseline",
|
|
214
|
+
"working_diff_sha256": zero_hash,
|
|
215
|
+
"scope_sha256": hashlib.sha256(json.dumps(
|
|
216
|
+
scope, sort_keys=True, separators=(",", ":"),
|
|
217
|
+
).encode()).hexdigest(),
|
|
218
|
+
}
|
|
219
|
+
evidence_id = hashlib.sha256(json.dumps(
|
|
220
|
+
identity, sort_keys=True, separators=(",", ":"),
|
|
221
|
+
).encode()).hexdigest()
|
|
222
|
+
evidence_root = evidence_base / evidence_id
|
|
223
|
+
evidence_root.mkdir(parents=True, exist_ok=True)
|
|
224
|
+
(evidence_root / "environment.json").write_text("{}\n", encoding="utf-8")
|
|
225
|
+
(evidence_root / "target-inventory.json").write_text(
|
|
226
|
+
json.dumps(inventory), encoding="utf-8",
|
|
227
|
+
)
|
|
228
|
+
(evidence_root / "scope.json").write_text(json.dumps(scope), encoding="utf-8")
|
|
229
|
+
verdict = {
|
|
230
|
+
"verdict": "TEST_PASS",
|
|
231
|
+
"testing_domain_only": True,
|
|
232
|
+
"authorizes_commit_or_release": False,
|
|
233
|
+
"real_environment_validated": False,
|
|
234
|
+
"repairs_business_defects": False,
|
|
235
|
+
"code_evidence_replayable": True,
|
|
236
|
+
"blockers": [],
|
|
237
|
+
"reproduced_failures": [],
|
|
238
|
+
}
|
|
239
|
+
(evidence_root / "verdict.json").write_text(json.dumps(verdict), encoding="utf-8")
|
|
240
|
+
report = scoped_report_text()
|
|
241
|
+
(evidence_root / "test-report.md").write_text(report, encoding="utf-8")
|
|
242
|
+
stages = []
|
|
243
|
+
stage_names = [
|
|
244
|
+
"CHANGE_CLASSIFY", "SCOPE_DISCOVER", "CONTRACT_MODEL", "TEST_PLAN",
|
|
245
|
+
"INFRA_READY", "TEST_BUILD", "EXECUTE_PROVE", "EVIDENCE_PACKAGE",
|
|
246
|
+
"EVIDENCE_VALIDATE",
|
|
247
|
+
]
|
|
248
|
+
for name in stage_names:
|
|
249
|
+
stages.append({"name": name, "status": "complete", "input_sha256": zero_hash, "outputs": []})
|
|
250
|
+
manifest = {
|
|
251
|
+
"protocol_version": "1.0",
|
|
252
|
+
"evidence_id": evidence_id,
|
|
253
|
+
"evidence_id_inputs": identity,
|
|
254
|
+
"sensitivity": "project-controlled",
|
|
255
|
+
"environment_claim": "mocked-code-level-only",
|
|
256
|
+
"compatibility": "legacy-test-report-interface-not-supported",
|
|
257
|
+
"change_class": "lightweight",
|
|
258
|
+
"baseline_commit": "baseline",
|
|
259
|
+
"working_diff_sha256": zero_hash,
|
|
260
|
+
"target_hashes": target_hashes,
|
|
261
|
+
"stages": stages,
|
|
262
|
+
"files": [],
|
|
263
|
+
"final_verdict": "TEST_PASS",
|
|
264
|
+
}
|
|
265
|
+
file_records = []
|
|
266
|
+
for path in sorted(evidence_root.rglob("*")):
|
|
267
|
+
if path.is_file():
|
|
268
|
+
relative = path.relative_to(evidence_root).as_posix()
|
|
269
|
+
file_records.append({
|
|
270
|
+
"path": relative,
|
|
271
|
+
"sha256": hashlib.sha256(path.read_bytes()).hexdigest(),
|
|
272
|
+
"produced_by": "EVIDENCE_PACKAGE",
|
|
273
|
+
})
|
|
274
|
+
payload = sorted(
|
|
275
|
+
({"path": item["path"], "sha256": item["sha256"]}
|
|
276
|
+
for item in file_records
|
|
277
|
+
if item["path"] not in {"verdict.json", "test-report.md"}),
|
|
278
|
+
key=lambda item: item["path"],
|
|
279
|
+
)
|
|
280
|
+
validation = {
|
|
281
|
+
"validator": "validate_test_evidence.py",
|
|
282
|
+
"validator_sha256": zero_hash,
|
|
283
|
+
"payload_sha256": hashlib.sha256(json.dumps(
|
|
284
|
+
payload, sort_keys=True, separators=(",", ":"),
|
|
285
|
+
).encode()).hexdigest(),
|
|
286
|
+
"result": "passed",
|
|
287
|
+
"verdict": "TEST_PASS",
|
|
288
|
+
"semantics": "integrity-and-protocol-validation-not-hostile-producer-proof",
|
|
289
|
+
"replay_receipt_ids": [],
|
|
290
|
+
}
|
|
291
|
+
(evidence_root / "validation.json").write_text(json.dumps(validation), encoding="utf-8")
|
|
292
|
+
file_records.append({
|
|
293
|
+
"path": "validation.json",
|
|
294
|
+
"sha256": hashlib.sha256((evidence_root / "validation.json").read_bytes()).hexdigest(),
|
|
295
|
+
"produced_by": "EVIDENCE_VALIDATE",
|
|
296
|
+
})
|
|
297
|
+
manifest["files"] = file_records
|
|
298
|
+
(evidence_root / "manifest.json").write_text(json.dumps(manifest), encoding="utf-8")
|
|
299
|
+
pointer = project_root / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
|
|
300
|
+
pointer.parent.mkdir(parents=True, exist_ok=True)
|
|
301
|
+
pointer.write_text(str(evidence_root / "test-report.md"), encoding="utf-8")
|
|
302
|
+
report_path = evidence_root / "test-report.md"
|
|
303
|
+
now = time.time()
|
|
304
|
+
os.utime(marker, (now, now))
|
|
305
|
+
os.utime(report_path, (now + 1, now + 1))
|
|
306
|
+
return evidence_root
|
|
307
|
+
|
|
308
|
+
|
|
176
309
|
# ---------------------------------------------------------------------------
|
|
177
310
|
# compute_feature_slug
|
|
178
311
|
# ---------------------------------------------------------------------------
|
|
@@ -469,6 +602,13 @@ class TestProcessModeBlocks:
|
|
|
469
602
|
# ---------------------------------------------------------------------------
|
|
470
603
|
|
|
471
604
|
class TestScopedFeatureTestGate:
|
|
605
|
+
@staticmethod
|
|
606
|
+
def _trust_fixture_validator(monkeypatch):
|
|
607
|
+
monkeypatch.setattr(
|
|
608
|
+
"generate_bootstrap_prompt._evidence_validator_passes",
|
|
609
|
+
lambda _project_root, _evidence_root, _validation: True,
|
|
610
|
+
)
|
|
611
|
+
|
|
472
612
|
def test_standard_sections_insert_prizmkit_test_after_review(self):
|
|
473
613
|
sections_dir = Path(__file__).resolve().parents[1] / "templates" / "sections"
|
|
474
614
|
sections = assemble_sections(
|
|
@@ -561,7 +701,9 @@ class TestScopedFeatureTestGate:
|
|
|
561
701
|
test_step = next(step for step in checkpoint["steps"] if step["skill"] == "prizmkit-test")
|
|
562
702
|
assert test_step["required_artifacts"] == [
|
|
563
703
|
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
564
|
-
".prizmkit/test/*/
|
|
704
|
+
".prizmkit/test/evidence/*/manifest.json",
|
|
705
|
+
".prizmkit/test/evidence/*/verdict.json",
|
|
706
|
+
".prizmkit/test/evidence/*/validation.json",
|
|
565
707
|
]
|
|
566
708
|
|
|
567
709
|
def test_merge_resets_completed_prizmkit_test_without_report_artifact(self, tmp_path):
|
|
@@ -596,19 +738,19 @@ class TestScopedFeatureTestGate:
|
|
|
596
738
|
|
|
597
739
|
assert [step["status"] for step in merged["steps"]] == ["pending", "pending"]
|
|
598
740
|
|
|
599
|
-
def test_merge_keeps_completed_prizmkit_test_only_for_scoped_pass_report(self, tmp_path):
|
|
600
|
-
|
|
601
|
-
|
|
741
|
+
def test_merge_keeps_completed_prizmkit_test_only_for_scoped_pass_report(self, tmp_path, monkeypatch):
|
|
742
|
+
self._trust_fixture_validator(monkeypatch)
|
|
743
|
+
evidence_root = write_fresh_structured_evidence(tmp_path)
|
|
602
744
|
pointer = tmp_path / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
|
|
603
745
|
pointer.parent.mkdir(parents=True, exist_ok=True)
|
|
604
|
-
pointer.write_text("
|
|
746
|
+
pointer.write_text(str(evidence_root / "test-report.md") + "\n", encoding="utf-8")
|
|
605
747
|
existing = {
|
|
606
748
|
"steps": [
|
|
607
|
-
{"id": "S01", "skill": "prizmkit-test", "status": "completed", "required_artifacts": [
|
|
749
|
+
{"id": "S01", "skill": "prizmkit-test", "status": "completed", "stage_result": "TEST_PASS", "required_artifacts": [
|
|
608
750
|
".prizmkit/specs/123-scope-test/test-report-path.txt",
|
|
609
751
|
".prizmkit/test/*/test-report.md",
|
|
610
752
|
]},
|
|
611
|
-
{"id": "S02", "skill": "prizmkit-code-review", "status": "completed", "required_artifacts": []},
|
|
753
|
+
{"id": "S02", "skill": "prizmkit-code-review", "status": "completed", "stage_result": "REVIEW_PASS", "required_artifacts": []},
|
|
612
754
|
],
|
|
613
755
|
}
|
|
614
756
|
fresh = {
|
|
@@ -834,25 +976,171 @@ class TestScopedFeatureTestGate:
|
|
|
834
976
|
|
|
835
977
|
assert merged["steps"][0]["status"] == "pending"
|
|
836
978
|
|
|
837
|
-
def
|
|
979
|
+
def test_structured_evidence_accepts_current_matching_package(self, tmp_path, monkeypatch):
|
|
980
|
+
self._trust_fixture_validator(monkeypatch)
|
|
981
|
+
evidence_root = write_fresh_structured_evidence(tmp_path)
|
|
982
|
+
|
|
983
|
+
evidence = validate_feature_test_evidence(
|
|
984
|
+
str(tmp_path), ".prizmkit/specs/123-scope-test",
|
|
985
|
+
)
|
|
986
|
+
|
|
987
|
+
assert evidence is not None
|
|
988
|
+
assert evidence["verdict"] == "TEST_PASS"
|
|
989
|
+
assert evidence["manifest_path"] == str(
|
|
990
|
+
(evidence_root / "manifest.json").relative_to(tmp_path),
|
|
991
|
+
)
|
|
992
|
+
assert evidence["validation_path"].endswith("/validation.json")
|
|
993
|
+
|
|
994
|
+
def test_structured_evidence_requires_authoritative_validator(self, tmp_path, monkeypatch):
|
|
995
|
+
write_fresh_structured_evidence(tmp_path)
|
|
996
|
+
monkeypatch.setattr(
|
|
997
|
+
"generate_bootstrap_prompt._evidence_validator_passes",
|
|
998
|
+
lambda _project_root, _evidence_root, _validation: False,
|
|
999
|
+
)
|
|
1000
|
+
|
|
1001
|
+
assert validate_feature_test_evidence(
|
|
1002
|
+
str(tmp_path), ".prizmkit/specs/123-scope-test",
|
|
1003
|
+
) is None
|
|
1004
|
+
|
|
1005
|
+
def test_structured_evidence_rejects_legacy_markdown_only_pass(self, tmp_path):
|
|
838
1006
|
report = tmp_path / ".prizmkit" / "test" / "run" / "test-report.md"
|
|
839
|
-
|
|
840
|
-
write_fresh_scoped_report(tmp_path, report, report_text)
|
|
1007
|
+
write_fresh_scoped_report(tmp_path, report)
|
|
841
1008
|
pointer = tmp_path / ".prizmkit" / "specs" / "123-scope-test" / "test-report-path.txt"
|
|
842
|
-
pointer.write_text(
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
".prizmkit/test
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
1009
|
+
pointer.write_text(str(report), encoding="utf-8")
|
|
1010
|
+
|
|
1011
|
+
assert validate_feature_test_evidence(
|
|
1012
|
+
str(tmp_path), ".prizmkit/specs/123-scope-test",
|
|
1013
|
+
) is None
|
|
1014
|
+
|
|
1015
|
+
def test_structured_evidence_rejects_missing_validation(self, tmp_path):
|
|
1016
|
+
evidence_root = write_fresh_structured_evidence(tmp_path)
|
|
1017
|
+
(evidence_root / "validation.json").unlink()
|
|
1018
|
+
|
|
1019
|
+
assert validate_feature_test_evidence(
|
|
1020
|
+
str(tmp_path), ".prizmkit/specs/123-scope-test",
|
|
1021
|
+
) is None
|
|
1022
|
+
|
|
1023
|
+
def test_structured_evidence_rejects_tampered_verdict(self, tmp_path, monkeypatch):
|
|
1024
|
+
self._trust_fixture_validator(monkeypatch)
|
|
1025
|
+
evidence_root = write_fresh_structured_evidence(tmp_path)
|
|
1026
|
+
verdict_path = evidence_root / "verdict.json"
|
|
1027
|
+
verdict = json.loads(verdict_path.read_text(encoding="utf-8"))
|
|
1028
|
+
verdict["verdict"] = "TEST_BLOCKED"
|
|
1029
|
+
verdict_path.write_text(json.dumps(verdict), encoding="utf-8")
|
|
1030
|
+
|
|
1031
|
+
assert validate_feature_test_evidence(
|
|
1032
|
+
str(tmp_path), ".prizmkit/specs/123-scope-test",
|
|
1033
|
+
) is None
|
|
1034
|
+
|
|
1035
|
+
def test_structured_evidence_rejects_wrong_artifact_scope(self, tmp_path, monkeypatch):
|
|
1036
|
+
self._trust_fixture_validator(monkeypatch)
|
|
1037
|
+
evidence_root = write_fresh_structured_evidence(tmp_path)
|
|
1038
|
+
report_path = evidence_root / "test-report.md"
|
|
1039
|
+
report_path.write_text(
|
|
1040
|
+
scoped_report_text().replace(
|
|
1041
|
+
".prizmkit/specs/123-scope-test/",
|
|
1042
|
+
".prizmkit/specs/999-other/",
|
|
1043
|
+
),
|
|
1044
|
+
encoding="utf-8",
|
|
1045
|
+
)
|
|
851
1046
|
|
|
852
|
-
|
|
1047
|
+
assert validate_feature_test_evidence(
|
|
1048
|
+
str(tmp_path), ".prizmkit/specs/123-scope-test",
|
|
1049
|
+
) is None
|
|
853
1050
|
|
|
854
|
-
|
|
1051
|
+
def test_structured_evidence_rejects_stale_report(self, tmp_path, monkeypatch):
|
|
1052
|
+
self._trust_fixture_validator(monkeypatch)
|
|
1053
|
+
evidence_root = write_fresh_structured_evidence(tmp_path)
|
|
1054
|
+
report = evidence_root / "test-report.md"
|
|
1055
|
+
marker = tmp_path / ".prizmkit" / "specs" / "123-scope-test" / ".prizmkit-test-started"
|
|
1056
|
+
now = time.time()
|
|
1057
|
+
os.utime(report, (now, now))
|
|
1058
|
+
os.utime(marker, (now + 10, now + 10))
|
|
1059
|
+
|
|
1060
|
+
assert validate_feature_test_evidence(
|
|
1061
|
+
str(tmp_path), ".prizmkit/specs/123-scope-test",
|
|
1062
|
+
) is None
|
|
1063
|
+
|
|
1064
|
+
def test_structured_evidence_rejects_live_inventory_drift(self, tmp_path, monkeypatch):
|
|
1065
|
+
self._trust_fixture_validator(monkeypatch)
|
|
1066
|
+
write_fresh_structured_evidence(tmp_path)
|
|
1067
|
+
source = tmp_path / "src" / "feature.js"
|
|
1068
|
+
source.write_text("export const feature = false;\n", encoding="utf-8")
|
|
855
1069
|
|
|
1070
|
+
assert validate_feature_test_evidence(
|
|
1071
|
+
str(tmp_path), ".prizmkit/specs/123-scope-test",
|
|
1072
|
+
) is None
|
|
1073
|
+
|
|
1074
|
+
def test_feature_repair_routing_preserves_review_and_test_boundaries(self):
|
|
1075
|
+
review = route_feature_repair("REVIEW_NEEDS_FIXES", repair_round=0)
|
|
1076
|
+
test_infra = route_feature_repair(
|
|
1077
|
+
"TEST_FAIL", "test-infrastructure", repair_round=1,
|
|
1078
|
+
)
|
|
1079
|
+
production = route_feature_repair("TEST_FAIL", "production", repair_round=1)
|
|
1080
|
+
|
|
1081
|
+
assert review["route"] == [
|
|
1082
|
+
"prizmkit-implement", "prizmkit-code-review", "prizmkit-test",
|
|
1083
|
+
]
|
|
1084
|
+
assert test_infra["route"] == ["prizmkit-implement", "prizmkit-test"]
|
|
1085
|
+
assert production["route"] == [
|
|
1086
|
+
"prizmkit-implement", "prizmkit-code-review", "prizmkit-test",
|
|
1087
|
+
]
|
|
1088
|
+
assert review["repair_round"] == 1
|
|
1089
|
+
|
|
1090
|
+
def test_feature_repair_routing_blocks_unsafe_and_exhausted_routes(self):
|
|
1091
|
+
unknown = route_feature_repair("TEST_FAIL", "unknown", repair_round=1)
|
|
1092
|
+
blocked = route_feature_repair("TEST_BLOCKED", "production", repair_round=1)
|
|
1093
|
+
exhausted = route_feature_repair("TEST_FAIL", "production", repair_round=3)
|
|
1094
|
+
|
|
1095
|
+
assert unknown["blocked"] is True
|
|
1096
|
+
assert unknown["route"] == []
|
|
1097
|
+
assert blocked["blocked"] is True
|
|
1098
|
+
assert blocked["resumable"] is False
|
|
1099
|
+
assert blocked["next_stage"] is None
|
|
1100
|
+
assert blocked["terminal_status"] == "WORKFLOW_BLOCKED"
|
|
1101
|
+
assert blocked["blocked_reason"] == "test_blocked"
|
|
1102
|
+
assert exhausted["blocked"] is True
|
|
1103
|
+
assert exhausted["repair_round"] == 3
|
|
1104
|
+
|
|
1105
|
+
def test_feature_checkpoint_metadata_records_l1_handoff_fields(self):
|
|
1106
|
+
evidence = {
|
|
1107
|
+
"manifest_path": ".prizmkit/test/evidence/id/manifest.json",
|
|
1108
|
+
"verdict_path": ".prizmkit/test/evidence/id/verdict.json",
|
|
1109
|
+
"validation_path": ".prizmkit/test/evidence/id/validation.json",
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
metadata = feature_checkpoint_metadata(
|
|
1113
|
+
".prizmkit/specs/123-scope-test",
|
|
1114
|
+
evidence=evidence,
|
|
1115
|
+
stage="test",
|
|
1116
|
+
status="TEST_FAIL",
|
|
1117
|
+
stage_result="TEST_FAIL",
|
|
1118
|
+
repair_scope="test-infrastructure",
|
|
1119
|
+
repair_round=2,
|
|
1120
|
+
next_stage="prizmkit-implement",
|
|
1121
|
+
resume_from="prizmkit-implement",
|
|
1122
|
+
)
|
|
1123
|
+
|
|
1124
|
+
assert metadata == {
|
|
1125
|
+
"schema_version": 1,
|
|
1126
|
+
"artifact_dir": ".prizmkit/specs/123-scope-test",
|
|
1127
|
+
"orchestrator": "prizmkit-l4",
|
|
1128
|
+
"stage": "test",
|
|
1129
|
+
"current_stage": "test",
|
|
1130
|
+
"status": "TEST_FAIL",
|
|
1131
|
+
"stage_result": "TEST_FAIL",
|
|
1132
|
+
"repair_scope": "test-infrastructure",
|
|
1133
|
+
"repair_round": 2,
|
|
1134
|
+
"authoritative_evidence_paths": {
|
|
1135
|
+
"manifest": ".prizmkit/test/evidence/id/manifest.json",
|
|
1136
|
+
"verdict": ".prizmkit/test/evidence/id/verdict.json",
|
|
1137
|
+
"validation": ".prizmkit/test/evidence/id/validation.json",
|
|
1138
|
+
},
|
|
1139
|
+
"next_stage": "prizmkit-implement",
|
|
1140
|
+
"resume_from": "prizmkit-implement",
|
|
1141
|
+
"blocked_reason": None,
|
|
1142
|
+
"terminal_status": None,
|
|
1143
|
+
}
|
|
856
1144
|
|
|
857
1145
|
# ---------------------------------------------------------------------------
|
|
858
1146
|
# Active compaction harness rollback
|
|
@@ -947,10 +1235,10 @@ class TestDirectOrchestratorImplementationPrompts:
|
|
|
947
1235
|
assert FORBIDDEN_TEST_CMD_PLACEHOLDER not in rendered
|
|
948
1236
|
assert FORBIDDEN_THREE_STRIKE not in rendered
|
|
949
1237
|
assert FORBIDDEN_TWENTY_STEP not in rendered
|
|
950
|
-
assert "Main-Agent review
|
|
1238
|
+
assert "mandatory Main-Agent review" in rendered
|
|
951
1239
|
assert "up to ten completed rounds" in rendered
|
|
952
|
-
assert "
|
|
953
|
-
assert "
|
|
1240
|
+
assert "strict structural capability gate" in rendered
|
|
1241
|
+
assert "skill-owned independent Reviewer" in rendered
|
|
954
1242
|
assert "low=0" not in rendered
|
|
955
1243
|
assert "Reviewer 3" not in rendered
|
|
956
1244
|
|
|
@@ -1078,9 +1366,19 @@ class TestContinuationHandoff:
|
|
|
1078
1366
|
assert checkpoint_cursor(checkpoint)["first_cursor"] == "S02 (active)"
|
|
1079
1367
|
|
|
1080
1368
|
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1369
|
+
def test_modular_feature_prompt_renders_user_context(tmp_path):
|
|
1370
|
+
prompt = _render_feature_prompt(
|
|
1371
|
+
tmp_path / "user-context",
|
|
1372
|
+
mode="standard",
|
|
1373
|
+
user_context=["Use the existing event envelope", "Keep the public API backward compatible"],
|
|
1374
|
+
)
|
|
1375
|
+
|
|
1376
|
+
assert "Use the existing event envelope" in prompt
|
|
1377
|
+
assert "Keep the public API backward compatible" in prompt
|
|
1378
|
+
assert "These materials were provided by the user" in prompt
|
|
1379
|
+
assert "{{USER_CONTEXT}}" not in prompt
|
|
1380
|
+
|
|
1381
|
+
|
|
1084
1382
|
|
|
1085
1383
|
HEADLESS_FORBIDDEN_PROMPT_STRINGS = [
|
|
1086
1384
|
"### Interactive Claude Code Note",
|
|
@@ -1101,7 +1399,7 @@ def _write_feature_list(path, feature):
|
|
|
1101
1399
|
path.write_text(json.dumps({"features": [feature], "global_context": {"language": "Python"}}), encoding="utf-8")
|
|
1102
1400
|
|
|
1103
1401
|
|
|
1104
|
-
def _render_feature_prompt(tmp_path, mode="standard", template=None, platform="claude", browser_interaction=None, complexity=None):
|
|
1402
|
+
def _render_feature_prompt(tmp_path, mode="standard", template=None, platform="claude", browser_interaction=None, complexity=None, user_context=None):
|
|
1105
1403
|
project = tmp_path / "project"
|
|
1106
1404
|
project.mkdir(parents=True)
|
|
1107
1405
|
(project / ".prizmkit" / "plans").mkdir(parents=True)
|
|
@@ -1115,6 +1413,8 @@ def _render_feature_prompt(tmp_path, mode="standard", template=None, platform="c
|
|
|
1115
1413
|
"estimated_complexity": complexity or {"lite": "low", "standard": "medium", "full": "high"}[mode],
|
|
1116
1414
|
"acceptance_criteria": ["Prompts are clean"],
|
|
1117
1415
|
}
|
|
1416
|
+
if user_context is not None:
|
|
1417
|
+
feature["user_context"] = user_context
|
|
1118
1418
|
if browser_interaction is not None:
|
|
1119
1419
|
feature["browser_interaction"] = browser_interaction
|
|
1120
1420
|
feature_list = project / ".prizmkit" / "plans" / "feature-list.json"
|
|
@@ -1513,28 +1813,29 @@ class TestBrowserOptInPromptPolicy:
|
|
|
1513
1813
|
assert "browser-verification" not in self._checkpoint_skills(tmp_path / "global-disable" / "bugfix", "bugfix")
|
|
1514
1814
|
assert "browser-verification" not in self._checkpoint_skills(tmp_path / "global-disable" / "refactor", "refactor")
|
|
1515
1815
|
|
|
1516
|
-
def
|
|
1816
|
+
def test_manual_bugfix_prompt_remains_headless_and_committable(self, tmp_path):
|
|
1517
1817
|
prompt = _render_bugfix_prompt(
|
|
1518
|
-
tmp_path / "
|
|
1818
|
+
tmp_path / "manual-bugfix",
|
|
1519
1819
|
bug_overrides={"verification_type": "manual"},
|
|
1520
1820
|
)
|
|
1521
1821
|
|
|
1522
1822
|
assert "Manual / Hybrid Verification Gate" in prompt
|
|
1523
|
-
assert "
|
|
1524
|
-
assert "
|
|
1525
|
-
assert "
|
|
1823
|
+
assert "must complete manual/hybrid verification automatically" in prompt
|
|
1824
|
+
assert "Stop before `/prizmkit-retrospective` and `/prizmkit-committer`" not in prompt
|
|
1825
|
+
assert "### Headless Commit Authorization" in prompt
|
|
1826
|
+
assert "status=\"partial\"" not in prompt
|
|
1526
1827
|
|
|
1527
|
-
def
|
|
1828
|
+
def test_recovery_manual_bugfix_uses_same_automatic_policy(self, tmp_path):
|
|
1528
1829
|
prompt = _render_bugfix_prompt(
|
|
1529
1830
|
tmp_path / "recovery-manual-bugfix",
|
|
1530
1831
|
bug_overrides={"verification_type": "manual"},
|
|
1531
1832
|
retry_count="1",
|
|
1532
1833
|
)
|
|
1533
1834
|
|
|
1534
|
-
assert "Manual / Hybrid Verification Gate"
|
|
1535
|
-
assert "
|
|
1536
|
-
assert "
|
|
1537
|
-
assert "
|
|
1835
|
+
assert "Manual / Hybrid Verification Gate" in prompt
|
|
1836
|
+
assert "must complete manual/hybrid verification automatically" in prompt
|
|
1837
|
+
assert "Recovery-class session: automated verification may substitute" not in prompt
|
|
1838
|
+
assert "### Headless Commit Authorization" in prompt
|
|
1538
1839
|
|
|
1539
1840
|
|
|
1540
1841
|
class TestHeadlessPromptCleanupF033:
|
|
@@ -1630,9 +1931,9 @@ class TestHeadlessPromptCleanupF033:
|
|
|
1630
1931
|
refactor_prompt = _render_refactor_prompt(tmp_path / "refactor-mixed", platform="claude")
|
|
1631
1932
|
|
|
1632
1933
|
assert "{{" + "REVIEWER_" + "SUBAGENT_PATH" + "}}" not in replacements
|
|
1633
|
-
assert "Main-Agent review
|
|
1634
|
-
assert "
|
|
1635
|
-
assert "
|
|
1934
|
+
assert "mandatory Main-Agent review" in refactor_prompt
|
|
1935
|
+
assert "strict structural capability gate" in refactor_prompt
|
|
1936
|
+
assert "skill-owned independent Reviewer" in refactor_prompt
|
|
1636
1937
|
assert "Reviewer 3" not in refactor_prompt
|
|
1637
1938
|
|
|
1638
1939
|
def test_bugfix_refactor_explicit_codex_override(self, tmp_path, monkeypatch):
|
|
@@ -1654,9 +1955,9 @@ class TestHeadlessPromptCleanupF033:
|
|
|
1654
1955
|
refactor_prompt = _render_refactor_prompt(tmp_path / "refactor-codex", platform="claude", explicit_platform="codex")
|
|
1655
1956
|
|
|
1656
1957
|
assert "{{" + "REVIEWER_" + "SUBAGENT_PATH" + "}}" not in replacements
|
|
1657
|
-
assert "Main-Agent review
|
|
1658
|
-
assert "
|
|
1659
|
-
assert "
|
|
1958
|
+
assert "mandatory Main-Agent review" in refactor_prompt
|
|
1959
|
+
assert "strict structural capability gate" in refactor_prompt
|
|
1960
|
+
assert "skill-owned independent Reviewer" in refactor_prompt
|
|
1660
1961
|
assert "Reviewer 3" not in refactor_prompt
|
|
1661
1962
|
|
|
1662
1963
|
|