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
|
@@ -38,6 +38,7 @@ LAYER_ORDER = {
|
|
|
38
38
|
"regression-ring": 4,
|
|
39
39
|
}
|
|
40
40
|
FINAL_RECORDS = {"validation.json", "verdict.json", "test-report.md"}
|
|
41
|
+
ATTESTATION_MUTABLE = {"validation.json"}
|
|
41
42
|
COMMON_RECORDS = {
|
|
42
43
|
"change-classification.json", "scope.json", "target-inventory.json",
|
|
43
44
|
"environment.json", "executions.json", "verdict.json", "validation.json",
|
|
@@ -54,9 +55,13 @@ BLOCKED_EXTERNAL_CLASSES = {"production", "unknown"}
|
|
|
54
55
|
SNAPSHOT_VOLATILE_ROOTS = (
|
|
55
56
|
".prizmkit/state",
|
|
56
57
|
".prizmkit/test/evidence",
|
|
58
|
+
".prizmkit/" + "dev-" + "pipeline",
|
|
57
59
|
".claude/worktrees",
|
|
60
|
+
".agents/worktrees",
|
|
61
|
+
".codebuddy/worktrees",
|
|
62
|
+
".codex/worktrees",
|
|
58
63
|
)
|
|
59
|
-
SNAPSHOT_VOLATILE_NAMES = {".git", "__pycache__"}
|
|
64
|
+
SNAPSHOT_VOLATILE_NAMES = {".git", "__pycache__", ".pytest_cache", ".mypy_cache"}
|
|
60
65
|
RISK_CONFLICT_PATTERNS = {
|
|
61
66
|
"permission": re.compile(r"auth|tenant|role|permission|access|acl|rbac", re.I),
|
|
62
67
|
"concurrency": re.compile(r"lock|shared[ _-]?state|worker|queue|concurr|parallel|race", re.I),
|
|
@@ -219,6 +224,18 @@ def validate_schema(
|
|
|
219
224
|
"""Validate the strict JSON-Schema subset used by skill-owned schemas."""
|
|
220
225
|
schema = resolve_ref(schema, root_schema)
|
|
221
226
|
|
|
227
|
+
one_of = schema.get("oneOf")
|
|
228
|
+
if isinstance(one_of, list):
|
|
229
|
+
matching = 0
|
|
230
|
+
for branch in one_of:
|
|
231
|
+
if not isinstance(branch, dict):
|
|
232
|
+
continue
|
|
233
|
+
branch_errors: list[str] = []
|
|
234
|
+
validate_schema(value, branch, root_schema, location, branch_errors)
|
|
235
|
+
if not branch_errors:
|
|
236
|
+
matching += 1
|
|
237
|
+
require(matching == 1, f"{location}: value must match exactly one schema branch", errors)
|
|
238
|
+
|
|
222
239
|
for child in schema.get("allOf", []):
|
|
223
240
|
if isinstance(child, dict):
|
|
224
241
|
validate_schema(value, child, root_schema, location, errors)
|
|
@@ -341,6 +358,31 @@ def require_manifest_entry(
|
|
|
341
358
|
return path
|
|
342
359
|
|
|
343
360
|
|
|
361
|
+
def validate_differential_na(
|
|
362
|
+
value: Any,
|
|
363
|
+
location: str,
|
|
364
|
+
errors: list[str],
|
|
365
|
+
) -> None:
|
|
366
|
+
"""Validate the differential-only N/A shape without weakening risk-cell N/A."""
|
|
367
|
+
if not isinstance(value, dict):
|
|
368
|
+
errors.append(f"{location}: differential N/A must be an object")
|
|
369
|
+
return
|
|
370
|
+
required = {"reason", "rationale", "evidence", "considered_signals", "conflicts"}
|
|
371
|
+
require(set(value) == required, f"{location}: differential N/A fields are incomplete", errors)
|
|
372
|
+
require(value.get("reason") in {"new-behavior", "textual-contract"}, f"{location}: invalid differential N/A reason", errors)
|
|
373
|
+
rationale = value.get("rationale")
|
|
374
|
+
require(isinstance(rationale, str) and len(rationale.strip()) >= 16, f"{location}: differential N/A rationale is too short", errors)
|
|
375
|
+
if value.get("reason") == "new-behavior" and isinstance(rationale, str):
|
|
376
|
+
require("no stable pre-change observable" in rationale.lower(), f"{location}: new behavior N/A lacks baseline rationale", errors)
|
|
377
|
+
if value.get("reason") == "textual-contract" and isinstance(rationale, str):
|
|
378
|
+
require("textual" in rationale.lower(), f"{location}: textual contract N/A lacks textual rationale", errors)
|
|
379
|
+
evidence = value.get("evidence")
|
|
380
|
+
require(isinstance(evidence, list) and bool(evidence) and all(isinstance(item, str) and item.strip() for item in evidence), f"{location}: differential N/A needs evidence strings", errors)
|
|
381
|
+
signals = value.get("considered_signals")
|
|
382
|
+
require(isinstance(signals, list) and bool(signals) and all(isinstance(item, str) and item.strip() for item in signals), f"{location}: differential N/A needs considered signals", errors)
|
|
383
|
+
require(isinstance(value.get("conflicts"), list), f"{location}: differential N/A conflicts must be an array", errors)
|
|
384
|
+
|
|
385
|
+
|
|
344
386
|
def structured_na(
|
|
345
387
|
value: Any,
|
|
346
388
|
records_schema: dict[str, Any],
|
|
@@ -503,6 +545,25 @@ def validate_manifest(
|
|
|
503
545
|
return entry_map
|
|
504
546
|
|
|
505
547
|
|
|
548
|
+
def get_captured_deleted_paths(root: Path) -> set[str]:
|
|
549
|
+
capture_path = root / "change-capture.json"
|
|
550
|
+
if not capture_path.is_file():
|
|
551
|
+
return set()
|
|
552
|
+
try:
|
|
553
|
+
capture = load_json(capture_path)
|
|
554
|
+
except EvidenceError:
|
|
555
|
+
return set()
|
|
556
|
+
if not isinstance(capture, dict):
|
|
557
|
+
return set()
|
|
558
|
+
return {
|
|
559
|
+
item["path"]
|
|
560
|
+
for item in capture.get("changed_files", [])
|
|
561
|
+
if isinstance(item, dict)
|
|
562
|
+
and isinstance(item.get("path"), str)
|
|
563
|
+
and (item.get("status", "").strip() == "D" or item.get("role") == "source")
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
|
|
506
567
|
def validate_inventory(
|
|
507
568
|
root: Path,
|
|
508
569
|
project_root: Path,
|
|
@@ -569,6 +630,7 @@ def validate_inventory(
|
|
|
569
630
|
require(plan.get("plan_inputs") == plan_inputs, "test plan inputs do not match target inventory", errors)
|
|
570
631
|
|
|
571
632
|
patch = require_manifest_entry("source-change.patch", entry_map, root, errors, "source-change patch")
|
|
633
|
+
captured_deleted_paths = get_captured_deleted_paths(root)
|
|
572
634
|
if patch is not None:
|
|
573
635
|
require(manifest.get("working_diff_sha256") == file_sha256(patch), "working_diff_sha256 does not match source-change.patch", errors)
|
|
574
636
|
|
|
@@ -579,7 +641,19 @@ def validate_inventory(
|
|
|
579
641
|
request = load_json(request_path)
|
|
580
642
|
if isinstance(request, dict):
|
|
581
643
|
require(request.get("changed_files") == inventory.get("changed_files"), "inventory request changed_files mismatch", errors)
|
|
582
|
-
|
|
644
|
+
request_roots = request.get("module_roots")
|
|
645
|
+
normalized_request_roots = []
|
|
646
|
+
if isinstance(request_roots, list):
|
|
647
|
+
for item in request_roots:
|
|
648
|
+
if isinstance(item, str):
|
|
649
|
+
normalized_request_roots.append(item)
|
|
650
|
+
elif isinstance(item, dict) and isinstance(item.get("path"), str):
|
|
651
|
+
normalized_request_roots.append(item["path"])
|
|
652
|
+
require(
|
|
653
|
+
normalized_request_roots == inventory.get("module_roots"),
|
|
654
|
+
"inventory request module_roots mismatch",
|
|
655
|
+
errors,
|
|
656
|
+
)
|
|
583
657
|
require(request.get("exclusions") == scope.get("exclusions"), "inventory request exclusions mismatch", errors)
|
|
584
658
|
require(request.get("plan_inputs") == plan_inputs, "inventory request plan_inputs mismatch", errors)
|
|
585
659
|
else:
|
|
@@ -594,6 +668,7 @@ def validate_scope(
|
|
|
594
668
|
inventory_by_category: dict[str, dict[str, str]],
|
|
595
669
|
project_root: Path,
|
|
596
670
|
evidence_root: Path,
|
|
671
|
+
captured_deleted_paths: set[str],
|
|
597
672
|
errors: list[str],
|
|
598
673
|
) -> None:
|
|
599
674
|
changed_files = scope.get("changed_files")
|
|
@@ -614,8 +689,14 @@ def validate_scope(
|
|
|
614
689
|
if relative in exclusion_paths:
|
|
615
690
|
require(relative not in inventory_paths, f"excluded changed file is also inventoried: {relative}", errors)
|
|
616
691
|
else:
|
|
617
|
-
|
|
618
|
-
|
|
692
|
+
candidate = safe_path(project_root, relative, errors, "changed file")
|
|
693
|
+
deleted = relative in captured_deleted_paths
|
|
694
|
+
require(
|
|
695
|
+
deleted or (candidate is not None and candidate.is_file()),
|
|
696
|
+
f"changed file is not live: {relative}",
|
|
697
|
+
errors,
|
|
698
|
+
)
|
|
699
|
+
require(relative in inventory_paths or deleted, f"changed file is neither inventoried nor excluded: {relative}", errors)
|
|
619
700
|
|
|
620
701
|
roots = scope.get("module_roots")
|
|
621
702
|
require(isinstance(roots, list) and bool(roots), "scope module_roots is empty", errors)
|
|
@@ -695,7 +776,13 @@ def validate_requests_and_receipts(
|
|
|
695
776
|
if not isinstance(request, dict):
|
|
696
777
|
errors.append(f"request must be an object: {relative}")
|
|
697
778
|
continue
|
|
698
|
-
if "
|
|
779
|
+
if request.get("method") == "not-applicable":
|
|
780
|
+
validate_definition(request, "differentialRequest", records_schema, relative, errors)
|
|
781
|
+
not_applicable = request.get("not_applicable")
|
|
782
|
+
if isinstance(not_applicable, dict):
|
|
783
|
+
validate_differential_na(not_applicable, f"{relative}.not_applicable", errors)
|
|
784
|
+
differential_requests[relative] = request
|
|
785
|
+
elif "execution_request" in request:
|
|
699
786
|
validate_definition(request, "differentialRequest", records_schema, relative, errors)
|
|
700
787
|
nested = request.get("execution_request")
|
|
701
788
|
if isinstance(nested, dict):
|
|
@@ -707,6 +794,17 @@ def validate_requests_and_receipts(
|
|
|
707
794
|
validate_execution_request_semantics(request, relative, errors)
|
|
708
795
|
validate_external_targets(request.get("external_targets"), f"{relative}.external_targets", errors)
|
|
709
796
|
execution_requests[relative] = request
|
|
797
|
+
elif set(request) == {
|
|
798
|
+
"request_version", "change_class", "artifact_dir",
|
|
799
|
+
} or (
|
|
800
|
+
"request_version" in request
|
|
801
|
+
and "change_class" in request
|
|
802
|
+
and set(request) <= {
|
|
803
|
+
"request_version", "change_class", "baseline_commit", "scope_path",
|
|
804
|
+
"environment_path", "artifact_dir", "blockers", "repair_scope",
|
|
805
|
+
}
|
|
806
|
+
):
|
|
807
|
+
require(request.get("request_version", "1.0") == "1.0", f"unsupported finalize request version: {relative}", errors)
|
|
710
808
|
elif set(request) == {
|
|
711
809
|
"request_version", "categories", "changed_files", "module_roots",
|
|
712
810
|
"exclusions", "discovery_evidence", "plan_inputs",
|
|
@@ -922,12 +1020,18 @@ def validate_plan_and_scope_bindings(
|
|
|
922
1020
|
project_relative = test.get("project_path")
|
|
923
1021
|
inventory_relative = test.get("inventory_path")
|
|
924
1022
|
snapshot_relative = test.get("snapshot_path")
|
|
1023
|
+
change_status = test.get("change_status", "modified" if isinstance(snapshot_relative, str) else "existing")
|
|
1024
|
+
require(change_status in {"existing", "added", "modified"}, f"planned test has invalid change_status: {test_id}", errors)
|
|
925
1025
|
project_path = safe_path(project_root, project_relative, errors, f"planned test {test_id} project_path")
|
|
926
1026
|
require(project_path is not None and project_path.is_file(), f"planned test live path is missing: {test_id}", errors)
|
|
927
1027
|
require(inventory_relative == project_relative, f"planned test inventory path differs from live project path: {test_id}", errors)
|
|
928
1028
|
require(inventory_relative in inventory_by_category.get("tests", {}), f"planned test is not in the test inventory: {test_id}", errors)
|
|
929
|
-
|
|
930
|
-
|
|
1029
|
+
if change_status == "existing":
|
|
1030
|
+
require(snapshot_relative is None, f"existing test must not have a generated snapshot: {test_id}", errors)
|
|
1031
|
+
snapshot_path = None
|
|
1032
|
+
else:
|
|
1033
|
+
snapshot_path = require_manifest_entry(snapshot_relative, entry_map, root, errors, f"planned test snapshot {test_id}") if isinstance(snapshot_relative, str) else None
|
|
1034
|
+
require(isinstance(snapshot_relative, str) and path_under(snapshot_relative, "generated-tests"), f"planned test snapshot must be under generated-tests/: {test_id}", errors)
|
|
931
1035
|
if project_path is not None and project_path.is_file() and snapshot_path is not None and snapshot_path.is_file():
|
|
932
1036
|
require(file_sha256(project_path) == file_sha256(snapshot_path), f"generated test snapshot differs from live test: {test_id}", errors)
|
|
933
1037
|
test_layers = test.get("layers")
|
|
@@ -1082,7 +1186,7 @@ def validate_differential_proofs(
|
|
|
1082
1186
|
proof_by_behavior[behavior_id] = item
|
|
1083
1187
|
classification = item.get("classification")
|
|
1084
1188
|
if classification == "NOT_APPLICABLE":
|
|
1085
|
-
|
|
1189
|
+
validate_differential_na(item.get("not_applicable"), f"differential proof {behavior_id} not_applicable", errors)
|
|
1086
1190
|
for key in (
|
|
1087
1191
|
"method", "differential_request_path", "differential_request_sha256", "baseline_commit",
|
|
1088
1192
|
"current_tree_sha256", "baseline_execution_id", "mutation_execution_id", "current_execution_id",
|
|
@@ -1245,6 +1349,13 @@ def validate_verdict_and_attestation(
|
|
|
1245
1349
|
require(bool(verdict_record.get("blockers")), "TEST_BLOCKED lacks blocker reasons", errors)
|
|
1246
1350
|
elif verdict == "TEST_FAIL":
|
|
1247
1351
|
require(bool(verdict_record.get("reproduced_failures")), "TEST_FAIL lacks reproduced failures", errors)
|
|
1352
|
+
require(
|
|
1353
|
+
verdict_record.get("repair_scope") in {
|
|
1354
|
+
"test-infrastructure", "production", "runtime", "schema", "dependency", "public-interface",
|
|
1355
|
+
},
|
|
1356
|
+
"TEST_FAIL lacks a valid evidence-backed repair_scope",
|
|
1357
|
+
errors,
|
|
1358
|
+
)
|
|
1248
1359
|
|
|
1249
1360
|
if verify_attestation:
|
|
1250
1361
|
validate_definition(validation, "validation", records_schema, "validation", errors)
|
|
@@ -1264,6 +1375,10 @@ def validate_verdict_and_attestation(
|
|
|
1264
1375
|
require(validation.get("semantics") == "integrity-and-protocol-validation-not-hostile-producer-proof", "validation attestation semantics exceed integrity/protocol validation", errors)
|
|
1265
1376
|
require(validation.get("replay_receipt_ids") == replayed_ids, "validation replay receipt IDs do not match actual replay executions", errors)
|
|
1266
1377
|
|
|
1378
|
+
if root.joinpath("lifecycle.json").is_file():
|
|
1379
|
+
expected_report = render_report_text(root, manifest, verdict_record, validation)
|
|
1380
|
+
require(report == expected_report, "derived report does not match canonical rendering", errors)
|
|
1381
|
+
|
|
1267
1382
|
fragments = [
|
|
1268
1383
|
f"Verdict: {verdict}", f"Evidence ID: {manifest.get('evidence_id')}",
|
|
1269
1384
|
"Derived from structured evidence", "sensitivity=project-controlled",
|
|
@@ -1299,7 +1414,10 @@ def validate_evidence(
|
|
|
1299
1414
|
|
|
1300
1415
|
entry_map = validate_manifest(root, manifest, manifest_schema, records_schema, errors, ignored_hash_paths)
|
|
1301
1416
|
change_class = manifest.get("change_class")
|
|
1302
|
-
|
|
1417
|
+
final_verdict = manifest.get("final_verdict")
|
|
1418
|
+
required = COMMON_RECORDS
|
|
1419
|
+
if final_verdict != "TEST_BLOCKED":
|
|
1420
|
+
required = required | (BEHAVIOR_RECORDS if change_class == "behavior" else LIGHTWEIGHT_RECORDS)
|
|
1303
1421
|
for relative in required:
|
|
1304
1422
|
require(relative in entry_map, f"required evidence record not hashed: {relative}", errors)
|
|
1305
1423
|
validate_definition(classification, "changeClassification", records_schema, "change-classification", errors)
|
|
@@ -1322,7 +1440,10 @@ def validate_evidence(
|
|
|
1322
1440
|
|
|
1323
1441
|
inventory_by_category, _ = validate_inventory(root, project_root, manifest, inventory, scope, plan, entry_map, errors)
|
|
1324
1442
|
require(manifest.get("evidence_id_inputs", {}).get("scope_sha256") == canonical_sha256(scope), "scope identity hash mismatch", errors)
|
|
1325
|
-
validate_scope(
|
|
1443
|
+
validate_scope(
|
|
1444
|
+
scope, inventory, inventory_by_category, project_root, root,
|
|
1445
|
+
get_captured_deleted_paths(root), errors,
|
|
1446
|
+
)
|
|
1326
1447
|
execution_requests, differential_requests, executions = validate_requests_and_receipts(root, entry_map, records_schema, errors)
|
|
1327
1448
|
|
|
1328
1449
|
if change_class == "behavior":
|
|
@@ -1373,6 +1494,64 @@ def validate_evidence(
|
|
|
1373
1494
|
return errors
|
|
1374
1495
|
|
|
1375
1496
|
|
|
1497
|
+
def atomic_write_text(path: Path, value: str) -> None:
|
|
1498
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
1499
|
+
descriptor, temporary_name = tempfile.mkstemp(prefix=f".{path.name}.", suffix=".tmp", dir=path.parent)
|
|
1500
|
+
try:
|
|
1501
|
+
with os.fdopen(descriptor, "w", encoding="utf-8") as handle:
|
|
1502
|
+
handle.write(value)
|
|
1503
|
+
os.replace(temporary_name, path)
|
|
1504
|
+
except BaseException:
|
|
1505
|
+
try:
|
|
1506
|
+
os.unlink(temporary_name)
|
|
1507
|
+
except FileNotFoundError:
|
|
1508
|
+
pass
|
|
1509
|
+
raise
|
|
1510
|
+
|
|
1511
|
+
|
|
1512
|
+
def render_report_text(
|
|
1513
|
+
root: Path,
|
|
1514
|
+
manifest: dict[str, Any],
|
|
1515
|
+
verdict: dict[str, Any],
|
|
1516
|
+
validation: dict[str, Any],
|
|
1517
|
+
) -> str:
|
|
1518
|
+
lifecycle_path = root / "lifecycle.json"
|
|
1519
|
+
lifecycle = load_json(lifecycle_path) if lifecycle_path.exists() else {}
|
|
1520
|
+
proof_path = root / "differential-proof.json"
|
|
1521
|
+
proof_record = load_json(proof_path) if proof_path.exists() else {}
|
|
1522
|
+
executions_record = load_json(root / "executions.json")
|
|
1523
|
+
proofs = proof_record.get("proofs", []) if isinstance(proof_record, dict) else []
|
|
1524
|
+
executions = executions_record if isinstance(executions_record, list) else []
|
|
1525
|
+
artifact_dir = lifecycle.get("artifact_dir") if isinstance(lifecycle, dict) else None
|
|
1526
|
+
lines = [
|
|
1527
|
+
"# Test Evidence Report",
|
|
1528
|
+
"",
|
|
1529
|
+
"Derived from structured evidence",
|
|
1530
|
+
f"Evidence ID: {manifest.get('evidence_id')}",
|
|
1531
|
+
f"Verdict: {verdict.get('verdict')}",
|
|
1532
|
+
f"Validator result: {validation.get('result', 'pending')}",
|
|
1533
|
+
"sensitivity=project-controlled",
|
|
1534
|
+
"The project owns access control, retention, and upload policy.",
|
|
1535
|
+
"Mocked code-level evidence does not verify a real deployed environment.",
|
|
1536
|
+
"",
|
|
1537
|
+
"## Scope",
|
|
1538
|
+
f"- Artifact Dir: {artifact_dir or 'not provided'}",
|
|
1539
|
+
"",
|
|
1540
|
+
f"Execution receipts: {len(executions)}",
|
|
1541
|
+
f"Differential proofs: {len(proofs) if isinstance(proofs, list) else 0}",
|
|
1542
|
+
"The legacy test-report interface is not supported.",
|
|
1543
|
+
"",
|
|
1544
|
+
]
|
|
1545
|
+
return "\n".join(lines)
|
|
1546
|
+
|
|
1547
|
+
|
|
1548
|
+
def refresh_final_record_hashes(root: Path, manifest: dict[str, Any]) -> None:
|
|
1549
|
+
for entry in manifest.get("files", []):
|
|
1550
|
+
if isinstance(entry, dict) and entry.get("path") in FINAL_RECORDS:
|
|
1551
|
+
entry["sha256"] = file_sha256(root / entry["path"])
|
|
1552
|
+
atomic_write_json(root / "manifest.json", manifest)
|
|
1553
|
+
|
|
1554
|
+
|
|
1376
1555
|
def main() -> int:
|
|
1377
1556
|
parser = argparse.ArgumentParser(description="Validate a PrizmKit test evidence package")
|
|
1378
1557
|
parser.add_argument("--evidence-dir", required=True, help="Path to .prizmkit/test/evidence/<evidence-id>")
|
|
@@ -1388,7 +1567,7 @@ def main() -> int:
|
|
|
1388
1567
|
except IndexError:
|
|
1389
1568
|
print("Test evidence validation: FAIL\n- cannot infer project root from evidence directory")
|
|
1390
1569
|
return 1
|
|
1391
|
-
errors = validate_evidence(root, project_root,
|
|
1570
|
+
errors = validate_evidence(root, project_root, ATTESTATION_MUTABLE if args.attest else None)
|
|
1392
1571
|
if errors:
|
|
1393
1572
|
print("Test evidence validation: FAIL")
|
|
1394
1573
|
for error in errors:
|
|
@@ -1396,6 +1575,7 @@ def main() -> int:
|
|
|
1396
1575
|
return 1
|
|
1397
1576
|
|
|
1398
1577
|
manifest = load_json(root / "manifest.json")
|
|
1578
|
+
verdict_record = load_json(root / "verdict.json")
|
|
1399
1579
|
if args.attest:
|
|
1400
1580
|
executions = load_json(root / "executions.json")
|
|
1401
1581
|
replayed_ids = [
|
|
@@ -1421,10 +1601,9 @@ def main() -> int:
|
|
|
1421
1601
|
"replay_receipt_ids": replayed_ids,
|
|
1422
1602
|
}
|
|
1423
1603
|
atomic_write_json(root / "validation.json", validation)
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
atomic_write_json(root / "manifest.json", manifest)
|
|
1604
|
+
report_text = render_report_text(root, manifest, verdict_record, validation)
|
|
1605
|
+
atomic_write_text(root / "test-report.md", report_text)
|
|
1606
|
+
refresh_final_record_hashes(root, manifest)
|
|
1428
1607
|
errors = validate_evidence(root, project_root)
|
|
1429
1608
|
if errors:
|
|
1430
1609
|
print("Test evidence validation: FAIL")
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "prizmkit-workflow"
|
|
3
|
+
description: "Coordinate one explicitly requested formal software requirement through the complete PrizmKit lifecycle from plan to confirmed commit. Use when the user invokes prizmkit-workflow or explicitly asks for the full six-stage lifecycle, a plan-to-commit workflow, or one formal requirement completed end to end. Do not trigger on generic build, implement, feature, bug, or refactor requests; those belong to the applicable individual skill or an external integration. (project)"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# PrizmKit Workflow
|
|
7
|
+
|
|
8
|
+
`/prizmkit-workflow` is the optional composite entry point for one interactive formal software requirement. The user describes the requirement once; this skill coordinates the six atomic lifecycle skills in order and preserves the same requirement context throughout. It does not replace or reimplement any atomic stage.
|
|
9
|
+
|
|
10
|
+
```text
|
|
11
|
+
prizmkit-plan
|
|
12
|
+
→ prizmkit-implement
|
|
13
|
+
→ prizmkit-code-review
|
|
14
|
+
→ prizmkit-test
|
|
15
|
+
→ prizmkit-retrospective
|
|
16
|
+
→ prizmkit-committer
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The six stages are mandatory for a formal requirement. The order is not a suggestion and no stage is silently optional.
|
|
20
|
+
|
|
21
|
+
## When to Use
|
|
22
|
+
|
|
23
|
+
Use this composite entry point when the user:
|
|
24
|
+
|
|
25
|
+
- explicitly invokes `/prizmkit-workflow`;
|
|
26
|
+
- explicitly asks for the full six-stage PrizmKit lifecycle;
|
|
27
|
+
- asks for one formal requirement to be coordinated from plan through commit;
|
|
28
|
+
- asks for a plan-to-commit or single-requirement full lifecycle;
|
|
29
|
+
- wants the workflow to continue automatically after each successful stage.
|
|
30
|
+
|
|
31
|
+
Generic "implement", "build", "add feature", "fix bug", or "refactor" requests do not select this workflow by themselves. Use the applicable individual skill unless the user explicitly asks for this complete lifecycle; external integrations may also invoke individual skills through their published contracts.
|
|
32
|
+
|
|
33
|
+
Use an individual atomic stage skill when the user explicitly wants only planning, implementation, review, testing, documentation maintenance, or committing.
|
|
34
|
+
|
|
35
|
+
Do not use this skill for:
|
|
36
|
+
|
|
37
|
+
- a typo, pure formatting change, or explicitly low-risk direct edit;
|
|
38
|
+
- first-time project initialization only; recommend `/prizmkit-init`;
|
|
39
|
+
- standalone Prizm documentation repair; use `/prizmkit-prizm-docs`;
|
|
40
|
+
- deployment or operations; use `/prizmkit-deploy` separately;
|
|
41
|
+
- multiple requirements in one invocation; this composite handles one requirement only.
|
|
42
|
+
|
|
43
|
+
## Inputs
|
|
44
|
+
|
|
45
|
+
Accept:
|
|
46
|
+
|
|
47
|
+
- `description`: the natural-language formal requirement;
|
|
48
|
+
- `artifact_dir`: optional explicit requirement artifact root;
|
|
49
|
+
- `execution_mode`: `interactive` by default; headless only when a trusted host explicitly authorizes it;
|
|
50
|
+
- `resume`: optional workflow state path or requirement slug for recovery.
|
|
51
|
+
|
|
52
|
+
If `description` is missing and no resumable workflow is supplied, ask for the requirement before invoking `prizmkit-plan`. External automation must invoke atomic stages directly with its own execution checkpoint rather than nesting this composite workflow.
|
|
53
|
+
|
|
54
|
+
## Core Orchestration Rules
|
|
55
|
+
|
|
56
|
+
### 1. Start with Plan
|
|
57
|
+
|
|
58
|
+
Invoke `/prizmkit-plan` with the requirement and any explicit `artifact_dir`. Do not write a second plan in this orchestrator.
|
|
59
|
+
|
|
60
|
+
If initialization context is missing, allow `prizmkit-plan` to recommend initialization and continue with source fallback when the user chooses to proceed. Initialization is a soft prerequisite, not a hidden lifecycle stage.
|
|
61
|
+
|
|
62
|
+
### 2. Preserve Requirement Identity
|
|
63
|
+
|
|
64
|
+
Once `prizmkit-plan` resolves an `artifact_dir`, capture it and pass the exact same value to every later stage. The artifact root is generic and is not restricted to one directory family:
|
|
65
|
+
|
|
66
|
+
```text
|
|
67
|
+
.prizmkit/specs/<requirement-slug>/
|
|
68
|
+
.prizmkit/bugfix/<bug-id>/
|
|
69
|
+
.prizmkit/refactor/<refactor-id>/
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Never select a different most-recent plan when resuming or handing off. The workflow state path is:
|
|
73
|
+
|
|
74
|
+
```text
|
|
75
|
+
.prizmkit/state/workflows/<requirement-slug>.json
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Read `${SKILL_DIR}/references/workflow-state-protocol.md` for the shared state contract. This workflow state remains distinct from any external host execution checkpoint. The target project controls whether generated `.prizmkit/` files are committed, ignored, or shared; do not modify its Git policy.
|
|
79
|
+
|
|
80
|
+
### 3. Advance Only on Truthful Success
|
|
81
|
+
|
|
82
|
+
After each atomic stage:
|
|
83
|
+
|
|
84
|
+
1. Read the stage's actual output and terminal status.
|
|
85
|
+
2. Validate the expected authoritative artifact or evidence and workflow-state transition.
|
|
86
|
+
3. Preserve the same `artifact_dir`.
|
|
87
|
+
4. Continue only on the permitted success status.
|
|
88
|
+
5. Because this composite is the active orchestrator, atomic skills return terminal state and `next_stage` to it; they must not invoke the next stage recursively.
|
|
89
|
+
6. If the host cannot invoke another skill automatically, stop with exactly one deterministic next skill, its `artifact_dir`, and the workflow-state path.
|
|
90
|
+
|
|
91
|
+
Expected transitions:
|
|
92
|
+
|
|
93
|
+
| Stage | Required success | Next stage |
|
|
94
|
+
|---|---|---|
|
|
95
|
+
| `prizmkit-plan` | `PLAN_READY` | `prizmkit-implement` |
|
|
96
|
+
| `prizmkit-implement` | `IMPLEMENTED` | `prizmkit-code-review` |
|
|
97
|
+
| `prizmkit-code-review` | `REVIEW_PASS` | `prizmkit-test` |
|
|
98
|
+
| `prizmkit-test` | `TEST_PASS` | `prizmkit-retrospective` |
|
|
99
|
+
| `prizmkit-retrospective` | `status=RETRO_COMPLETE` with result `DOCS_UPDATED` or `NO_DOC_CHANGE` | `prizmkit-committer` |
|
|
100
|
+
| `prizmkit-committer` | explicit interactive confirmation, then `COMMITTED` | end |
|
|
101
|
+
|
|
102
|
+
`TEST_NOT_APPLICABLE` is not a valid lifecycle success. Lightweight changes must execute deterministic verification and return `TEST_PASS`.
|
|
103
|
+
|
|
104
|
+
### 4. Do Not Duplicate Stage Responsibilities
|
|
105
|
+
|
|
106
|
+
The composite must not:
|
|
107
|
+
|
|
108
|
+
- reinterpret a plan as implementation;
|
|
109
|
+
- repair production code outside `prizmkit-implement` or the Main-Agent review loop;
|
|
110
|
+
- claim tests passed without validated authoritative test evidence;
|
|
111
|
+
- treat a derived Markdown report as the test source of truth;
|
|
112
|
+
- perform retrospective documentation changes itself;
|
|
113
|
+
- stage or commit before `prizmkit-committer` applies the current execution's authorization boundary;
|
|
114
|
+
- invoke `prizmkit-deploy` as a hidden seventh stage.
|
|
115
|
+
|
|
116
|
+
## Failure and Repair Routing
|
|
117
|
+
|
|
118
|
+
Use the shared workflow state and authoritative stage evidence to determine routing. Do not blindly retry every failure.
|
|
119
|
+
|
|
120
|
+
### Review Failure
|
|
121
|
+
|
|
122
|
+
`REVIEW_NEEDS_FIXES` maps from the final review report result `NEEDS_FIXES`. Route to:
|
|
123
|
+
|
|
124
|
+
```text
|
|
125
|
+
prizmkit-implement
|
|
126
|
+
→ prizmkit-code-review
|
|
127
|
+
→ prizmkit-test
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
The Main-Agent review skill owns its internal review repairs and its internal ten-round limit before returning its terminal result. The outer workflow repair counter is separate.
|
|
131
|
+
|
|
132
|
+
### Test Failure
|
|
133
|
+
|
|
134
|
+
`TEST_FAIL` must come from the validated authoritative evidence verdict and include `repair_scope`:
|
|
135
|
+
|
|
136
|
+
TEST_FAIL affecting only tests, fixtures, test-runner configuration, or evidence infrastructure uses `repair_scope=test-infrastructure`:
|
|
137
|
+
|
|
138
|
+
```text
|
|
139
|
+
TEST_FAIL with repair_scope=test-infrastructure
|
|
140
|
+
→ prizmkit-implement
|
|
141
|
+
→ prizmkit-test
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Test infrastructure includes tests, fixtures, test-runner configuration, and evidence infrastructure only.
|
|
145
|
+
|
|
146
|
+
TEST_FAIL affecting production code, runtime configuration, schema, dependencies, or public interfaces uses the matching production-family scope:
|
|
147
|
+
|
|
148
|
+
```text
|
|
149
|
+
TEST_FAIL with repair_scope=production | runtime | schema | dependency | public-interface
|
|
150
|
+
→ prizmkit-implement
|
|
151
|
+
→ prizmkit-code-review
|
|
152
|
+
→ prizmkit-test
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Production-affecting repair after `REVIEW_PASS` must receive another review. A test-infrastructure-only repair may return directly to testing. Unknown scope uses the conservative production route only when safe; otherwise the workflow is blocked.
|
|
156
|
+
|
|
157
|
+
### Environment Block
|
|
158
|
+
|
|
159
|
+
`TEST_BLOCKED` means trustworthy evidence is unavailable because of environment, permission, dependency, external-service, scope, evidence, reliability, cleanup, or budget problems.
|
|
160
|
+
|
|
161
|
+
Interactive behavior:
|
|
162
|
+
|
|
163
|
+
```text
|
|
164
|
+
TEST_BLOCKED
|
|
165
|
+
→ persist the blocker
|
|
166
|
+
→ do not make speculative production edits
|
|
167
|
+
→ stop with a deterministic prizmkit-test resume entry
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
A trusted headless host performs its own bounded automatic environment recovery when invoking atomic stages. It does not invoke this composite workflow or silently turn a blocked result into success.
|
|
171
|
+
|
|
172
|
+
### Repair Limit
|
|
173
|
+
|
|
174
|
+
The outer orchestrator allows at most three automatic repair rounds. These are cross-stage rounds: one repair route from `implement` through all gates required by the repair scope.
|
|
175
|
+
|
|
176
|
+
```text
|
|
177
|
+
repair_round: 0 → 1 → 2 → 3
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
When the limit is reached:
|
|
181
|
+
|
|
182
|
+
- set workflow status to `WORKFLOW_BLOCKED`;
|
|
183
|
+
- preserve the latest authoritative failure evidence;
|
|
184
|
+
- report completed rounds and unresolved cause;
|
|
185
|
+
- report the exact skill, `artifact_dir`, and state path from which a user may resume after resolving the cause or explicitly authorizing another attempt;
|
|
186
|
+
- do not claim the requirement is complete.
|
|
187
|
+
|
|
188
|
+
The internal `prizmkit-code-review` limit of ten completed review rounds remains separate and does not increment `repair_round`.
|
|
189
|
+
|
|
190
|
+
## Commit Authorization Boundary
|
|
191
|
+
|
|
192
|
+
The composite may automatically reach `/prizmkit-committer`, but it must not silently create a Git commit.
|
|
193
|
+
|
|
194
|
+
Interactive execution requires the committer to:
|
|
195
|
+
|
|
196
|
+
1. verify all five preceding stage results for the same `artifact_dir`;
|
|
197
|
+
2. inspect the final workspace;
|
|
198
|
+
3. present intended files, diff summary, sensitive-file warnings, and the proposed Conventional Commit message;
|
|
199
|
+
4. wait for explicit user confirmation from the current user;
|
|
200
|
+
5. create and verify the local commit only after confirmation.
|
|
201
|
+
|
|
202
|
+
Trusted headless execution is a separate atomic-stage path. It requires a host-defined non-interactive `mode`, a trusted `owner` identifier, and `local_commit_authorized=true`; it does not ask or wait. Unknown headless contexts are blocked. Remote publication is a separate host-runtime operation and is never decided by this composite workflow.
|
|
203
|
+
|
|
204
|
+
## Automatic Handoff and Manual Fallback
|
|
205
|
+
|
|
206
|
+
When the host supports semantic skill-to-skill invocation, the active composite invokes the next atomic stage after each permitted success transition.
|
|
207
|
+
|
|
208
|
+
When it does not:
|
|
209
|
+
|
|
210
|
+
1. update or verify workflow state;
|
|
211
|
+
2. stop without claiming the next stage ran;
|
|
212
|
+
3. print one exact recovery instruction:
|
|
213
|
+
|
|
214
|
+
```text
|
|
215
|
+
Next stage: /prizmkit-<skill>
|
|
216
|
+
artifact_dir: <same resolved artifact_dir>
|
|
217
|
+
workflow_state: .prizmkit/state/workflows/<requirement-slug>.json
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
The user can invoke that one atomic skill and this composite can resume with `resume` later.
|
|
221
|
+
|
|
222
|
+
## Resume Protocol
|
|
223
|
+
|
|
224
|
+
On resume:
|
|
225
|
+
|
|
226
|
+
1. Read the workflow state specified by `resume` or discover the target project's active workflow state.
|
|
227
|
+
2. Read `${SKILL_DIR}/references/workflow-state-protocol.md`.
|
|
228
|
+
3. Verify `spec.md`, `plan.md`, review report, authoritative test evidence, retrospective result, and current workspace against state.
|
|
229
|
+
4. If state is missing or stale, reconstruct the safest recoverable predecessor and report the reconstruction.
|
|
230
|
+
5. Continue from the first incomplete stage; never bypass a required gate based only on stale state.
|
|
231
|
+
6. Preserve the same `artifact_dir` and repair-round count.
|
|
232
|
+
7. When an external host is involved, let it validate its own checkpoint independently; never merge that checkpoint into workflow state.
|
|
233
|
+
|
|
234
|
+
## Completion Report
|
|
235
|
+
|
|
236
|
+
At successful completion, report:
|
|
237
|
+
|
|
238
|
+
```text
|
|
239
|
+
WORKFLOW_COMPLETE
|
|
240
|
+
artifact_dir: <path>
|
|
241
|
+
stages:
|
|
242
|
+
- PLAN_READY
|
|
243
|
+
- IMPLEMENTED
|
|
244
|
+
- REVIEW_PASS
|
|
245
|
+
- TEST_PASS
|
|
246
|
+
- RETRO_COMPLETE (DOCS_UPDATED | NO_DOC_CHANGE)
|
|
247
|
+
- COMMITTED
|
|
248
|
+
commit: <hash>
|
|
249
|
+
push: not performed automatically
|
|
250
|
+
next_action: invoke /prizmkit-deploy separately if deployment is needed
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
If the user declines interactive commit confirmation, report `COMMIT_PENDING` rather than `WORKFLOW_COMPLETE` and provide the exact `/prizmkit-committer` resume entry.
|
|
254
|
+
|
|
255
|
+
If blocked, report:
|
|
256
|
+
|
|
257
|
+
```text
|
|
258
|
+
WORKFLOW_BLOCKED
|
|
259
|
+
stage: <stage>
|
|
260
|
+
reason: <evidence-backed reason>
|
|
261
|
+
repair_round: <0..3>
|
|
262
|
+
resume_from: <skill>
|
|
263
|
+
artifact_dir: <same path>
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
Never report success for a stage that did not produce its required terminal result.
|