prizmkit 1.1.120 → 1.1.121
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundled/VERSION.json +3 -3
- package/bundled/dev-pipeline/prizmkit_runtime/runner_classification.py +23 -25
- package/bundled/dev-pipeline/prizmkit_runtime/runner_models.py +8 -2
- package/bundled/dev-pipeline/prizmkit_runtime/runner_prompts.py +10 -1
- package/bundled/dev-pipeline/prizmkit_runtime/runners.py +43 -29
- package/bundled/dev-pipeline/scripts/continuation.py +51 -6
- package/bundled/dev-pipeline/scripts/update-bug-status.py +57 -14
- package/bundled/dev-pipeline/scripts/update-feature-status.py +54 -5
- package/bundled/dev-pipeline/scripts/update-refactor-status.py +57 -14
- package/bundled/dev-pipeline/templates/bootstrap-tier3.md +2 -2
- package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +2 -1
- package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +2 -2
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-review.md +3 -3
- package/bundled/dev-pipeline/templates/sections/phase-review-agent.md +3 -3
- package/bundled/dev-pipeline/templates/sections/phase-review-full.md +4 -4
- package/bundled/dev-pipeline/templates/sections/refactor-phase-review.md +3 -3
- package/bundled/dev-pipeline/tests/test_auto_skip.py +5 -5
- package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +10 -3
- package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +9 -2
- package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +9 -1
- package/bundled/dev-pipeline/tests/test_python_runner_parity.py +222 -18
- package/bundled/dev-pipeline/tests/test_unified_cli.py +14 -10
- package/bundled/skills/_metadata.json +4 -4
- package/bundled/skills/prizmkit-code-review/SKILL.md +49 -48
- package/bundled/skills/prizmkit-code-review/references/review-report-template.md +13 -8
- package/bundled/skills/prizmkit-code-review/references/reviewer-agent-prompt.md +46 -32
- package/bundled/skills/prizmkit-code-review/references/reviewer-workspace-protocol.md +90 -0
- package/bundled/skills/prizmkit-test/SKILL.md +159 -155
- package/bundled/skills/prizmkit-test/assets/authoritative-records.schema.json +420 -0
- package/bundled/skills/prizmkit-test/assets/behavior-risk-matrix.schema.json +116 -0
- package/bundled/skills/prizmkit-test/assets/evidence-manifest.schema.json +112 -0
- package/bundled/skills/prizmkit-test/assets/evidence-package-template.json +97 -0
- package/bundled/skills/prizmkit-test/references/boundary-coverage-protocol.md +76 -192
- package/bundled/skills/prizmkit-test/references/contract-mock-protocol.md +65 -0
- package/bundled/skills/prizmkit-test/references/evidence-protocol.md +194 -0
- package/bundled/skills/prizmkit-test/references/evidence-request-protocol.md +78 -0
- package/bundled/skills/prizmkit-test/references/examples.md +65 -108
- package/bundled/skills/prizmkit-test/references/service-boundary-test-catalog.md +10 -7
- package/bundled/skills/prizmkit-test/references/test-generation-steps.md +90 -82
- package/bundled/skills/prizmkit-test/references/test-report-template.md +77 -98
- package/bundled/skills/prizmkit-test/references/trusted-evidence-execution.md +97 -0
- package/bundled/skills/prizmkit-test/scripts/build_test_evidence.py +723 -0
- package/bundled/skills/prizmkit-test/scripts/validate_test_evidence.py +1426 -0
- package/package.json +1 -1
- package/bundled/skills/prizmkit-test/scripts/validate_boundary_report.py +0 -347
|
@@ -904,7 +904,20 @@ def _install_runner_session_fakes(monkeypatch, runners, *, statuses=("success",)
|
|
|
904
904
|
|
|
905
905
|
def fake_update(family, invocation, item_id, session_status, session_id, project_root, **metadata):
|
|
906
906
|
updates.append((session_status, session_id, metadata))
|
|
907
|
-
|
|
907
|
+
current_update_status = update_status
|
|
908
|
+
if session_status in {"context_overflow", "infra_error", "crashed", "timed_out", "failed"} and len(status_queue) > 0:
|
|
909
|
+
current_update_status = "in_progress"
|
|
910
|
+
return SimpleNamespace(
|
|
911
|
+
ok=True,
|
|
912
|
+
text="",
|
|
913
|
+
data={
|
|
914
|
+
"new_status": current_update_status,
|
|
915
|
+
"retry_count": sum(status in {"crashed", "timed_out", "failed"} for status, *_ in updates),
|
|
916
|
+
"infra_error_count": sum(status == "infra_error" for status, *_ in updates),
|
|
917
|
+
"continuation_count": len(updates),
|
|
918
|
+
"context_overflow_count": sum(status == "context_overflow" for status, *_ in updates),
|
|
919
|
+
},
|
|
920
|
+
)
|
|
908
921
|
|
|
909
922
|
def fake_cleanup(runtime, *, delete_branch=True):
|
|
910
923
|
cleanup_calls.append((runtime, delete_branch))
|
|
@@ -1096,8 +1109,8 @@ def test_explicit_worktree_materializes_ignored_support_assets_before_prompt_and
|
|
|
1096
1109
|
(project / ".prizmkit" / "prizm-docs" / "root.prizm").write_text("PRIZM_VERSION: 1\n", encoding="utf-8")
|
|
1097
1110
|
(project / ".prizmkit" / "dev-pipeline" / "scripts").mkdir(parents=True)
|
|
1098
1111
|
(project / ".prizmkit" / "dev-pipeline" / "scripts" / "init-change-artifact.py").write_text("# init", encoding="utf-8")
|
|
1099
|
-
(project / ".prizmkit" / "config.json").write_text("
|
|
1100
|
-
(project / ".prizmkit" / "manifest.json").write_text("
|
|
1112
|
+
(project / ".prizmkit" / "config.json").write_text(json.dumps({"completion_notes": ["Completed durable workflow"]}), encoding="utf-8")
|
|
1113
|
+
(project / ".prizmkit" / "manifest.json").write_text(json.dumps({"completion_notes": ["Completed durable workflow"]}), encoding="utf-8")
|
|
1101
1114
|
|
|
1102
1115
|
paths = resolve_runtime_paths(pipeline_root=PIPELINE_ROOT, project_root=project)
|
|
1103
1116
|
family = family_for("feature", paths)
|
|
@@ -1225,7 +1238,7 @@ def test_has_branch_completion_evidence_requires_task_branch_ahead_of_base(tmp_p
|
|
|
1225
1238
|
subprocess.run(["git", "config", "user.name", "Test"], cwd=tmp_path, check=True)
|
|
1226
1239
|
artifact_dir = tmp_path / ".prizmkit" / "specs" / "001-done"
|
|
1227
1240
|
artifact_dir.mkdir(parents=True)
|
|
1228
|
-
(artifact_dir / "completion-summary.json").write_text("
|
|
1241
|
+
(artifact_dir / "completion-summary.json").write_text(json.dumps({"completion_notes": ["Completed durable workflow"]}), encoding="utf-8")
|
|
1229
1242
|
(tmp_path / "base.txt").write_text("base", encoding="utf-8")
|
|
1230
1243
|
subprocess.run(["git", "add", "base.txt"], cwd=tmp_path, check=True)
|
|
1231
1244
|
subprocess.run(["git", "commit", "-m", "base"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
@@ -1405,7 +1418,7 @@ def test_default_branch_names_keep_timestamp_for_each_runner_family(monkeypatch,
|
|
|
1405
1418
|
("refactor", "R-001", "refactor/R-001-20260707010203"),
|
|
1406
1419
|
],
|
|
1407
1420
|
)
|
|
1408
|
-
def
|
|
1421
|
+
def test_no_worktree_exhausted_failure_stays_on_task_branch_without_merge_for_all_families(monkeypatch, tmp_path, kind, item_id, branch):
|
|
1409
1422
|
from prizmkit_runtime import runners
|
|
1410
1423
|
from prizmkit_runtime.runner_models import family_for, parse_invocation
|
|
1411
1424
|
|
|
@@ -1415,7 +1428,7 @@ def test_no_worktree_failure_stays_on_task_branch_without_merge_for_all_families
|
|
|
1415
1428
|
invocation = parse_invocation(family, "run", ())
|
|
1416
1429
|
plan_path = {"feature": paths.feature_plan, "bugfix": paths.bugfix_plan, "refactor": paths.refactor_plan}[kind]
|
|
1417
1430
|
invocation = invocation.__class__(**{**invocation.__dict__, "list_path": plan_path})
|
|
1418
|
-
_install_runner_session_fakes(monkeypatch, runners, statuses=("crashed",), update_status="
|
|
1431
|
+
_install_runner_session_fakes(monkeypatch, runners, statuses=("crashed",), update_status="failed")
|
|
1419
1432
|
events = []
|
|
1420
1433
|
final_commits = []
|
|
1421
1434
|
wip_commits = []
|
|
@@ -1431,12 +1444,12 @@ def test_no_worktree_failure_stays_on_task_branch_without_merge_for_all_families
|
|
|
1431
1444
|
|
|
1432
1445
|
status = runners._process_item(family, invocation, item_id, paths, initial_metadata={})
|
|
1433
1446
|
|
|
1434
|
-
assert status == "
|
|
1447
|
+
assert status == "failed"
|
|
1435
1448
|
assert events[0] == "branch_create"
|
|
1436
1449
|
assert "branch_merge" not in events
|
|
1437
1450
|
assert events[-1] == ("return", "main", branch)
|
|
1438
|
-
assert final_commits == [(paths.project_root, item_id, "
|
|
1439
|
-
assert wip_commits == [(paths.project_root, item_id, "
|
|
1451
|
+
assert final_commits == [(paths.project_root, item_id, "failed")]
|
|
1452
|
+
assert wip_commits == [(paths.project_root, item_id, "failed")]
|
|
1440
1453
|
|
|
1441
1454
|
|
|
1442
1455
|
def test_no_worktree_runner_ignores_recovery_side_effect_branch(monkeypatch, tmp_path):
|
|
@@ -1845,7 +1858,7 @@ def test_bookkeeping_commits_only_runner_owned_paths(tmp_path):
|
|
|
1845
1858
|
item_state.mkdir(parents=True)
|
|
1846
1859
|
plan = plans / "feature-list.json"
|
|
1847
1860
|
plan.write_text('{"features": []}', encoding="utf-8")
|
|
1848
|
-
(item_state / "status.json").write_text("
|
|
1861
|
+
(item_state / "status.json").write_text(json.dumps({"completion_notes": ["Completed durable workflow"]}), encoding="utf-8")
|
|
1849
1862
|
source = tmp_path / "src.txt"
|
|
1850
1863
|
source.write_text("base", encoding="utf-8")
|
|
1851
1864
|
subprocess.run(["git", "add", ".prizmkit/plans/feature-list.json", ".prizmkit/state/features/F-001/status.json", "src.txt"], cwd=tmp_path, check=True)
|
|
@@ -1899,6 +1912,8 @@ def test_context_overflow_reuses_worktree_and_skips_cleanup_until_terminal_outco
|
|
|
1899
1912
|
invocation = parse_invocation(family, "run", ())
|
|
1900
1913
|
invocation = invocation.__class__(**{**invocation.__dict__, "list_path": paths.feature_plan})
|
|
1901
1914
|
observed = _install_runner_session_fakes(monkeypatch, runners, statuses=("context_overflow", "success"))
|
|
1915
|
+
sleep_calls = []
|
|
1916
|
+
monkeypatch.setattr(runners.time, "sleep", lambda seconds: sleep_calls.append(seconds))
|
|
1902
1917
|
setup_calls = []
|
|
1903
1918
|
monkeypatch.setattr(runners, "ensure_linked_worktree", lambda runtime: (setup_calls.append(runtime) or SimpleNamespace(ok=True, runtime=runtime, reason="")), raising=False)
|
|
1904
1919
|
|
|
@@ -1911,6 +1926,7 @@ def test_context_overflow_reuses_worktree_and_skips_cleanup_until_terminal_outco
|
|
|
1911
1926
|
assert observed.prompt_calls[0].get("continuation") is None
|
|
1912
1927
|
assert observed.prompt_calls[1]["continuation"]["previous_session_id"] == "F-001-session-1"
|
|
1913
1928
|
assert observed.updates[0][0] == "context_overflow"
|
|
1929
|
+
assert sleep_calls == [5]
|
|
1914
1930
|
assert len(observed.cleanup_calls) == 1
|
|
1915
1931
|
assert observed.cleanup_calls[0][1] is True
|
|
1916
1932
|
|
|
@@ -1959,7 +1975,7 @@ def test_classification_accepts_late_infra_error_with_completed_checkpoint_and_c
|
|
|
1959
1975
|
assert classification.reason == "completed_checkpoint_with_late_runtime_error"
|
|
1960
1976
|
|
|
1961
1977
|
|
|
1962
|
-
def
|
|
1978
|
+
def test_classification_accepts_late_infra_with_completion_artifact_without_new_commit(tmp_path):
|
|
1963
1979
|
from prizmkit_runtime.runner_classification import classify_session
|
|
1964
1980
|
from prizmkit_runtime.runner_models import PromptGenerationResult
|
|
1965
1981
|
from prizmkit_runtime.sessions import AISessionCommand, AISessionResult
|
|
@@ -1983,7 +1999,7 @@ def test_classification_rejects_late_infra_when_head_diff_has_no_commit_range(tm
|
|
|
1983
1999
|
subprocess.run(["git", "checkout", "side"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
1984
2000
|
artifact_dir = tmp_path / ".prizmkit" / "specs" / "001-done"
|
|
1985
2001
|
artifact_dir.mkdir(parents=True)
|
|
1986
|
-
(artifact_dir / "completion-summary.json").write_text("
|
|
2002
|
+
(artifact_dir / "completion-summary.json").write_text(json.dumps({"completion_notes": ["Completed durable workflow"]}), encoding="utf-8")
|
|
1987
2003
|
log = tmp_path / "session.log"
|
|
1988
2004
|
log.write_text("quota exceeded", encoding="utf-8")
|
|
1989
2005
|
raw = AISessionResult(
|
|
@@ -2004,7 +2020,8 @@ def test_classification_rejects_late_infra_when_head_diff_has_no_commit_range(tm
|
|
|
2004
2020
|
|
|
2005
2021
|
classification = classify_session(raw, project_root=tmp_path, base_head=side_head, prompt=prompt)
|
|
2006
2022
|
|
|
2007
|
-
assert classification.session_status == "
|
|
2023
|
+
assert classification.session_status == "success"
|
|
2024
|
+
assert classification.reason == "completed_checkpoint_with_late_runtime_error"
|
|
2008
2025
|
|
|
2009
2026
|
|
|
2010
2027
|
def test_classification_keeps_pre_completion_provider_error_as_infra_error(tmp_path):
|
|
@@ -2060,7 +2077,7 @@ def test_classification_accepts_terminal_success_with_completed_checkpoint_witho
|
|
|
2060
2077
|
artifact_dir.mkdir(parents=True)
|
|
2061
2078
|
checkpoint = artifact_dir / "workflow-checkpoint.json"
|
|
2062
2079
|
checkpoint.write_text(json.dumps({"steps": [{"id": "S01", "skill": "done", "name": "Done", "status": "completed"}]}), encoding="utf-8")
|
|
2063
|
-
(artifact_dir / "completion-summary.json").write_text("
|
|
2080
|
+
(artifact_dir / "completion-summary.json").write_text(json.dumps({"completion_notes": ["Completed durable workflow"]}), encoding="utf-8")
|
|
2064
2081
|
log = tmp_path / "session.log"
|
|
2065
2082
|
log.write_text("terminal success", encoding="utf-8")
|
|
2066
2083
|
raw = AISessionResult(
|
|
@@ -2122,8 +2139,8 @@ def test_classification_preserves_success_before_context_overflow(tmp_path):
|
|
|
2122
2139
|
)
|
|
2123
2140
|
|
|
2124
2141
|
classification = classify_session(raw, project_root=tmp_path, base_head=base_head)
|
|
2125
|
-
assert classification.session_status == "
|
|
2126
|
-
assert classification.reason == "
|
|
2142
|
+
assert classification.session_status == "context_overflow"
|
|
2143
|
+
assert classification.reason == "context_overflow"
|
|
2127
2144
|
|
|
2128
2145
|
|
|
2129
2146
|
def test_classification_crash_with_commit_since_base_does_not_become_success(tmp_path):
|
|
@@ -2256,7 +2273,7 @@ def _write_family_case(paths, kind, item_id=""):
|
|
|
2256
2273
|
state_dir = paths.refactor_state_dir
|
|
2257
2274
|
plan = paths.refactor_plan
|
|
2258
2275
|
state_dir.mkdir(parents=True, exist_ok=True)
|
|
2259
|
-
(state_dir / "pipeline.json").write_text("
|
|
2276
|
+
(state_dir / "pipeline.json").write_text(json.dumps({"completion_notes": ["Completed durable workflow"]}), encoding="utf-8")
|
|
2260
2277
|
item_state = state_dir / item_id
|
|
2261
2278
|
item_state.mkdir(parents=True, exist_ok=True)
|
|
2262
2279
|
(item_state / "status.json").write_text(
|
|
@@ -2517,7 +2534,7 @@ def test_invalid_existing_checkpoint_blocks_terminal_success_with_completion_sum
|
|
|
2517
2534
|
artifact_dir.mkdir(parents=True)
|
|
2518
2535
|
checkpoint = artifact_dir / "workflow-checkpoint.json"
|
|
2519
2536
|
checkpoint.write_text(json.dumps({"steps": [{"id": "S01", "skill": "done", "name": "Done", "status": "not-valid"}]}), encoding="utf-8")
|
|
2520
|
-
(artifact_dir / "completion-summary.json").write_text("
|
|
2537
|
+
(artifact_dir / "completion-summary.json").write_text(json.dumps({"completion_notes": ["Completed durable workflow"]}), encoding="utf-8")
|
|
2521
2538
|
prompt = PromptGenerationResult(output_path=tmp_path / "prompt.md", checkpoint_path=checkpoint, artifact_path=artifact_dir)
|
|
2522
2539
|
|
|
2523
2540
|
classification = classify_session(_terminal_success_result(tmp_path), project_root=tmp_path, base_head=base_head, prompt=prompt)
|
|
@@ -2597,3 +2614,190 @@ def test_invalid_existing_checkpoint_blocks_workspace_auto_commit_success(tmp_pa
|
|
|
2597
2614
|
|
|
2598
2615
|
assert classification.session_status == "crashed"
|
|
2599
2616
|
assert classification.reason == "invalid_workflow_checkpoint:empty_steps"
|
|
2617
|
+
|
|
2618
|
+
|
|
2619
|
+
@pytest.mark.parametrize(
|
|
2620
|
+
("kind", "item_id", "branch"),
|
|
2621
|
+
[
|
|
2622
|
+
("feature", "F-001", "dev/F-001-ai-retry"),
|
|
2623
|
+
("bugfix", "B-001", "bugfix/B-001-ai-retry"),
|
|
2624
|
+
("refactor", "R-001", "refactor/R-001-ai-retry"),
|
|
2625
|
+
],
|
|
2626
|
+
)
|
|
2627
|
+
@pytest.mark.parametrize("retry_status", ["infra_error", "crashed", "timed_out"])
|
|
2628
|
+
def test_retryable_ai_errors_continue_in_process_for_all_families(monkeypatch, tmp_path, kind, item_id, branch, retry_status):
|
|
2629
|
+
from prizmkit_runtime import runners
|
|
2630
|
+
from prizmkit_runtime.runner_models import family_for, parse_invocation
|
|
2631
|
+
|
|
2632
|
+
monkeypatch.setenv("DEV_BRANCH", branch)
|
|
2633
|
+
paths = _make_paths(tmp_path / kind / retry_status)
|
|
2634
|
+
family = family_for(kind, paths)
|
|
2635
|
+
invocation = parse_invocation(family, "run", ())
|
|
2636
|
+
plan_path = {"feature": paths.feature_plan, "bugfix": paths.bugfix_plan, "refactor": paths.refactor_plan}[kind]
|
|
2637
|
+
invocation = invocation.__class__(**{**invocation.__dict__, "list_path": plan_path})
|
|
2638
|
+
observed = _install_runner_session_fakes(monkeypatch, runners, statuses=(retry_status, "success"))
|
|
2639
|
+
sleep_calls = []
|
|
2640
|
+
branch_plans = []
|
|
2641
|
+
monkeypatch.setattr(runners.time, "sleep", lambda seconds: sleep_calls.append(seconds))
|
|
2642
|
+
monkeypatch.setattr(
|
|
2643
|
+
runners,
|
|
2644
|
+
"run_git_plan",
|
|
2645
|
+
lambda project_root, plan: (branch_plans.append(plan.name) or SimpleNamespace(ok=True, plan=plan, results=())),
|
|
2646
|
+
)
|
|
2647
|
+
|
|
2648
|
+
status = runners._process_item(family, invocation, item_id, paths, initial_metadata={})
|
|
2649
|
+
|
|
2650
|
+
assert status == "completed"
|
|
2651
|
+
assert sleep_calls == [5]
|
|
2652
|
+
assert len(observed.launch_cwds) == 2
|
|
2653
|
+
assert observed.launch_cwds[0] == observed.launch_cwds[1]
|
|
2654
|
+
assert branch_plans.count("branch_create") == 1
|
|
2655
|
+
assert observed.updates[0][0] == retry_status
|
|
2656
|
+
continuation = observed.prompt_calls[1]["continuation"]
|
|
2657
|
+
assert continuation["previous_session_id"].endswith("session-1")
|
|
2658
|
+
assert continuation["failure_classification"] == retry_status
|
|
2659
|
+
assert continuation["active_dev_branch"] == branch
|
|
2660
|
+
|
|
2661
|
+
|
|
2662
|
+
def test_unique_session_ids_do_not_collide_when_wall_clock_second_is_frozen(monkeypatch):
|
|
2663
|
+
from prizmkit_runtime import runners
|
|
2664
|
+
|
|
2665
|
+
monkeypatch.setattr(runners.time, "strftime", lambda _fmt: "20260711030000")
|
|
2666
|
+
values = iter((101, 102))
|
|
2667
|
+
monkeypatch.setattr(runners.time, "time_ns", lambda: next(values))
|
|
2668
|
+
|
|
2669
|
+
first = runners._session_id("F-001")
|
|
2670
|
+
second = runners._session_id("F-001")
|
|
2671
|
+
|
|
2672
|
+
assert first != second
|
|
2673
|
+
assert first.endswith("-101")
|
|
2674
|
+
assert second.endswith("-102")
|
|
2675
|
+
|
|
2676
|
+
|
|
2677
|
+
@pytest.mark.parametrize("summary_text", ["{}", "{not-json", '{"completion_notes": []}', '{"completion_notes": [""]}'])
|
|
2678
|
+
def test_invalid_completion_summary_does_not_prove_late_runtime_success(tmp_path, summary_text):
|
|
2679
|
+
from prizmkit_runtime.runner_classification import classify_session
|
|
2680
|
+
from prizmkit_runtime.runner_models import PromptGenerationResult
|
|
2681
|
+
from prizmkit_runtime.sessions import AISessionCommand, AISessionResult
|
|
2682
|
+
from prizmkit_runtime.status import ProgressSummary
|
|
2683
|
+
|
|
2684
|
+
subprocess.run(["git", "init", "-b", "main"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
2685
|
+
subprocess.run(["git", "config", "user.email", "test@example.com"], cwd=tmp_path, check=True)
|
|
2686
|
+
subprocess.run(["git", "config", "user.name", "Test"], cwd=tmp_path, check=True)
|
|
2687
|
+
(tmp_path / "base.txt").write_text("base", encoding="utf-8")
|
|
2688
|
+
subprocess.run(["git", "add", "base.txt"], cwd=tmp_path, check=True)
|
|
2689
|
+
subprocess.run(["git", "commit", "-m", "base"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
2690
|
+
base_head = subprocess.run(["git", "rev-parse", "HEAD"], cwd=tmp_path, check=True, stdout=subprocess.PIPE, text=True).stdout.strip()
|
|
2691
|
+
artifact_dir = tmp_path / ".prizmkit" / "specs" / "001-invalid"
|
|
2692
|
+
artifact_dir.mkdir(parents=True)
|
|
2693
|
+
(artifact_dir / "completion-summary.json").write_text(summary_text, encoding="utf-8")
|
|
2694
|
+
log = tmp_path / "session.log"
|
|
2695
|
+
log.write_text("quota exceeded", encoding="utf-8")
|
|
2696
|
+
result = AISessionResult(
|
|
2697
|
+
command=AISessionCommand(("fake",), None, "codebuddy", "stdin"),
|
|
2698
|
+
exit_code=1,
|
|
2699
|
+
timed_out=False,
|
|
2700
|
+
termination_reason=None,
|
|
2701
|
+
stale_marker=None,
|
|
2702
|
+
fatal_error_code="api_error",
|
|
2703
|
+
progress_summary=ProgressSummary(terminal_result_text="quota exceeded"),
|
|
2704
|
+
session_log=log,
|
|
2705
|
+
backup_log=tmp_path / "backup.log",
|
|
2706
|
+
log_recovery=None,
|
|
2707
|
+
started_epoch=0,
|
|
2708
|
+
ended_epoch=1,
|
|
2709
|
+
)
|
|
2710
|
+
|
|
2711
|
+
classification = classify_session(
|
|
2712
|
+
result,
|
|
2713
|
+
project_root=tmp_path,
|
|
2714
|
+
base_head=base_head,
|
|
2715
|
+
prompt=PromptGenerationResult(output_path=tmp_path / "prompt.md", artifact_path=artifact_dir),
|
|
2716
|
+
)
|
|
2717
|
+
|
|
2718
|
+
assert classification.session_status == "infra_error"
|
|
2719
|
+
|
|
2720
|
+
|
|
2721
|
+
def test_non_utf8_completion_summary_does_not_abort_classification(tmp_path):
|
|
2722
|
+
from prizmkit_runtime.runner_classification import classify_session
|
|
2723
|
+
from prizmkit_runtime.runner_models import PromptGenerationResult
|
|
2724
|
+
from prizmkit_runtime.sessions import AISessionCommand, AISessionResult
|
|
2725
|
+
from prizmkit_runtime.status import ProgressSummary
|
|
2726
|
+
|
|
2727
|
+
subprocess.run(["git", "init", "-b", "main"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
2728
|
+
subprocess.run(["git", "config", "user.email", "test@example.com"], cwd=tmp_path, check=True)
|
|
2729
|
+
subprocess.run(["git", "config", "user.name", "Test"], cwd=tmp_path, check=True)
|
|
2730
|
+
(tmp_path / "base.txt").write_text("base", encoding="utf-8")
|
|
2731
|
+
subprocess.run(["git", "add", "base.txt"], cwd=tmp_path, check=True)
|
|
2732
|
+
subprocess.run(["git", "commit", "-m", "base"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
2733
|
+
base_head = subprocess.run(["git", "rev-parse", "HEAD"], cwd=tmp_path, check=True, stdout=subprocess.PIPE, text=True).stdout.strip()
|
|
2734
|
+
artifact_dir = tmp_path / ".prizmkit" / "specs" / "001-corrupt"
|
|
2735
|
+
artifact_dir.mkdir(parents=True)
|
|
2736
|
+
(artifact_dir / "completion-summary.json").write_bytes(b"\xff\xfe")
|
|
2737
|
+
log = tmp_path / "session.log"
|
|
2738
|
+
log.write_text("quota exceeded", encoding="utf-8")
|
|
2739
|
+
result = AISessionResult(
|
|
2740
|
+
command=AISessionCommand(("fake",), None, "codebuddy", "stdin"),
|
|
2741
|
+
exit_code=1,
|
|
2742
|
+
timed_out=False,
|
|
2743
|
+
termination_reason=None,
|
|
2744
|
+
stale_marker=None,
|
|
2745
|
+
fatal_error_code="api_error",
|
|
2746
|
+
progress_summary=ProgressSummary(terminal_result_text="quota exceeded"),
|
|
2747
|
+
session_log=log,
|
|
2748
|
+
backup_log=tmp_path / "backup.log",
|
|
2749
|
+
log_recovery=None,
|
|
2750
|
+
started_epoch=0,
|
|
2751
|
+
ended_epoch=1,
|
|
2752
|
+
)
|
|
2753
|
+
|
|
2754
|
+
classification = classify_session(
|
|
2755
|
+
result,
|
|
2756
|
+
project_root=tmp_path,
|
|
2757
|
+
base_head=base_head,
|
|
2758
|
+
prompt=PromptGenerationResult(output_path=tmp_path / "prompt.md", artifact_path=artifact_dir),
|
|
2759
|
+
)
|
|
2760
|
+
|
|
2761
|
+
assert classification.session_status == "infra_error"
|
|
2762
|
+
|
|
2763
|
+
|
|
2764
|
+
@pytest.mark.parametrize(
|
|
2765
|
+
("timed_out", "termination_reason", "expected_status", "expected_reason"),
|
|
2766
|
+
[
|
|
2767
|
+
(True, "timeout", "timed_out", "timeout"),
|
|
2768
|
+
(False, "stale_session", "crashed", "stale_session"),
|
|
2769
|
+
],
|
|
2770
|
+
)
|
|
2771
|
+
def test_timeout_and_stale_termination_override_provider_error_markers(tmp_path, timed_out, termination_reason, expected_status, expected_reason):
|
|
2772
|
+
from prizmkit_runtime.runner_classification import classify_session
|
|
2773
|
+
from prizmkit_runtime.sessions import AISessionCommand, AISessionResult
|
|
2774
|
+
from prizmkit_runtime.status import ProgressSummary
|
|
2775
|
+
|
|
2776
|
+
subprocess.run(["git", "init", "-b", "main"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
2777
|
+
subprocess.run(["git", "config", "user.email", "test@example.com"], cwd=tmp_path, check=True)
|
|
2778
|
+
subprocess.run(["git", "config", "user.name", "Test"], cwd=tmp_path, check=True)
|
|
2779
|
+
(tmp_path / "base.txt").write_text("base", encoding="utf-8")
|
|
2780
|
+
subprocess.run(["git", "add", "base.txt"], cwd=tmp_path, check=True)
|
|
2781
|
+
subprocess.run(["git", "commit", "-m", "base"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
2782
|
+
base_head = subprocess.run(["git", "rev-parse", "HEAD"], cwd=tmp_path, check=True, stdout=subprocess.PIPE, text=True).stdout.strip()
|
|
2783
|
+
log = tmp_path / "session.log"
|
|
2784
|
+
log.write_text("gateway API error", encoding="utf-8")
|
|
2785
|
+
result = AISessionResult(
|
|
2786
|
+
command=AISessionCommand(("fake",), None, "codebuddy", "stdin"),
|
|
2787
|
+
exit_code=1,
|
|
2788
|
+
timed_out=timed_out,
|
|
2789
|
+
termination_reason=termination_reason,
|
|
2790
|
+
stale_marker=None,
|
|
2791
|
+
fatal_error_code="api_error",
|
|
2792
|
+
progress_summary=ProgressSummary(terminal_result_text="provider gateway error"),
|
|
2793
|
+
session_log=log,
|
|
2794
|
+
backup_log=tmp_path / "backup.log",
|
|
2795
|
+
log_recovery=None,
|
|
2796
|
+
started_epoch=0,
|
|
2797
|
+
ended_epoch=1,
|
|
2798
|
+
)
|
|
2799
|
+
|
|
2800
|
+
classification = classify_session(result, project_root=tmp_path, base_head=base_head)
|
|
2801
|
+
|
|
2802
|
+
assert classification.session_status == expected_status
|
|
2803
|
+
assert classification.reason == expected_reason
|
|
@@ -802,7 +802,7 @@ def test_session_preamble_writes_redacted_start_command_for_all_launch_profiles(
|
|
|
802
802
|
assert str(prompt) not in "\n".join(lines[:6])
|
|
803
803
|
|
|
804
804
|
|
|
805
|
-
def
|
|
805
|
+
def test_headless_review_guidance_uses_workspace_equivalence_protocol():
|
|
806
806
|
guidance_files = [
|
|
807
807
|
REPO_ROOT / "dev-pipeline" / "templates" / "sections" / "phase-review-full.md",
|
|
808
808
|
REPO_ROOT / "dev-pipeline" / "templates" / "bootstrap-tier3.md",
|
|
@@ -811,15 +811,19 @@ def test_headless_subagent_prompt_guidance_keeps_no_worktree_boundary_visible():
|
|
|
811
811
|
|
|
812
812
|
for path in guidance_files:
|
|
813
813
|
text = path.read_text(encoding="utf-8")
|
|
814
|
-
assert
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
assert "
|
|
820
|
-
assert "
|
|
821
|
-
assert "
|
|
822
|
-
|
|
814
|
+
assert "platform-supported" in text, path
|
|
815
|
+
assert "snapshot" in text and "equivalent" in text, path
|
|
816
|
+
assert "Main-Agent self-review" in text, path
|
|
817
|
+
assert "current-workspace / active-checkout / no-worktree" not in text, path
|
|
818
|
+
assert ".claude/worktrees/..." not in text, path
|
|
819
|
+
assert ".prizmkit/state/worktrees/..." not in text, path
|
|
820
|
+
assert "USE_WORKTREE" not in text, path
|
|
821
|
+
assert "isolation:" not in text, path
|
|
822
|
+
|
|
823
|
+
skill_text = guidance_files[-1].read_text(encoding="utf-8")
|
|
824
|
+
assert "reviewer-workspace-protocol.md" in skill_text
|
|
825
|
+
assert "staged, unstaged, untracked, deleted, and renamed" in skill_text
|
|
826
|
+
assert "WORKSPACE_MISMATCH" in skill_text
|
|
823
827
|
|
|
824
828
|
for path in guidance_files[:-1]:
|
|
825
829
|
text = path.read_text(encoding="utf-8")
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.1.
|
|
2
|
+
"version": "1.1.121",
|
|
3
3
|
"skills": {
|
|
4
4
|
"prizmkit": {
|
|
5
5
|
"description": "Full-lifecycle dev toolkit. Covers spec-driven development, Prizm context docs, code quality, debugging, deployment, and knowledge management.",
|
|
@@ -65,11 +65,11 @@
|
|
|
65
65
|
"hasScripts": false
|
|
66
66
|
},
|
|
67
67
|
"prizmkit-test": {
|
|
68
|
-
"description": "
|
|
68
|
+
"description": "Auditable project-native test evidence with request-driven execution, runner-generated receipts, differential proof, replay, and deterministic validation.",
|
|
69
69
|
"tier": "1",
|
|
70
70
|
"category": "prizmkit-skill",
|
|
71
|
-
"hasAssets":
|
|
72
|
-
"hasScripts":
|
|
71
|
+
"hasAssets": true,
|
|
72
|
+
"hasScripts": true
|
|
73
73
|
},
|
|
74
74
|
"feature-workflow": {
|
|
75
75
|
"description": "One-stop entry point for feature development. Orchestrates feature-planner → feature-pipeline-launcher → background execution. Handles multi-feature batch development from a single request.",
|
|
@@ -1,56 +1,53 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: "prizmkit-code-review"
|
|
3
|
-
description: "Iterative review-fix loop against spec
|
|
3
|
+
description: "Iterative independent review-fix loop against spec, plan, workspace changes, and test evidence. Establishes a verified review snapshot using any platform-supported subagent strategy while keeping review judgment separate from Main-Agent filtering and fixes. Use after /prizmkit-implement as a Full path quality gate or when the user asks to review, check code, validate implementation, or decide whether changes are ready to commit. Loops until PASS or max 3 rounds. (project)"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# PrizmKit Code Review
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Run an iterative review-fix loop against the current change. Keep review judgment independent from fix application: an independent read-only Reviewer evaluates a verified snapshot, then the Main Agent filters findings and applies accepted fixes.
|
|
9
9
|
|
|
10
10
|
Review modes:
|
|
11
|
-
- `
|
|
11
|
+
- `independent-reviewer-verified-snapshot`: an independent Reviewer verified that its review snapshot is equivalent to the Main Agent's current change.
|
|
12
12
|
- `not-run-no-changes`: no workspace changes were detected.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
The skill defines review invariants rather than platform tool parameters. Choose any platform-supported subagent launch and snapshot transport strategy that satisfies `${SKILL_DIR}/references/reviewer-workspace-protocol.md`.
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
Cannot run /prizmkit-code-review because this platform cannot start an inline reviewer subagent in the active checkout without worktree, copy, remote, or branch isolation.
|
|
18
|
-
```
|
|
16
|
+
If no independent Reviewer can receive and verify an equivalent snapshot, stop normal review and report the workspace verification failure. Do not replace the independent review with Main-Agent self-review because that would remove the separation between judgment and implementation.
|
|
19
17
|
|
|
20
18
|
### When to Use
|
|
21
19
|
- After `/prizmkit-implement` as the Full path quality gate
|
|
22
|
-
- For Fast path
|
|
23
|
-
-
|
|
20
|
+
- For Fast path when risk, scope, or the user warrants review
|
|
21
|
+
- When the user says "review", "check code", "review my implementation", or "is it ready to commit"
|
|
24
22
|
- Before `/prizmkit-committer` when the chosen lifecycle path requires review
|
|
25
23
|
|
|
26
24
|
### When NOT to Use
|
|
27
25
|
- Direct edit or low-risk Fast path where review is not required
|
|
28
|
-
- No spec.md or plan.md exists and there is no clear task goal to review against
|
|
26
|
+
- No `spec.md` or `plan.md` exists and there is no clear task goal to review against
|
|
29
27
|
- Trivial typo, formatting, or config-only changes that the user wants committed directly
|
|
30
28
|
|
|
31
29
|
## Input
|
|
32
30
|
|
|
33
31
|
| Parameter | Required | Description |
|
|
34
32
|
|-----------|----------|-------------|
|
|
35
|
-
| `artifact_dir` | No | Directory containing `spec.md`
|
|
33
|
+
| `artifact_dir` | No | Directory containing `spec.md` and `plan.md`. If omitted, scan `.prizmkit/` subdirectories for the most recently modified directory with a completed `plan.md`. When invoked as a handoff step, reuse the caller's `artifact_dir` rather than re-detecting. |
|
|
36
34
|
|
|
37
|
-
## Phase 0: Context
|
|
35
|
+
## Phase 0: Context and Snapshot
|
|
38
36
|
|
|
39
37
|
1. Read `spec.md` from the artifact directory and extract goals plus acceptance criteria.
|
|
40
38
|
2. Read `plan.md` from the artifact directory and extract architecture decisions plus completed tasks.
|
|
41
|
-
3. Read scoped `/prizmkit-test` report when present:
|
|
39
|
+
3. Read a scoped `/prizmkit-test` report when present:
|
|
42
40
|
- If `{artifact_dir}/test-report-path.txt` exists, read the pointed report.
|
|
43
41
|
- Extract Scope, Generated / Updated Tests, In-Scope Failures, Baseline Failures, Out of Scope Gaps, Boundary Matrix / Boundary Completion Gate, and Verdict when present.
|
|
44
|
-
- If no
|
|
42
|
+
- If no report exists, pass `No scoped /prizmkit-test report found` as informational context. Treat missing tests as a finding only when the lifecycle path required testing before review.
|
|
45
43
|
4. Read dev rules when configured: load `.prizmkit/prizm-docs/root.prizm`, then referenced `.prizmkit/rules/<layer>-rules.md` files if any exist.
|
|
46
|
-
5. Capture
|
|
47
|
-
-
|
|
48
|
-
- `git diff`
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
-
|
|
52
|
-
|
|
53
|
-
6. If no changes are detected, skip Phase 1 and write `review-report.md` with verdict PASS, rounds 0, and review mode `not-run-no-changes`.
|
|
44
|
+
5. Capture the current workspace manifest and review payload according to `${SKILL_DIR}/references/reviewer-workspace-protocol.md`. Include:
|
|
45
|
+
- baseline repository and revision identity
|
|
46
|
+
- `git status`, `git diff`, and `git diff --cached` evidence
|
|
47
|
+
- staged, unstaged, untracked, deleted, and renamed path inventory
|
|
48
|
+
- changed content and stable content identities sufficient to detect missing, stale, or mixed snapshots
|
|
49
|
+
- relevant root Prizm rules and plan decisions
|
|
50
|
+
6. If no changes are detected, skip Phase 1 and write `review-report.md` with verdict PASS, rounds 0, review mode `not-run-no-changes`, and snapshot verification `NOT_APPLICABLE`.
|
|
54
51
|
|
|
55
52
|
## Phase 1: Review-Fix Loop
|
|
56
53
|
|
|
@@ -70,42 +67,43 @@ python3 --version 2>/dev/null && echo "SCRIPT_MODE" || echo "TEXT_MODE"
|
|
|
70
67
|
- `round = 1`
|
|
71
68
|
- `max_rounds = 3`
|
|
72
69
|
- `findings_history = []`
|
|
73
|
-
- `review_mode =
|
|
70
|
+
- `review_mode = independent-reviewer-verified-snapshot` after snapshot verification succeeds
|
|
74
71
|
|
|
75
|
-
### Step 1:
|
|
72
|
+
### Step 1: Establish Independent Review
|
|
76
73
|
|
|
77
|
-
|
|
74
|
+
Use the skill-owned review contract:
|
|
78
75
|
|
|
79
76
|
```yaml
|
|
80
77
|
prompt_reference: ${SKILL_DIR}/references/reviewer-agent-prompt.md
|
|
81
|
-
|
|
82
|
-
|
|
78
|
+
workspace_protocol: ${SKILL_DIR}/references/reviewer-workspace-protocol.md
|
|
79
|
+
reviewer_contract: independent, read-only, workspace-equivalent
|
|
83
80
|
```
|
|
84
81
|
|
|
85
|
-
1. Read
|
|
86
|
-
2.
|
|
87
|
-
3.
|
|
88
|
-
4.
|
|
89
|
-
5.
|
|
82
|
+
1. Read both references and fill the Reviewer prompt with Phase 0 context plus the workspace manifest and payload.
|
|
83
|
+
2. Choose a subagent launch and snapshot transport strategy supported by the current platform. Strategy choice belongs to the Main Agent because platforms expose different capabilities; do not treat a particular tool parameter, checkout layout, branch name, or transport as part of the skill contract.
|
|
84
|
+
3. Require the Reviewer to verify snapshot equivalence before reviewing. Different physical paths or execution environments are acceptable when the verified review view is content-equivalent to the captured Main-Agent workspace.
|
|
85
|
+
4. If the Reviewer returns `WORKSPACE_MISMATCH`, repair or replace the strategy only when equivalence can be established safely. Otherwise write a `NEEDS_FIXES` report with the verification failure unresolved and stop; do not perform Main-Agent self-review.
|
|
86
|
+
5. After the Main Agent applies fixes, capture and verify a fresh snapshot for the next round because the previous snapshot is stale by definition.
|
|
90
87
|
|
|
91
88
|
### Step 2: Run Review
|
|
92
89
|
|
|
93
90
|
Round context:
|
|
94
91
|
- Round 1: examine all current changes comprehensively.
|
|
95
|
-
- Round 2+:
|
|
92
|
+
- Round 2+: verify prior fixes, look for regressions introduced by the fixes, and identify accepted findings that remain.
|
|
96
93
|
|
|
97
94
|
Review result:
|
|
98
95
|
- `PASS`: no findings.
|
|
99
96
|
- `NEEDS_FIXES`: one or more findings with severity, dimension, location, problem, and suggested fix.
|
|
97
|
+
- `WORKSPACE_MISMATCH`: snapshot equivalence was not established; this is a review transport failure, not a code verdict.
|
|
100
98
|
|
|
101
99
|
### Step 3: Loop Exit Gate
|
|
102
100
|
|
|
103
|
-
Call the gate only
|
|
101
|
+
Call the gate only when:
|
|
104
102
|
|
|
105
103
|
1. Review returned `PASS`.
|
|
106
|
-
2. Main Agent has filtered `NEEDS_FIXES` findings.
|
|
104
|
+
2. The Main Agent has filtered `NEEDS_FIXES` findings.
|
|
107
105
|
|
|
108
|
-
Do not call the gate for `NEEDS_FIXES` before filtering because `accepted_count` is not known
|
|
106
|
+
Do not call the gate for `NEEDS_FIXES` before filtering because `accepted_count` is not known. Handle `WORKSPACE_MISMATCH` through Step 1 rather than passing it to the code-verdict gate.
|
|
109
107
|
|
|
110
108
|
Script mode invocation:
|
|
111
109
|
|
|
@@ -113,7 +111,7 @@ Script mode invocation:
|
|
|
113
111
|
echo '{"reviewer_result":"NEEDS_FIXES","accepted_count":2,"findings_count":3,"round":1,"findings_history":[],"max_rounds":3,"filtering_done":true}' | python3 ${SKILL_DIR}/scripts/check_loop.py
|
|
114
112
|
```
|
|
115
113
|
|
|
116
|
-
|
|
114
|
+
The gate output determines whether to end the loop. If `divergenceWarning` is true, warn the user that the loop may not be converging.
|
|
117
115
|
|
|
118
116
|
### Text Fallback Constraints
|
|
119
117
|
|
|
@@ -130,36 +128,39 @@ When Python is unavailable, apply these rules manually:
|
|
|
130
128
|
|
|
131
129
|
For each finding, decide whether it is reasonable:
|
|
132
130
|
|
|
133
|
-
- Is
|
|
134
|
-
- Is
|
|
135
|
-
- Would
|
|
136
|
-
- Does
|
|
131
|
+
- Is the finding relevant to the current changes?
|
|
132
|
+
- Is it a real problem rather than a subjective style preference?
|
|
133
|
+
- Would the fix improve the code without broad out-of-scope refactoring?
|
|
134
|
+
- Does the finding conflict with the spec, plan, dev rules, or current test evidence?
|
|
137
135
|
|
|
138
136
|
Output per finding:
|
|
139
137
|
- Accepted: queue for direct Main Agent fix work.
|
|
140
|
-
- Rejected: record a short reason such as out of scope, not a defect, or too risky for current task.
|
|
138
|
+
- Rejected: record a short reason such as out of scope, not a defect, or too risky for the current task.
|
|
141
139
|
|
|
142
140
|
After filtering, call the Loop Exit Gate. If `endLoop=true`, proceed to Phase 2.
|
|
143
141
|
|
|
144
142
|
### Step 5: Main Agent Applies Accepted Fixes
|
|
145
143
|
|
|
146
|
-
The Main Agent applies accepted findings directly in
|
|
144
|
+
The Main Agent applies accepted findings directly in its current workspace. Keeping fix ownership with the Main Agent prevents the read-only Reviewer from becoming a second, conflicting implementation authority.
|
|
147
145
|
|
|
148
|
-
If an accepted finding cannot be safely
|
|
146
|
+
If an accepted finding cannot be fixed safely within scope, record the reason and carry the finding into `review-report.md` as unresolved.
|
|
149
147
|
|
|
150
|
-
After fixes are complete, record results, increment the round, and return to Step
|
|
148
|
+
After fixes are complete, record results, increment the round, capture a fresh workspace snapshot, and return to Step 1.
|
|
151
149
|
|
|
152
150
|
## Phase 2: Output
|
|
153
151
|
|
|
154
|
-
|
|
152
|
+
Write `review-report.md` to the artifact directory, including no-change PASS and workspace-verification failure results. Read `${SKILL_DIR}/references/review-report-template.md` for the full output format.
|
|
155
153
|
|
|
156
154
|
Required report fields:
|
|
157
155
|
- Verdict: `PASS` or `NEEDS_FIXES`
|
|
158
|
-
- Review Mode: `
|
|
156
|
+
- Review Mode: `independent-reviewer-verified-snapshot` or `not-run-no-changes`
|
|
157
|
+
- Reviewer Strategy: concise strategy summary without relying on platform-specific parameter names
|
|
158
|
+
- Snapshot Verification: `VERIFIED`, `FAILED`, or `NOT_APPLICABLE`
|
|
159
|
+
- Snapshot Identity: manifest identity or failure reason
|
|
159
160
|
- Rounds and total findings
|
|
160
161
|
- Fixed, rejected, and unresolved findings
|
|
161
162
|
- Final summary
|
|
162
163
|
|
|
163
164
|
Completion summary:
|
|
164
165
|
- `PASS`: hand off to `/prizmkit-retrospective` when docs or durable knowledge changed, otherwise `/prizmkit-committer`.
|
|
165
|
-
- `NEEDS_FIXES`: inform the caller of
|
|
166
|
+
- `NEEDS_FIXES`: inform the caller of unresolved code findings or snapshot-verification blockers.
|