prizmkit 1.1.127 → 1.1.129
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 +2 -75
- package/bundled/dev-pipeline/assets/skill-subagent-integration.md +14 -11
- package/bundled/dev-pipeline/prizmkit_runtime/gitops.py +88 -58
- package/bundled/dev-pipeline/prizmkit_runtime/reset.py +4 -3
- package/bundled/dev-pipeline/prizmkit_runtime/runner_bookkeeping.py +53 -1
- package/bundled/dev-pipeline/prizmkit_runtime/runners.py +33 -17
- package/bundled/dev-pipeline/prizmkit_runtime/runtime_helper.py +0 -1
- package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +28 -26
- package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +1 -1
- package/bundled/dev-pipeline/scripts/generate-recovery-prompt.py +4 -3
- package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +1 -1
- package/bundled/dev-pipeline/scripts/parse-stream-progress.py +3 -3
- package/bundled/dev-pipeline/templates/bootstrap-prompt.md +2 -2
- package/bundled/dev-pipeline/templates/bootstrap-tier3.md +22 -21
- package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +4 -4
- package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +5 -5
- package/bundled/dev-pipeline/templates/sections/bugfix-mission.md +1 -1
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-review.md +11 -4
- package/bundled/dev-pipeline/templates/sections/context-budget-rules.md +1 -1
- package/bundled/dev-pipeline/templates/sections/directory-convention-agent.md +3 -2
- package/bundled/dev-pipeline/templates/sections/directory-convention-full.md +3 -2
- package/bundled/dev-pipeline/templates/sections/phase-commit-full.md +1 -1
- package/bundled/dev-pipeline/templates/sections/phase-commit.md +1 -1
- package/bundled/dev-pipeline/templates/sections/phase-implement-agent.md +1 -1
- package/bundled/dev-pipeline/templates/sections/phase-implement-full.md +1 -1
- package/bundled/dev-pipeline/templates/sections/phase-review-agent.md +12 -5
- package/bundled/dev-pipeline/templates/sections/phase-review-full.md +12 -5
- package/bundled/dev-pipeline/templates/sections/phase-specify-plan-full.md +1 -1
- package/bundled/dev-pipeline/templates/sections/phase0-init.md +1 -1
- package/bundled/dev-pipeline/templates/sections/refactor-mission.md +1 -1
- package/bundled/dev-pipeline/templates/sections/refactor-phase-review.md +13 -6
- package/bundled/dev-pipeline/templates/sections/refactor-reminders.md +1 -1
- package/bundled/dev-pipeline/templates/sections/subagent-timeout-recovery.md +6 -4
- package/bundled/dev-pipeline/templates/sections/test-failure-recovery-agent.md +1 -1
- package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +106 -7
- package/bundled/dev-pipeline/tests/test_python_runner_parity.py +139 -11
- package/bundled/dev-pipeline/tests/test_runtime_helper.py +9 -3
- package/bundled/dev-pipeline/tests/test_unified_cli.py +225 -24
- package/bundled/skills/_metadata.json +3 -3
- package/bundled/skills/feature-planner/SKILL.md +2 -2
- package/bundled/skills/prizmkit/SKILL.md +1 -1
- package/bundled/skills/prizmkit-code-review/SKILL.md +63 -122
- package/bundled/skills/prizmkit-code-review/references/review-report-template.md +9 -36
- package/bundled/skills/prizmkit-code-review/scripts/render_review_report.py +21 -111
- package/package.json +1 -1
- package/src/manifest.js +8 -3
- package/src/scaffold.js +5 -16
- package/bundled/skills/prizmkit-code-review/assets/reviewer-role.json +0 -19
- package/bundled/skills/prizmkit-code-review/references/reviewer-agent-prompt.md +0 -120
|
@@ -71,7 +71,7 @@ Before proceeding past CP-1, verify:
|
|
|
71
71
|
|
|
72
72
|
### Plan Self-Check — Orchestrator Direct
|
|
73
73
|
|
|
74
|
-
Do not
|
|
74
|
+
Do not delegate planning analysis. The current Main Agent performs a bounded self-check before implementation:
|
|
75
75
|
|
|
76
76
|
1. Re-read only `context-snapshot.md`, `spec.md`, and `plan.md`.
|
|
77
77
|
2. Cross-check spec.md and plan.md Tasks against the feature description and Verification Gates in the Task Contract.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
### Phase 0: Project Bootstrap
|
|
2
2
|
- Run `/prizmkit-init` (invoke the prizmkit-init skill)
|
|
3
|
-
- Run `{{PYTHON_CMD}}
|
|
3
|
+
- Run `{{PYTHON_CMD}} .prizmkit/dev-pipeline/scripts/init-change-artifact.py --project-root {{PROJECT_ROOT}} --feature-id {{FEATURE_ID}} --feature-slug {{FEATURE_SLUG}}`
|
|
4
4
|
- **CP-0**: Verify `.prizmkit/prizm-docs/root.prizm`, `.prizmkit/config.json` exist
|
|
5
5
|
|
|
6
6
|
|
|
@@ -4,6 +4,6 @@ You are the **refactor session orchestrator**. Execute Refactor {{REFACTOR_ID}}:
|
|
|
4
4
|
|
|
5
5
|
**CRITICAL**: Preserve observable behavior. Do not change product behavior unless the refactor contract explicitly requires it.
|
|
6
6
|
|
|
7
|
-
**Execution model**: You implement directly in the current checkout. Do not spawn a top-level Dev subagent.
|
|
7
|
+
**Execution model**: You implement directly in the current checkout. Do not spawn a top-level Dev subagent. The current Main Agent runs `/prizmkit-code-review` directly and owns its complete review, repair, verification, and reporting loop without direct or indirect review delegation.
|
|
8
8
|
|
|
9
9
|
**NON-INTERACTIVE MODE**: There is NO human on the other end. Resolve uncertainty from existing code and conservative project patterns.
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
### Phase 3: Review — Code Review & Behavior Verification
|
|
2
2
|
|
|
3
|
-
Run `/prizmkit-code-review` directly with `artifact_dir=.prizmkit/refactor/{{REFACTOR_ID}}/`. The skill
|
|
3
|
+
Run `/prizmkit-code-review` directly with `artifact_dir=.prizmkit/refactor/{{REFACTOR_ID}}/`. The skill runs a Main-Agent review loop for up to ten completed rounds. The current Main Agent is the only Code Review executor: it reviews the complete current change, rejects unsupported candidate findings, directly repairs accepted findings, verifies repairs, and repeats until `accepted = 0` with no unresolved findings.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
The Code Review execution boundary is absolute for this phase:
|
|
6
|
+
|
|
7
|
+
- Do not delegate any review work directly or indirectly.
|
|
8
|
+
- Do not invoke another review skill or review workflow.
|
|
9
|
+
- Do not use a general-purpose execution unit for review or relabel delegated work as a finder, verifier, audit, compatibility review, verification, or gap sweep.
|
|
10
|
+
- Direct reading, searching, editing, and testing by the current Main Agent remain allowed.
|
|
11
|
+
- `review-report.md` is the only persisted review artifact; do not append separate Review Notes to another artifact.
|
|
12
|
+
|
|
13
|
+
Missing required evidence, an unsafe repair, a failed verification, or failure to converge by round ten produces `NEEDS_FIXES`.
|
|
6
14
|
|
|
7
15
|
**Gate Check — Final Review Result**:
|
|
8
16
|
|
|
@@ -12,9 +20,8 @@ Do not spawn a top-level Reviewer subagent. The code-review skill owns its uncha
|
|
|
12
20
|
|
|
13
21
|
- `REVIEW_PASS` → proceed.
|
|
14
22
|
- `REVIEW_NEEDS_FIXES` → log unresolved findings and proceed only when they do not block behavior preservation under explicit task policy.
|
|
15
|
-
- `
|
|
16
|
-
- `REVIEW_INCOMPLETE` or `REVIEW_INVALID` → inspect progress once, write `failure-log.md`, and stop. Do not accept stale verdict text or replace required independent review with Main-Agent self-review.
|
|
23
|
+
- `REVIEW_INCOMPLETE` or `REVIEW_INVALID` → inspect progress once, write `failure-log.md`, and stop. Do not accept stale verdict text or synthesize a passing result outside the skill.
|
|
17
24
|
|
|
18
|
-
Verify the review
|
|
25
|
+
Verify directly that the review checks behavior preservation, public API compatibility, imports, tests, and rollback assumptions.
|
|
19
26
|
|
|
20
|
-
**Checkpoint update**: Set step `prizmkit-code-review` to `completed` only for an accepted valid Final Result. Do not complete the checkpoint for
|
|
27
|
+
**Checkpoint update**: Set step `prizmkit-code-review` to `completed` only for an accepted valid Final Result. Do not complete the checkpoint for incomplete or invalid reports.
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
- Use L1 skills: `/prizmkit-plan`, `/prizmkit-implement`, `/prizmkit-code-review`, `/prizmkit-retrospective`, `/prizmkit-committer`.
|
|
4
4
|
- All refactor skills use `artifact_dir=.prizmkit/refactor/{{REFACTOR_ID}}/`.
|
|
5
5
|
- Behavior preservation is the first priority.
|
|
6
|
-
- Do not spawn top-level Dev
|
|
6
|
+
- Do not spawn a top-level Dev implementation subagent; Code Review runs only in the current Main Agent without delegation.
|
|
7
7
|
- Main orchestrator runs `/prizmkit-code-review` directly.
|
|
8
8
|
- Commit with `refactor(<scope>):`, not `feat:` or `fix:`.
|
|
9
9
|
- Checkpoint state is the recovery truth; skipped steps must be marked `skipped`.
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
##
|
|
1
|
+
## Normal Work Delegation Timeout Recovery
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This recovery path applies only to normal work delegated outside Code Review. Code Review never uses delegated execution or this retry procedure.
|
|
4
|
+
|
|
5
|
+
If a normal work execution unit times out:
|
|
4
6
|
1. `{{RUNTIME_HELPER_CMD}} artifact list .prizmkit/specs/{{FEATURE_SLUG}}` — check what exists
|
|
5
|
-
2. If `context-snapshot.md` exists: open recovery prompt with `"Read .prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md for project context and
|
|
6
|
-
3. Max 2 retries per phase. After 2 failures,
|
|
7
|
+
2. If `context-snapshot.md` exists: open a recovery prompt with `"Read .prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md for project context and the Implementation Log. Run git diff HEAD to see actual code changes already made. Do NOT re-read individual source files unless the File Manifest directs you to."` + only remaining steps + `model: "lite"`
|
|
8
|
+
3. Max 2 retries per phase. After 2 failures, the Main Agent completes the work directly and appends a Recovery Note to context-snapshot.md.
|
|
@@ -6,7 +6,7 @@ Use this protocol whenever the post-review PrizmKit Test gate fails. Its purpose
|
|
|
6
6
|
|
|
7
7
|
| Class | Meaning | Required Action | May Continue? |
|
|
8
8
|
|-------|---------|-----------------|---------------|
|
|
9
|
-
| Baseline failure | Existed before this session | Document in Implementation Log or
|
|
9
|
+
| Baseline failure | Existed before this session | Document in the Implementation Log or `failure-log.md` | Yes |
|
|
10
10
|
| New regression | Introduced by this session | Fix before reporting success | No |
|
|
11
11
|
| Brittle test | Test expectation/setup is wrong for the intended behavior | Fix the test or environment setup, then rerun | Only after fixed or documented as blocked |
|
|
12
12
|
| Environment/tooling failure | External tool, network, install, or local environment prevents verification | Document in `failure-log.md` with impact on gates | Only if no Verification Gate is blocked |
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Tests for generate-bootstrap-prompt.py core functions."""
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
+
import importlib.util
|
|
4
5
|
import os
|
|
5
6
|
import re
|
|
6
7
|
import time
|
|
@@ -25,6 +26,7 @@ from generate_bootstrap_prompt import (
|
|
|
25
26
|
generate_checkpoint_definition,
|
|
26
27
|
merge_checkpoint_state,
|
|
27
28
|
load_active_agent_prompts,
|
|
29
|
+
validate_rendered,
|
|
28
30
|
main as generate_bootstrap_main,
|
|
29
31
|
)
|
|
30
32
|
from continuation import append_continuation_handoff, checkpoint_cursor
|
|
@@ -44,6 +46,69 @@ FORBIDDEN_TEST_CMD_PLACEHOLDER = "(" + "$TEST_CMD" + ")"
|
|
|
44
46
|
FORBIDDEN_THREE_STRIKE = "3-" + "strike"
|
|
45
47
|
FORBIDDEN_TWENTY_STEP = "20-" + "step window"
|
|
46
48
|
|
|
49
|
+
REVIEW_PROMPT_REQUIRED = (
|
|
50
|
+
"The current Main Agent is the only Code Review executor",
|
|
51
|
+
"Do not invoke another review skill or review workflow",
|
|
52
|
+
"finder, verifier, audit, compatibility review, verification, or gap sweep",
|
|
53
|
+
"`review-report.md` is the only persisted review artifact",
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
REVIEW_PROMPT_FORBIDDEN = (
|
|
57
|
+
"Orchestrator + Reviewer",
|
|
58
|
+
"Reviewer reviews",
|
|
59
|
+
"Reviewer subagents",
|
|
60
|
+
"Reviewer appends Review Notes",
|
|
61
|
+
"Implementation Log/Review Notes",
|
|
62
|
+
"internal Reviewer Agent",
|
|
63
|
+
"spawning Reviewer",
|
|
64
|
+
"delayed teammate/reviewer findings",
|
|
65
|
+
"Gate checks enforce Implementation Log and Review Notes",
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def assert_unambiguous_main_agent_review(prompt):
|
|
70
|
+
for required in REVIEW_PROMPT_REQUIRED:
|
|
71
|
+
assert required in prompt
|
|
72
|
+
for forbidden in REVIEW_PROMPT_FORBIDDEN:
|
|
73
|
+
assert forbidden not in prompt
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
PROMPT_RENDERING_FORBIDDEN = (
|
|
77
|
+
"If implementation is delegated to a Dev subagent",
|
|
78
|
+
"Document in Implementation Log or review notes",
|
|
79
|
+
"{{INIT_SCRIPT_PATH}}",
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def assert_complete_prompt_rendering(prompt):
|
|
84
|
+
assert_unambiguous_main_agent_review(prompt)
|
|
85
|
+
for forbidden in PROMPT_RENDERING_FORBIDDEN:
|
|
86
|
+
assert forbidden not in prompt
|
|
87
|
+
assert re.findall(r"\{\{[A-Z][A-Z_0-9]+\}\}", prompt) == []
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _load_recovery_prompt_module():
|
|
91
|
+
path = Path("dev-pipeline/scripts/generate-recovery-prompt.py")
|
|
92
|
+
spec = importlib.util.spec_from_file_location("generate_recovery_prompt", path)
|
|
93
|
+
module = importlib.util.module_from_spec(spec)
|
|
94
|
+
spec.loader.exec_module(module)
|
|
95
|
+
return module
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_rendered_prompt_validation_rejects_unresolved_control_placeholders():
|
|
99
|
+
content = (
|
|
100
|
+
"## Your Mission\n"
|
|
101
|
+
"## Execution\n"
|
|
102
|
+
"Run {{INIT_SCRIPT_PATH}}\n"
|
|
103
|
+
"## Failure Capture\n"
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
is_valid, warnings, errors = validate_rendered(content)
|
|
107
|
+
|
|
108
|
+
assert is_valid is False
|
|
109
|
+
assert warnings == []
|
|
110
|
+
assert errors == ["Unreplaced placeholders: {{INIT_SCRIPT_PATH}}"]
|
|
111
|
+
|
|
47
112
|
|
|
48
113
|
def scoped_report_text(verdict="PASS", boundary_missing=0,
|
|
49
114
|
happy_path_only=0, completion_gate="yes",
|
|
@@ -882,9 +947,39 @@ class TestDirectOrchestratorImplementationPrompts:
|
|
|
882
947
|
assert FORBIDDEN_TEST_CMD_PLACEHOLDER not in rendered
|
|
883
948
|
assert FORBIDDEN_THREE_STRIKE not in rendered
|
|
884
949
|
assert FORBIDDEN_TWENTY_STEP not in rendered
|
|
885
|
-
assert "Main-Agent
|
|
886
|
-
assert "
|
|
887
|
-
assert "
|
|
950
|
+
assert "Main-Agent review loop" in rendered
|
|
951
|
+
assert "up to ten completed rounds" in rendered
|
|
952
|
+
assert "The current Main Agent is the only Code Review executor" in rendered
|
|
953
|
+
assert "Do not invoke another review skill or review workflow" in rendered
|
|
954
|
+
assert "low=0" not in rendered
|
|
955
|
+
assert "Reviewer 3" not in rendered
|
|
956
|
+
|
|
957
|
+
def test_all_rendered_pipeline_prompts_have_one_main_agent_review_model(self, tmp_path):
|
|
958
|
+
recovery = _load_recovery_prompt_module()
|
|
959
|
+
prompts = [
|
|
960
|
+
_render_feature_prompt(tmp_path / "feature-lite", mode="lite"),
|
|
961
|
+
_render_feature_prompt(tmp_path / "feature-standard", mode="standard"),
|
|
962
|
+
_render_feature_prompt(tmp_path / "feature-full", mode="full"),
|
|
963
|
+
_render_feature_prompt(
|
|
964
|
+
tmp_path / "feature-tier3",
|
|
965
|
+
mode="full",
|
|
966
|
+
template="bootstrap-tier3.md",
|
|
967
|
+
),
|
|
968
|
+
_render_bugfix_prompt(tmp_path / "bugfix"),
|
|
969
|
+
_render_refactor_prompt(tmp_path / "refactor"),
|
|
970
|
+
recovery.build_bugfix_prompt(
|
|
971
|
+
{
|
|
972
|
+
"workflow_type": "bug-fix-workflow",
|
|
973
|
+
"phase": 5,
|
|
974
|
+
"phase_name": "Review",
|
|
975
|
+
"workflow_data": {"bug_id": "B-001"},
|
|
976
|
+
},
|
|
977
|
+
str(tmp_path),
|
|
978
|
+
),
|
|
979
|
+
]
|
|
980
|
+
|
|
981
|
+
for prompt in prompts:
|
|
982
|
+
assert_complete_prompt_rendering(prompt)
|
|
888
983
|
|
|
889
984
|
def test_retained_tier3_fallback_template_uses_direct_orchestrator_implementation(self):
|
|
890
985
|
rendered = Path("dev-pipeline/templates/bootstrap-tier3.md").read_text(encoding="utf-8")
|
|
@@ -1443,8 +1538,10 @@ class TestHeadlessPromptCleanupF033:
|
|
|
1443
1538
|
refactor_prompt = _render_refactor_prompt(tmp_path / "refactor-mixed", platform="claude")
|
|
1444
1539
|
|
|
1445
1540
|
assert "{{" + "REVIEWER_" + "SUBAGENT_PATH" + "}}" not in replacements
|
|
1446
|
-
assert "Main-Agent
|
|
1447
|
-
assert "
|
|
1541
|
+
assert "Main-Agent review loop" in refactor_prompt
|
|
1542
|
+
assert "The current Main Agent is the only Code Review executor" in refactor_prompt
|
|
1543
|
+
assert "Do not invoke another review skill or review workflow" in refactor_prompt
|
|
1544
|
+
assert "Reviewer 3" not in refactor_prompt
|
|
1448
1545
|
|
|
1449
1546
|
def test_bugfix_refactor_explicit_codex_override(self, tmp_path, monkeypatch):
|
|
1450
1547
|
from generate_bugfix_prompt import build_replacements as bugfix_build_replacements
|
|
@@ -1465,8 +1562,10 @@ class TestHeadlessPromptCleanupF033:
|
|
|
1465
1562
|
refactor_prompt = _render_refactor_prompt(tmp_path / "refactor-codex", platform="claude", explicit_platform="codex")
|
|
1466
1563
|
|
|
1467
1564
|
assert "{{" + "REVIEWER_" + "SUBAGENT_PATH" + "}}" not in replacements
|
|
1468
|
-
assert "Main-Agent
|
|
1469
|
-
assert "
|
|
1565
|
+
assert "Main-Agent review loop" in refactor_prompt
|
|
1566
|
+
assert "The current Main Agent is the only Code Review executor" in refactor_prompt
|
|
1567
|
+
assert "Do not invoke another review skill or review workflow" in refactor_prompt
|
|
1568
|
+
assert "Reviewer 3" not in refactor_prompt
|
|
1470
1569
|
|
|
1471
1570
|
|
|
1472
1571
|
class TestFeatureBootstrapShellExtraction:
|
|
@@ -1408,7 +1408,8 @@ def test_default_no_worktree_preserves_main_worktree_branch_launch(monkeypatch,
|
|
|
1408
1408
|
lifecycle_order.append("branch_create")
|
|
1409
1409
|
branch_events.append(("create", plan.commands[1].args[2]))
|
|
1410
1410
|
if plan.name == "branch_merge":
|
|
1411
|
-
|
|
1411
|
+
merge_command = next(command for command in plan.commands if command.args[:2] == ("merge", "--ff-only"))
|
|
1412
|
+
branch_events.append(("merge", merge_command.args[2]))
|
|
1412
1413
|
return SimpleNamespace(ok=True, plan=plan, results=())
|
|
1413
1414
|
|
|
1414
1415
|
def fake_start_item(*args, **kwargs):
|
|
@@ -1603,6 +1604,79 @@ def test_worktree_recovers_completed_pending_branch_from_base_session_history(mo
|
|
|
1603
1604
|
assert merge_calls and cleanup_calls[-1][1] is True
|
|
1604
1605
|
|
|
1605
1606
|
|
|
1607
|
+
def test_success_final_commit_failure_preserves_checkout_without_merge(monkeypatch, tmp_path):
|
|
1608
|
+
from prizmkit_runtime import runners
|
|
1609
|
+
from prizmkit_runtime.runner_models import family_for, parse_invocation
|
|
1610
|
+
from prizmkit_runtime.task_checkout import load_task_checkout
|
|
1611
|
+
|
|
1612
|
+
monkeypatch.setenv("DEV_BRANCH", "dev/F-001-finalize-fail")
|
|
1613
|
+
paths = _make_paths(tmp_path)
|
|
1614
|
+
family = family_for("feature", paths)
|
|
1615
|
+
invocation = parse_invocation(family, "run", ())
|
|
1616
|
+
invocation = invocation.__class__(**{**invocation.__dict__, "list_path": paths.feature_plan})
|
|
1617
|
+
_install_runner_session_fakes(monkeypatch, runners)
|
|
1618
|
+
merge_calls = []
|
|
1619
|
+
|
|
1620
|
+
monkeypatch.setattr(
|
|
1621
|
+
runners,
|
|
1622
|
+
"commit_final_bookkeeping",
|
|
1623
|
+
lambda *args, **kwargs: SimpleNamespace(ok=False, attempted=True, committed=False),
|
|
1624
|
+
)
|
|
1625
|
+
monkeypatch.setattr(
|
|
1626
|
+
runners,
|
|
1627
|
+
"commit_task_branch_changes",
|
|
1628
|
+
lambda *args, **kwargs: SimpleNamespace(ok=True, attempted=False, committed=False),
|
|
1629
|
+
)
|
|
1630
|
+
monkeypatch.setattr(
|
|
1631
|
+
runners,
|
|
1632
|
+
"run_git_plan",
|
|
1633
|
+
lambda root, plan: merge_calls.append(plan.name) or SimpleNamespace(ok=True, plan=plan, results=()),
|
|
1634
|
+
)
|
|
1635
|
+
|
|
1636
|
+
status = runners._process_item(family, invocation, "F-001", paths, initial_metadata={})
|
|
1637
|
+
|
|
1638
|
+
assert status == "completed"
|
|
1639
|
+
assert "branch_merge" not in merge_calls
|
|
1640
|
+
checkout = load_task_checkout(family.state_dir, family.task_type, "F-001")
|
|
1641
|
+
assert checkout is not None and checkout.is_active
|
|
1642
|
+
session_status = paths.feature_state_dir / "F-001" / "sessions" / "F-001-session-1" / "session-status.json"
|
|
1643
|
+
assert json.loads(session_status.read_text(encoding="utf-8"))["status"] == "merge_conflict"
|
|
1644
|
+
|
|
1645
|
+
|
|
1646
|
+
def test_wip_commit_failure_skips_base_return(monkeypatch, tmp_path):
|
|
1647
|
+
from prizmkit_runtime import runners
|
|
1648
|
+
from prizmkit_runtime.runner_models import family_for, parse_invocation
|
|
1649
|
+
|
|
1650
|
+
monkeypatch.setenv("DEV_BRANCH", "dev/F-001-wip-fail")
|
|
1651
|
+
paths = _make_paths(tmp_path)
|
|
1652
|
+
family = family_for("feature", paths)
|
|
1653
|
+
invocation = parse_invocation(family, "run", ())
|
|
1654
|
+
invocation = invocation.__class__(**{**invocation.__dict__, "list_path": paths.feature_plan})
|
|
1655
|
+
_install_runner_session_fakes(monkeypatch, runners, statuses=("crashed",), update_status="failed")
|
|
1656
|
+
returns = []
|
|
1657
|
+
|
|
1658
|
+
monkeypatch.setattr(
|
|
1659
|
+
runners,
|
|
1660
|
+
"commit_wip_changes",
|
|
1661
|
+
lambda *args, **kwargs: SimpleNamespace(ok=False, attempted=True, committed=False),
|
|
1662
|
+
)
|
|
1663
|
+
monkeypatch.setattr(
|
|
1664
|
+
runners,
|
|
1665
|
+
"branch_ensure_return",
|
|
1666
|
+
lambda *args, **kwargs: returns.append(args) or SimpleNamespace(ok=True),
|
|
1667
|
+
)
|
|
1668
|
+
monkeypatch.setattr(
|
|
1669
|
+
runners,
|
|
1670
|
+
"run_git_plan",
|
|
1671
|
+
lambda root, plan: SimpleNamespace(ok=True, plan=plan, results=()),
|
|
1672
|
+
)
|
|
1673
|
+
|
|
1674
|
+
status = runners._process_item(family, invocation, "F-001", paths, initial_metadata={})
|
|
1675
|
+
|
|
1676
|
+
assert status == "failed"
|
|
1677
|
+
assert returns == []
|
|
1678
|
+
|
|
1679
|
+
|
|
1606
1680
|
def test_default_branch_names_keep_timestamp_for_each_runner_family(monkeypatch, tmp_path):
|
|
1607
1681
|
from prizmkit_runtime import runners
|
|
1608
1682
|
from prizmkit_runtime.runner_models import family_for
|
|
@@ -1752,7 +1826,7 @@ def test_foreground_runner_emits_structured_feature_progress(monkeypatch, tmp_pa
|
|
|
1752
1826
|
assert "Feature: F-001 — Readable output" in captured.err
|
|
1753
1827
|
assert "Code retry: 1 / 3" in captured.err
|
|
1754
1828
|
assert "Preflight: no user-visible changes to preserve before feature task F-001" in captured.err
|
|
1755
|
-
assert "Pipeline mode: full (Tier 3 —
|
|
1829
|
+
assert "Pipeline mode: full (Tier 3 — Main-Agent Full Guardrails)" in captured.err
|
|
1756
1830
|
assert "Agents: 3" in captured.err
|
|
1757
1831
|
assert "Spawning AI CLI session: F-001-session-1" in captured.err
|
|
1758
1832
|
assert "Session result: success" in captured.err
|
|
@@ -2098,24 +2172,27 @@ def test_preflight_ready_commit_includes_tracked_state_but_excludes_untracked_st
|
|
|
2098
2172
|
assert untracked_state.relative_to(tmp_path).as_posix() not in _head_files(tmp_path)
|
|
2099
2173
|
|
|
2100
2174
|
|
|
2101
|
-
def
|
|
2175
|
+
def test_preflight_ready_commit_excludes_tracked_framework_manifest_but_preserves_other_tracked_support(tmp_path):
|
|
2102
2176
|
from prizmkit_runtime.runner_bookkeeping import commit_preflight_ready_changes
|
|
2103
2177
|
|
|
2104
2178
|
_init_bookkeeping_repo(tmp_path)
|
|
2179
|
+
manifest_path = tmp_path / ".prizmkit" / "manifest.json"
|
|
2105
2180
|
tracked_support_paths = [
|
|
2106
|
-
tmp_path / ".prizmkit" / "manifest.json",
|
|
2107
2181
|
tmp_path / ".prizmkit" / "dev-pipeline" / "prizmkit_runtime" / "runners.py",
|
|
2108
2182
|
tmp_path / ".claude" / "commands" / "prizmkit.md",
|
|
2109
2183
|
]
|
|
2184
|
+
manifest_path.parent.mkdir(parents=True, exist_ok=True)
|
|
2185
|
+
manifest_path.write_text('{"version":"1.1.127"}\n', encoding="utf-8")
|
|
2110
2186
|
for path in tracked_support_paths:
|
|
2111
2187
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
2112
2188
|
path.write_text("installed\n", encoding="utf-8")
|
|
2113
2189
|
subprocess.run(
|
|
2114
|
-
["git", "add", *(path.relative_to(tmp_path).as_posix() for path in tracked_support_paths)],
|
|
2190
|
+
["git", "add", manifest_path.relative_to(tmp_path).as_posix(), *(path.relative_to(tmp_path).as_posix() for path in tracked_support_paths)],
|
|
2115
2191
|
cwd=tmp_path,
|
|
2116
2192
|
check=True,
|
|
2117
2193
|
)
|
|
2118
2194
|
subprocess.run(["git", "commit", "-m", "track support"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
2195
|
+
manifest_path.write_text('{"version":"1.1.128"}\n', encoding="utf-8")
|
|
2119
2196
|
for path in tracked_support_paths:
|
|
2120
2197
|
path.write_text("upgraded\n", encoding="utf-8")
|
|
2121
2198
|
|
|
@@ -2129,19 +2206,23 @@ def test_preflight_ready_commit_includes_tracked_framework_support_but_excludes_
|
|
|
2129
2206
|
path.write_text("transient\n", encoding="utf-8")
|
|
2130
2207
|
|
|
2131
2208
|
result = commit_preflight_ready_changes(tmp_path, "B-001")
|
|
2132
|
-
committed_files = _head_files(tmp_path)
|
|
2133
2209
|
|
|
2134
|
-
assert result.
|
|
2135
|
-
assert
|
|
2136
|
-
assert
|
|
2137
|
-
assert
|
|
2210
|
+
assert result.attempted is True
|
|
2211
|
+
assert result.committed is False
|
|
2212
|
+
assert result.result is not None
|
|
2213
|
+
assert "installer-owned .prizmkit/manifest.json" in result.result.stderr
|
|
2214
|
+
assert _head_message(tmp_path) == "track support"
|
|
2138
2215
|
assert subprocess.run(
|
|
2139
2216
|
["git", "status", "--short", "--untracked-files=no"],
|
|
2140
2217
|
cwd=tmp_path,
|
|
2141
2218
|
check=True,
|
|
2142
2219
|
stdout=subprocess.PIPE,
|
|
2143
2220
|
text=True,
|
|
2144
|
-
).stdout ==
|
|
2221
|
+
).stdout.splitlines() == [
|
|
2222
|
+
" M .claude/commands/prizmkit.md",
|
|
2223
|
+
" M .prizmkit/dev-pipeline/prizmkit_runtime/runners.py",
|
|
2224
|
+
" M .prizmkit/manifest.json",
|
|
2225
|
+
]
|
|
2145
2226
|
|
|
2146
2227
|
|
|
2147
2228
|
def test_preflight_ready_commit_excludes_untracked_support_assets_and_state(tmp_path):
|
|
@@ -2176,6 +2257,53 @@ def test_preflight_ready_commit_excludes_untracked_support_assets_and_state(tmp_
|
|
|
2176
2257
|
assert ".claude" in hidden_status or ".prizmkit/state" in hidden_status
|
|
2177
2258
|
|
|
2178
2259
|
|
|
2260
|
+
def test_preflight_ready_commit_rejects_unmerged_index(tmp_path):
|
|
2261
|
+
from prizmkit_runtime.runner_bookkeeping import commit_preflight_ready_changes
|
|
2262
|
+
|
|
2263
|
+
_init_bookkeeping_repo(tmp_path)
|
|
2264
|
+
subprocess.run(["git", "checkout", "-b", "other"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
2265
|
+
(tmp_path / "staged.txt").write_text("other\n", encoding="utf-8")
|
|
2266
|
+
subprocess.run(["git", "commit", "-am", "other"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
2267
|
+
subprocess.run(["git", "checkout", "main"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
2268
|
+
(tmp_path / "staged.txt").write_text("main\n", encoding="utf-8")
|
|
2269
|
+
subprocess.run(["git", "commit", "-am", "main"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
2270
|
+
subprocess.run(["git", "merge", "other"], cwd=tmp_path, check=False, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
2271
|
+
before_count = _head_commit_count(tmp_path)
|
|
2272
|
+
|
|
2273
|
+
result = commit_preflight_ready_changes(tmp_path, "F-126")
|
|
2274
|
+
|
|
2275
|
+
assert result.attempted is True
|
|
2276
|
+
assert result.committed is False
|
|
2277
|
+
assert result.result is not None
|
|
2278
|
+
assert "unmerged Git paths" in result.result.stderr
|
|
2279
|
+
assert _head_commit_count(tmp_path) == before_count
|
|
2280
|
+
|
|
2281
|
+
|
|
2282
|
+
def test_preflight_ready_commit_rejects_conflict_markers_and_malformed_manifest(tmp_path):
|
|
2283
|
+
from prizmkit_runtime.runner_bookkeeping import commit_preflight_ready_changes
|
|
2284
|
+
|
|
2285
|
+
_init_bookkeeping_repo(tmp_path)
|
|
2286
|
+
(tmp_path / "unstaged.txt").write_text("<<<<<<< Updated upstream\ncurrent\n=======\nstashed\n>>>>>>> Stashed changes\n", encoding="utf-8")
|
|
2287
|
+
|
|
2288
|
+
marker_result = commit_preflight_ready_changes(tmp_path, "F-127")
|
|
2289
|
+
|
|
2290
|
+
assert marker_result.committed is False
|
|
2291
|
+
assert marker_result.result is not None
|
|
2292
|
+
assert "unresolved conflict markers" in marker_result.result.stderr
|
|
2293
|
+
|
|
2294
|
+
(tmp_path / "unstaged.txt").write_text("safe\n", encoding="utf-8")
|
|
2295
|
+
manifest = tmp_path / ".prizmkit" / "manifest.json"
|
|
2296
|
+
manifest.parent.mkdir(parents=True, exist_ok=True)
|
|
2297
|
+
manifest.write_text("<<<<<<< Updated upstream\n{}\n", encoding="utf-8")
|
|
2298
|
+
|
|
2299
|
+
manifest_result = commit_preflight_ready_changes(tmp_path, "F-128")
|
|
2300
|
+
|
|
2301
|
+
assert manifest_result.committed is False
|
|
2302
|
+
assert manifest_result.result is not None
|
|
2303
|
+
assert ".prizmkit/manifest.json" in manifest_result.result.stderr
|
|
2304
|
+
assert "valid JSON" in manifest_result.result.stderr
|
|
2305
|
+
|
|
2306
|
+
|
|
2179
2307
|
def test_preflight_ready_commit_noops_when_workspace_has_no_visible_dirty_content(tmp_path):
|
|
2180
2308
|
from prizmkit_runtime.runner_bookkeeping import commit_preflight_ready_changes
|
|
2181
2309
|
|
|
@@ -100,11 +100,17 @@ class TestArtifactAndTextHelpers:
|
|
|
100
100
|
report = tmp_path / "review-report.md"
|
|
101
101
|
report.write_text(
|
|
102
102
|
"## Final Result\n- Verdict: PASS\n\n"
|
|
103
|
-
"## Final Result\n- Verdict:
|
|
103
|
+
"## Final Result\n- Verdict: NEEDS_FIXES\n",
|
|
104
104
|
encoding="utf-8",
|
|
105
105
|
)
|
|
106
|
-
|
|
107
|
-
assert
|
|
106
|
+
needs_fixes = run_helper("text", "final-verdict", str(report))
|
|
107
|
+
assert needs_fixes.returncode == 0
|
|
108
|
+
assert first_line(needs_fixes) == "REVIEW_NEEDS_FIXES"
|
|
109
|
+
|
|
110
|
+
report.write_text("## Final Result\n- Verdict: BLOCKED\n", encoding="utf-8")
|
|
111
|
+
retired = run_helper("text", "final-verdict", str(report))
|
|
112
|
+
assert retired.returncode != 0
|
|
113
|
+
assert first_line(retired) == "REVIEW_INVALID"
|
|
108
114
|
|
|
109
115
|
report.write_text("## Final Result\n- Verdict: UNKNOWN\n", encoding="utf-8")
|
|
110
116
|
invalid = run_helper("text", "final-verdict", str(report))
|