prizmkit 1.1.114 → 1.1.116
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/.env.example +0 -1
- package/bundled/dev-pipeline/README.md +4 -6
- package/bundled/dev-pipeline/SCHEMA_ANALYSIS.md +40 -518
- package/bundled/dev-pipeline/assets/prizm-dev-team-integration.md +27 -99
- package/bundled/dev-pipeline/prizmkit_runtime/checkpoint_state.py +283 -0
- package/bundled/dev-pipeline/prizmkit_runtime/daemon.py +3 -13
- package/bundled/dev-pipeline/prizmkit_runtime/gitops.py +2 -0
- package/bundled/dev-pipeline/prizmkit_runtime/heartbeat.py +0 -1
- package/bundled/dev-pipeline/prizmkit_runtime/runner_bookkeeping.py +14 -2
- package/bundled/dev-pipeline/prizmkit_runtime/runner_classification.py +25 -31
- package/bundled/dev-pipeline/prizmkit_runtime/runner_models.py +0 -8
- package/bundled/dev-pipeline/prizmkit_runtime/runner_prompts.py +0 -3
- package/bundled/dev-pipeline/prizmkit_runtime/runners.py +13 -7
- package/bundled/dev-pipeline/prizmkit_runtime/sessions.py +13 -1
- package/bundled/dev-pipeline/scripts/continuation.py +8 -40
- package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +76 -261
- package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +16 -35
- package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +15 -34
- package/bundled/dev-pipeline/scripts/init-bugfix-pipeline.py +6 -0
- package/bundled/dev-pipeline/scripts/init-pipeline.py +6 -0
- package/bundled/dev-pipeline/scripts/init-refactor-pipeline.py +6 -0
- package/bundled/dev-pipeline/scripts/parse-stream-progress.py +1 -1
- package/bundled/dev-pipeline/scripts/prompt_framework.py +114 -63
- package/bundled/dev-pipeline/scripts/update-bug-status.py +3 -7
- package/bundled/dev-pipeline/scripts/update-checkpoint.py +54 -22
- package/bundled/dev-pipeline/scripts/update-feature-status.py +3 -7
- package/bundled/dev-pipeline/scripts/update-refactor-status.py +3 -7
- package/bundled/dev-pipeline/scripts/utils.py +6 -37
- package/bundled/dev-pipeline/templates/bootstrap-prompt.md +1 -1
- package/bundled/dev-pipeline/templates/bootstrap-tier1.md +1 -1
- package/bundled/dev-pipeline/templates/bootstrap-tier2.md +9 -91
- package/bundled/dev-pipeline/templates/bootstrap-tier3.md +9 -66
- package/bundled/dev-pipeline/templates/bug-fix-list-schema.json +10 -16
- package/bundled/dev-pipeline/templates/feature-list-schema.json +32 -17
- package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +0 -16
- package/bundled/dev-pipeline/templates/refactor-list-schema.json +10 -16
- package/bundled/dev-pipeline/templates/sections/bugfix-session-context.md +1 -1
- package/bundled/dev-pipeline/templates/sections/context-budget-rules.md +1 -1
- package/bundled/dev-pipeline/templates/sections/critical-paths-agent.md +0 -1
- package/bundled/dev-pipeline/templates/sections/critical-paths-full.md +0 -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/refactor-session-context.md +1 -1
- package/bundled/dev-pipeline/tests/conftest.py +1 -0
- package/bundled/dev-pipeline/tests/test_checkpoint_state.py +164 -0
- package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +48 -76
- package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +25 -3
- package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +25 -5
- package/bundled/dev-pipeline/tests/test_python_runner_parity.py +329 -16
- package/bundled/dev-pipeline/tests/test_unified_cli.py +40 -9
- package/bundled/skills/_metadata.json +1 -1
- package/bundled/skills/bug-planner/SKILL.md +5 -4
- package/bundled/skills/bug-planner/references/verification.md +29 -0
- package/bundled/skills/bug-planner/scripts/validate-bug-list.py +8 -0
- package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +2 -2
- package/bundled/skills/bugfix-pipeline-launcher/references/configuration.md +1 -6
- package/bundled/skills/feature-pipeline-launcher/SKILL.md +6 -12
- package/bundled/skills/feature-pipeline-launcher/references/configuration.md +0 -1
- package/bundled/skills/feature-planner/SKILL.md +13 -12
- package/bundled/skills/feature-planner/assets/planning-guide.md +2 -2
- package/bundled/skills/feature-planner/scripts/validate-and-generate.py +8 -11
- package/bundled/skills/prizmkit-plan/SKILL.md +15 -1
- package/bundled/skills/prizmkit-plan/references/review-plan-spec-loop.md +101 -0
- package/bundled/skills/refactor-pipeline-launcher/SKILL.md +5 -5
- package/bundled/skills/refactor-pipeline-launcher/references/configuration.md +2 -7
- package/bundled/skills/refactor-planner/SKILL.md +10 -8
- package/bundled/skills/refactor-planner/scripts/validate-and-generate-refactor.py +9 -0
- package/bundled/team/prizm-dev-team.json +2 -9
- package/package.json +1 -1
- package/bundled/agents/prizm-dev-team-critic.md +0 -179
- package/bundled/dev-pipeline/templates/agent-prompts/critic-plan-challenge.md +0 -8
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-critic.md +0 -22
- package/bundled/dev-pipeline/templates/sections/phase-critic-plan-full.md +0 -61
- package/bundled/dev-pipeline/templates/sections/phase-critic-plan.md +0 -38
- package/bundled/dev-pipeline/templates/sections/refactor-phase-critic.md +0 -22
- package/bundled/skills/bug-planner/references/critic-and-verification.md +0 -44
|
@@ -320,7 +320,7 @@ def test_runner_models_parse_legacy_args_and_environment(tmp_path):
|
|
|
320
320
|
invocation = parse_invocation(
|
|
321
321
|
family,
|
|
322
322
|
"run",
|
|
323
|
-
("custom-list.json", "F-2", "--features", "F-003:F-001", "--max-retries", "7", "--max-infra-retries=5", "--mode", "full"
|
|
323
|
+
("custom-list.json", "F-2", "--features", "F-003:F-001", "--max-retries", "7", "--max-infra-retries=5", "--mode", "full"),
|
|
324
324
|
)
|
|
325
325
|
env = RunnerEnvironment.from_env(
|
|
326
326
|
{
|
|
@@ -340,7 +340,6 @@ def test_runner_models_parse_legacy_args_and_environment(tmp_path):
|
|
|
340
340
|
assert invocation.max_retries == 7
|
|
341
341
|
assert invocation.max_infra_retries == 5
|
|
342
342
|
assert invocation.mode == "full"
|
|
343
|
-
assert invocation.critic is True
|
|
344
343
|
assert env.stop_on_failure is True
|
|
345
344
|
assert env.auto_push is True
|
|
346
345
|
assert env.dev_branch == "dev/custom"
|
|
@@ -765,7 +764,6 @@ def test_prompt_generation_result_preserves_per_item_model(monkeypatch, tmp_path
|
|
|
765
764
|
"model": "per-item-model",
|
|
766
765
|
"pipeline_mode": "standard",
|
|
767
766
|
"agent_count": 3,
|
|
768
|
-
"critic_enabled": False,
|
|
769
767
|
}
|
|
770
768
|
),
|
|
771
769
|
stderr="",
|
|
@@ -815,7 +813,6 @@ def test_prompt_generation_passes_explicit_execution_root_to_generator(monkeypat
|
|
|
815
813
|
"model": "",
|
|
816
814
|
"pipeline_mode": "lite",
|
|
817
815
|
"agent_count": 1,
|
|
818
|
-
"critic_enabled": False,
|
|
819
816
|
}
|
|
820
817
|
),
|
|
821
818
|
stderr="",
|
|
@@ -881,7 +878,6 @@ def _install_runner_session_fakes(monkeypatch, runners, *, statuses=("success",)
|
|
|
881
878
|
model="per-item-model",
|
|
882
879
|
pipeline_mode="lite",
|
|
883
880
|
agent_count=1,
|
|
884
|
-
critic_enabled=False,
|
|
885
881
|
checkpoint_path=execution_root / ".prizmkit" / "specs" / "001-low" / "workflow-checkpoint.json",
|
|
886
882
|
artifact_path=execution_root / ".prizmkit" / "specs" / "001-low",
|
|
887
883
|
raw={},
|
|
@@ -1144,7 +1140,6 @@ def test_explicit_worktree_materializes_ignored_support_assets_before_prompt_and
|
|
|
1144
1140
|
model="",
|
|
1145
1141
|
pipeline_mode="lite",
|
|
1146
1142
|
agent_count=1,
|
|
1147
|
-
critic_enabled=False,
|
|
1148
1143
|
checkpoint_path=execution_root / ".prizmkit" / "specs" / "001-support" / "workflow-checkpoint.json",
|
|
1149
1144
|
artifact_path=execution_root / ".prizmkit" / "specs" / "001-support",
|
|
1150
1145
|
raw={},
|
|
@@ -1294,7 +1289,7 @@ def test_no_worktree_recovers_completed_pending_branch_before_new_session(monkey
|
|
|
1294
1289
|
artifact_dir = paths.project_root / ".prizmkit" / "specs" / "001-low"
|
|
1295
1290
|
artifact_dir.mkdir(parents=True)
|
|
1296
1291
|
(artifact_dir / "workflow-checkpoint.json").write_text(
|
|
1297
|
-
json.dumps({"steps": [{"id": "S01", "skill": "done", "status": "completed"}]}),
|
|
1292
|
+
json.dumps({"steps": [{"id": "S01", "skill": "done", "name": "Done", "status": "completed"}]}),
|
|
1298
1293
|
encoding="utf-8",
|
|
1299
1294
|
)
|
|
1300
1295
|
updates = []
|
|
@@ -1476,15 +1471,25 @@ def test_no_worktree_runner_ignores_recovery_side_effect_branch(monkeypatch, tmp
|
|
|
1476
1471
|
assert observed.updates[-1][2]["base_branch"] == "main"
|
|
1477
1472
|
|
|
1478
1473
|
|
|
1479
|
-
|
|
1474
|
+
@pytest.mark.parametrize(
|
|
1475
|
+
("kind", "item_id", "branch"),
|
|
1476
|
+
[
|
|
1477
|
+
("feature", "F-001", "dev/F-001-preflight-fail"),
|
|
1478
|
+
("bugfix", "B-001", "bugfix/B-001-preflight-fail"),
|
|
1479
|
+
("refactor", "R-001", "refactor/R-001-preflight-fail"),
|
|
1480
|
+
],
|
|
1481
|
+
)
|
|
1482
|
+
def test_preflight_commit_failure_aborts_before_branch_worktree_or_status_for_all_families(monkeypatch, tmp_path, kind, item_id, branch):
|
|
1480
1483
|
from prizmkit_runtime import runners
|
|
1481
1484
|
from prizmkit_runtime.runner_models import family_for, parse_invocation
|
|
1482
1485
|
|
|
1483
|
-
monkeypatch.setenv("DEV_BRANCH",
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
+
monkeypatch.setenv("DEV_BRANCH", branch)
|
|
1487
|
+
monkeypatch.setenv("USE_WORKTREE", "1")
|
|
1488
|
+
paths = _make_paths(tmp_path / kind)
|
|
1489
|
+
family = family_for(kind, paths)
|
|
1486
1490
|
invocation = parse_invocation(family, "run", ())
|
|
1487
|
-
|
|
1491
|
+
plan_path = {"feature": paths.feature_plan, "bugfix": paths.bugfix_plan, "refactor": paths.refactor_plan}[kind]
|
|
1492
|
+
invocation = invocation.__class__(**{**invocation.__dict__, "list_path": plan_path})
|
|
1488
1493
|
events = []
|
|
1489
1494
|
|
|
1490
1495
|
monkeypatch.setattr(
|
|
@@ -1495,10 +1500,11 @@ def test_no_worktree_preflight_commit_failure_aborts_before_branch_or_status(mon
|
|
|
1495
1500
|
monkeypatch.setattr(runners, "current_branch", lambda _root: "main")
|
|
1496
1501
|
monkeypatch.setattr(runners, "commit_preflight_ready_changes", lambda *args, **kwargs: SimpleNamespace(attempted=True, committed=False, result=SimpleNamespace(stderr="commit failed\n", stdout="")))
|
|
1497
1502
|
monkeypatch.setattr(runners, "run_git_plan", lambda *args, **kwargs: events.append("branch_create"))
|
|
1503
|
+
monkeypatch.setattr(runners, "ensure_linked_worktree", lambda *args, **kwargs: events.append("worktree_create"), raising=False)
|
|
1498
1504
|
monkeypatch.setattr(runners, "start_item", lambda *args, **kwargs: events.append("start_item"))
|
|
1499
1505
|
|
|
1500
1506
|
with pytest.raises(RuntimeError, match="Preflight dirty workspace preservation failed"):
|
|
1501
|
-
runners._process_item(family, invocation,
|
|
1507
|
+
runners._process_item(family, invocation, item_id, paths, initial_metadata={})
|
|
1502
1508
|
|
|
1503
1509
|
assert events == []
|
|
1504
1510
|
|
|
@@ -1530,7 +1536,7 @@ def test_foreground_runner_emits_structured_feature_progress(monkeypatch, tmp_pa
|
|
|
1530
1536
|
assert "Code retry: 1 / 3" in captured.err
|
|
1531
1537
|
assert "Preflight: no user-visible changes to preserve before feature task F-001" in captured.err
|
|
1532
1538
|
assert "Pipeline mode: lite (Tier 1 — Single Agent)" in captured.err
|
|
1533
|
-
assert "Agents: 1
|
|
1539
|
+
assert "Agents: 1" in captured.err
|
|
1534
1540
|
assert "Spawning AI CLI session: F-001-session-1" in captured.err
|
|
1535
1541
|
assert "Session result: success" in captured.err
|
|
1536
1542
|
|
|
@@ -1564,6 +1570,190 @@ def test_no_worktree_interrupt_saves_wip_and_returns_to_base(monkeypatch, tmp_pa
|
|
|
1564
1570
|
assert ensure_calls == [("main", "dev/F-001-interrupt")]
|
|
1565
1571
|
|
|
1566
1572
|
|
|
1573
|
+
def _init_bookkeeping_repo(project_root):
|
|
1574
|
+
subprocess.run(["git", "init", "-b", "main"], cwd=project_root, check=True, stdout=subprocess.DEVNULL)
|
|
1575
|
+
subprocess.run(["git", "config", "user.email", "test@example.com"], cwd=project_root, check=True)
|
|
1576
|
+
subprocess.run(["git", "config", "user.name", "Test"], cwd=project_root, check=True)
|
|
1577
|
+
(project_root / "staged.txt").write_text("base staged\n", encoding="utf-8")
|
|
1578
|
+
(project_root / "unstaged.txt").write_text("base unstaged\n", encoding="utf-8")
|
|
1579
|
+
subprocess.run(["git", "add", "staged.txt", "unstaged.txt"], cwd=project_root, check=True)
|
|
1580
|
+
subprocess.run(["git", "commit", "-m", "base"], cwd=project_root, check=True, stdout=subprocess.DEVNULL)
|
|
1581
|
+
|
|
1582
|
+
|
|
1583
|
+
def _head_commit_count(project_root):
|
|
1584
|
+
return int(
|
|
1585
|
+
subprocess.run(
|
|
1586
|
+
["git", "rev-list", "--count", "HEAD"],
|
|
1587
|
+
cwd=project_root,
|
|
1588
|
+
check=True,
|
|
1589
|
+
stdout=subprocess.PIPE,
|
|
1590
|
+
text=True,
|
|
1591
|
+
).stdout.strip()
|
|
1592
|
+
)
|
|
1593
|
+
|
|
1594
|
+
|
|
1595
|
+
def _head_message(project_root):
|
|
1596
|
+
return subprocess.run(
|
|
1597
|
+
["git", "log", "-1", "--format=%s"],
|
|
1598
|
+
cwd=project_root,
|
|
1599
|
+
check=True,
|
|
1600
|
+
stdout=subprocess.PIPE,
|
|
1601
|
+
text=True,
|
|
1602
|
+
).stdout.strip()
|
|
1603
|
+
|
|
1604
|
+
|
|
1605
|
+
def _head_files(project_root):
|
|
1606
|
+
return set(
|
|
1607
|
+
subprocess.run(
|
|
1608
|
+
["git", "show", "--name-only", "--format=", "HEAD"],
|
|
1609
|
+
cwd=project_root,
|
|
1610
|
+
check=True,
|
|
1611
|
+
stdout=subprocess.PIPE,
|
|
1612
|
+
text=True,
|
|
1613
|
+
).stdout.splitlines()
|
|
1614
|
+
)
|
|
1615
|
+
|
|
1616
|
+
|
|
1617
|
+
@pytest.mark.parametrize(
|
|
1618
|
+
("scenario", "expected_files"),
|
|
1619
|
+
[
|
|
1620
|
+
("staged_only", {"staged.txt"}),
|
|
1621
|
+
("unstaged_only", {"unstaged.txt"}),
|
|
1622
|
+
("mixed", {"staged.txt", "unstaged.txt", "new-visible.txt"}),
|
|
1623
|
+
],
|
|
1624
|
+
)
|
|
1625
|
+
def test_preflight_ready_commit_preserves_staged_unstaged_and_mixed_visible_changes(tmp_path, scenario, expected_files):
|
|
1626
|
+
from prizmkit_runtime.gitops import HIDDEN_TOOL_WORKTREE_EXCLUDES
|
|
1627
|
+
from prizmkit_runtime.runner_bookkeeping import commit_preflight_ready_changes
|
|
1628
|
+
|
|
1629
|
+
_init_bookkeeping_repo(tmp_path)
|
|
1630
|
+
if scenario in {"staged_only", "mixed"}:
|
|
1631
|
+
(tmp_path / "staged.txt").write_text("staged change\n", encoding="utf-8")
|
|
1632
|
+
subprocess.run(["git", "add", "staged.txt"], cwd=tmp_path, check=True)
|
|
1633
|
+
if scenario in {"unstaged_only", "mixed"}:
|
|
1634
|
+
(tmp_path / "unstaged.txt").write_text("unstaged change\n", encoding="utf-8")
|
|
1635
|
+
if scenario == "mixed":
|
|
1636
|
+
(tmp_path / "new-visible.txt").write_text("new visible\n", encoding="utf-8")
|
|
1637
|
+
|
|
1638
|
+
before_count = _head_commit_count(tmp_path)
|
|
1639
|
+
result = commit_preflight_ready_changes(tmp_path, "F-123")
|
|
1640
|
+
visible_status = subprocess.run(
|
|
1641
|
+
["git", "status", "--porcelain", "--", ".", *HIDDEN_TOOL_WORKTREE_EXCLUDES],
|
|
1642
|
+
cwd=tmp_path,
|
|
1643
|
+
check=True,
|
|
1644
|
+
stdout=subprocess.PIPE,
|
|
1645
|
+
text=True,
|
|
1646
|
+
).stdout
|
|
1647
|
+
|
|
1648
|
+
assert result.attempted is True
|
|
1649
|
+
assert result.committed is True
|
|
1650
|
+
assert _head_commit_count(tmp_path) == before_count + 1
|
|
1651
|
+
assert _head_message(tmp_path) == "chore: ready for F-123"
|
|
1652
|
+
assert expected_files.issubset(_head_files(tmp_path))
|
|
1653
|
+
assert visible_status == ""
|
|
1654
|
+
|
|
1655
|
+
|
|
1656
|
+
def test_preflight_ready_commit_excludes_hidden_support_assets_and_state(tmp_path):
|
|
1657
|
+
from prizmkit_runtime.runner_bookkeeping import commit_preflight_ready_changes
|
|
1658
|
+
|
|
1659
|
+
_init_bookkeeping_repo(tmp_path)
|
|
1660
|
+
(tmp_path / "unstaged.txt").write_text("visible change\n", encoding="utf-8")
|
|
1661
|
+
hidden_paths = [
|
|
1662
|
+
tmp_path / ".claude" / "settings.json",
|
|
1663
|
+
tmp_path / ".codebuddy" / "skills" / "local.md",
|
|
1664
|
+
tmp_path / ".agents" / "skills" / "local.md",
|
|
1665
|
+
tmp_path / ".codex" / "config.toml",
|
|
1666
|
+
tmp_path / ".prizmkit" / "state" / "features" / "F-001" / "status.json",
|
|
1667
|
+
tmp_path / ".claude" / "worktrees" / "agent" / "scratch.txt",
|
|
1668
|
+
]
|
|
1669
|
+
for path in hidden_paths:
|
|
1670
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
1671
|
+
path.write_text("hidden support\n", encoding="utf-8")
|
|
1672
|
+
|
|
1673
|
+
result = commit_preflight_ready_changes(tmp_path, "F-124")
|
|
1674
|
+
committed_files = _head_files(tmp_path)
|
|
1675
|
+
hidden_status = subprocess.run(
|
|
1676
|
+
["git", "status", "--porcelain", "--", ".claude", ".codebuddy", ".agents", ".codex", ".prizmkit/state"],
|
|
1677
|
+
cwd=tmp_path,
|
|
1678
|
+
check=True,
|
|
1679
|
+
stdout=subprocess.PIPE,
|
|
1680
|
+
text=True,
|
|
1681
|
+
).stdout
|
|
1682
|
+
|
|
1683
|
+
assert result.committed is True
|
|
1684
|
+
assert committed_files == {"unstaged.txt"}
|
|
1685
|
+
assert ".claude" in hidden_status or ".prizmkit/state" in hidden_status
|
|
1686
|
+
|
|
1687
|
+
|
|
1688
|
+
def test_preflight_ready_commit_noops_when_workspace_has_no_visible_dirty_content(tmp_path):
|
|
1689
|
+
from prizmkit_runtime.runner_bookkeeping import commit_preflight_ready_changes
|
|
1690
|
+
|
|
1691
|
+
_init_bookkeeping_repo(tmp_path)
|
|
1692
|
+
(tmp_path / ".claude" / "worktrees" / "agent").mkdir(parents=True)
|
|
1693
|
+
(tmp_path / ".claude" / "worktrees" / "agent" / "scratch.txt").write_text("hidden only\n", encoding="utf-8")
|
|
1694
|
+
before_count = _head_commit_count(tmp_path)
|
|
1695
|
+
|
|
1696
|
+
result = commit_preflight_ready_changes(tmp_path, "F-125")
|
|
1697
|
+
|
|
1698
|
+
assert result.attempted is False
|
|
1699
|
+
assert result.committed is False
|
|
1700
|
+
assert _head_commit_count(tmp_path) == before_count
|
|
1701
|
+
|
|
1702
|
+
|
|
1703
|
+
def test_process_item_creates_one_ready_commit_before_branch_setup_for_mixed_visible_changes(monkeypatch, tmp_path):
|
|
1704
|
+
from prizmkit_runtime import runner_bookkeeping, runners
|
|
1705
|
+
from prizmkit_runtime.gitops import HIDDEN_TOOL_WORKTREE_EXCLUDES
|
|
1706
|
+
from prizmkit_runtime.runner_models import family_for, parse_invocation
|
|
1707
|
+
|
|
1708
|
+
paths = _make_paths(tmp_path)
|
|
1709
|
+
_init_bookkeeping_repo(paths.project_root)
|
|
1710
|
+
(paths.project_root / "staged.txt").write_text("staged before run\n", encoding="utf-8")
|
|
1711
|
+
subprocess.run(["git", "add", "staged.txt"], cwd=paths.project_root, check=True)
|
|
1712
|
+
(paths.project_root / "unstaged.txt").write_text("unstaged before run\n", encoding="utf-8")
|
|
1713
|
+
before_count = _head_commit_count(paths.project_root)
|
|
1714
|
+
branch_events = []
|
|
1715
|
+
|
|
1716
|
+
monkeypatch.setenv("DEV_BRANCH", "dev/F-001-ready")
|
|
1717
|
+
family = family_for("feature", paths)
|
|
1718
|
+
invocation = parse_invocation(family, "run", ())
|
|
1719
|
+
invocation = invocation.__class__(**{**invocation.__dict__, "list_path": paths.feature_plan})
|
|
1720
|
+
_install_runner_session_fakes(monkeypatch, runners)
|
|
1721
|
+
monkeypatch.setattr(runners, "commit_preflight_ready_changes", runner_bookkeeping.commit_preflight_ready_changes)
|
|
1722
|
+
|
|
1723
|
+
def fake_run_git_plan(project_root, plan):
|
|
1724
|
+
if plan.name == "branch_create":
|
|
1725
|
+
branch_events.append(
|
|
1726
|
+
{
|
|
1727
|
+
"commit_count": _head_commit_count(project_root),
|
|
1728
|
+
"message": _head_message(project_root),
|
|
1729
|
+
"visible_status": subprocess.run(
|
|
1730
|
+
["git", "status", "--porcelain", "--", ".", *HIDDEN_TOOL_WORKTREE_EXCLUDES],
|
|
1731
|
+
cwd=project_root,
|
|
1732
|
+
check=True,
|
|
1733
|
+
stdout=subprocess.PIPE,
|
|
1734
|
+
text=True,
|
|
1735
|
+
).stdout,
|
|
1736
|
+
}
|
|
1737
|
+
)
|
|
1738
|
+
return SimpleNamespace(ok=True, plan=plan, results=())
|
|
1739
|
+
|
|
1740
|
+
monkeypatch.setattr(runners, "run_git_plan", fake_run_git_plan)
|
|
1741
|
+
|
|
1742
|
+
status = runners._process_item(family, invocation, "F-001", paths, initial_metadata={})
|
|
1743
|
+
|
|
1744
|
+
assert status == "completed"
|
|
1745
|
+
assert branch_events == [
|
|
1746
|
+
{
|
|
1747
|
+
"commit_count": before_count + 1,
|
|
1748
|
+
"message": "chore: ready for F-001",
|
|
1749
|
+
"visible_status": "",
|
|
1750
|
+
}
|
|
1751
|
+
]
|
|
1752
|
+
assert _head_commit_count(paths.project_root) == before_count + 1
|
|
1753
|
+
assert {"staged.txt", "unstaged.txt"}.issubset(_head_files(paths.project_root))
|
|
1754
|
+
|
|
1755
|
+
|
|
1756
|
+
|
|
1567
1757
|
def test_bookkeeping_commits_only_runner_owned_paths(tmp_path):
|
|
1568
1758
|
from prizmkit_runtime.runner_bookkeeping import commit_final_bookkeeping
|
|
1569
1759
|
from prizmkit_runtime.runner_models import RunnerFamily
|
|
@@ -1667,7 +1857,7 @@ def test_classification_accepts_late_infra_error_with_completed_checkpoint_and_c
|
|
|
1667
1857
|
artifact_dir = tmp_path / ".prizmkit" / "specs" / "001-done"
|
|
1668
1858
|
artifact_dir.mkdir(parents=True)
|
|
1669
1859
|
checkpoint = artifact_dir / "workflow-checkpoint.json"
|
|
1670
|
-
checkpoint.write_text(json.dumps({"steps": [{"id": "S01", "skill": "done", "status": "completed"}]}), encoding="utf-8")
|
|
1860
|
+
checkpoint.write_text(json.dumps({"steps": [{"id": "S01", "skill": "done", "name": "Done", "status": "completed"}]}), encoding="utf-8")
|
|
1671
1861
|
log = tmp_path / "session.log"
|
|
1672
1862
|
log.write_text("model quota exceeded; API error after commit", encoding="utf-8")
|
|
1673
1863
|
raw = AISessionResult(
|
|
@@ -1792,7 +1982,7 @@ def test_classification_accepts_terminal_success_with_completed_checkpoint_witho
|
|
|
1792
1982
|
artifact_dir = tmp_path / ".prizmkit" / "specs" / "001-done"
|
|
1793
1983
|
artifact_dir.mkdir(parents=True)
|
|
1794
1984
|
checkpoint = artifact_dir / "workflow-checkpoint.json"
|
|
1795
|
-
checkpoint.write_text(json.dumps({"steps": [{"id": "S01", "skill": "done", "status": "completed"}]}), encoding="utf-8")
|
|
1985
|
+
checkpoint.write_text(json.dumps({"steps": [{"id": "S01", "skill": "done", "name": "Done", "status": "completed"}]}), encoding="utf-8")
|
|
1796
1986
|
(artifact_dir / "completion-summary.json").write_text("{}", encoding="utf-8")
|
|
1797
1987
|
log = tmp_path / "session.log"
|
|
1798
1988
|
log.write_text("terminal success", encoding="utf-8")
|
|
@@ -2207,3 +2397,126 @@ def test_python_daemon_progress_uses_list_status_without_runtime_files(tmp_path)
|
|
|
2207
2397
|
else:
|
|
2208
2398
|
assert summary["auto_skipped"] == 1
|
|
2209
2399
|
|
|
2400
|
+
|
|
2401
|
+
|
|
2402
|
+
def _init_git_repo_for_invalid_checkpoint_test(tmp_path):
|
|
2403
|
+
subprocess.run(["git", "init", "-b", "main"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
2404
|
+
subprocess.run(["git", "config", "user.email", "test@example.com"], cwd=tmp_path, check=True)
|
|
2405
|
+
subprocess.run(["git", "config", "user.name", "Test"], cwd=tmp_path, check=True)
|
|
2406
|
+
(tmp_path / "base.txt").write_text("base", encoding="utf-8")
|
|
2407
|
+
subprocess.run(["git", "add", "base.txt"], cwd=tmp_path, check=True)
|
|
2408
|
+
subprocess.run(["git", "commit", "-m", "base"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
2409
|
+
return subprocess.run(["git", "rev-parse", "HEAD"], cwd=tmp_path, check=True, stdout=subprocess.PIPE, text=True).stdout.strip()
|
|
2410
|
+
|
|
2411
|
+
|
|
2412
|
+
def _terminal_success_result(tmp_path):
|
|
2413
|
+
from prizmkit_runtime.sessions import AISessionCommand, AISessionResult
|
|
2414
|
+
from prizmkit_runtime.status import ProgressSummary
|
|
2415
|
+
|
|
2416
|
+
log = tmp_path / "session.log"
|
|
2417
|
+
log.write_text("terminal success", encoding="utf-8")
|
|
2418
|
+
return AISessionResult(
|
|
2419
|
+
command=AISessionCommand(("fake",), None, "claude", "stdin"),
|
|
2420
|
+
exit_code=0,
|
|
2421
|
+
timed_out=False,
|
|
2422
|
+
termination_reason=None,
|
|
2423
|
+
stale_marker=None,
|
|
2424
|
+
fatal_error_code="",
|
|
2425
|
+
progress_summary=ProgressSummary(result_subtype="success", terminal_result_text="Completed"),
|
|
2426
|
+
session_log=log,
|
|
2427
|
+
backup_log=tmp_path / "backup.log",
|
|
2428
|
+
log_recovery=None,
|
|
2429
|
+
started_epoch=0,
|
|
2430
|
+
ended_epoch=1,
|
|
2431
|
+
)
|
|
2432
|
+
|
|
2433
|
+
|
|
2434
|
+
def test_invalid_existing_checkpoint_blocks_terminal_success_with_completion_summary(tmp_path):
|
|
2435
|
+
from prizmkit_runtime.runner_classification import classify_session
|
|
2436
|
+
from prizmkit_runtime.runner_models import PromptGenerationResult
|
|
2437
|
+
|
|
2438
|
+
base_head = _init_git_repo_for_invalid_checkpoint_test(tmp_path)
|
|
2439
|
+
artifact_dir = tmp_path / ".prizmkit" / "specs" / "001-invalid"
|
|
2440
|
+
artifact_dir.mkdir(parents=True)
|
|
2441
|
+
checkpoint = artifact_dir / "workflow-checkpoint.json"
|
|
2442
|
+
checkpoint.write_text(json.dumps({"steps": [{"id": "S01", "skill": "done", "name": "Done", "status": "not-valid"}]}), encoding="utf-8")
|
|
2443
|
+
(artifact_dir / "completion-summary.json").write_text("{}", encoding="utf-8")
|
|
2444
|
+
prompt = PromptGenerationResult(output_path=tmp_path / "prompt.md", checkpoint_path=checkpoint, artifact_path=artifact_dir)
|
|
2445
|
+
|
|
2446
|
+
classification = classify_session(_terminal_success_result(tmp_path), project_root=tmp_path, base_head=base_head, prompt=prompt)
|
|
2447
|
+
|
|
2448
|
+
assert classification.session_status == "crashed"
|
|
2449
|
+
assert classification.reason == "invalid_workflow_checkpoint:invalid_status"
|
|
2450
|
+
|
|
2451
|
+
|
|
2452
|
+
def test_invalid_existing_checkpoint_blocks_head_change_success(tmp_path):
|
|
2453
|
+
from prizmkit_runtime.runner_classification import classify_session
|
|
2454
|
+
from prizmkit_runtime.runner_models import PromptGenerationResult
|
|
2455
|
+
from prizmkit_runtime.sessions import AISessionCommand, AISessionResult
|
|
2456
|
+
from prizmkit_runtime.status import ProgressSummary
|
|
2457
|
+
|
|
2458
|
+
base_head = _init_git_repo_for_invalid_checkpoint_test(tmp_path)
|
|
2459
|
+
(tmp_path / "done.txt").write_text("done", encoding="utf-8")
|
|
2460
|
+
subprocess.run(["git", "add", "done.txt"], cwd=tmp_path, check=True)
|
|
2461
|
+
subprocess.run(["git", "commit", "-m", "done"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
2462
|
+
artifact_dir = tmp_path / ".prizmkit" / "specs" / "001-invalid"
|
|
2463
|
+
artifact_dir.mkdir(parents=True)
|
|
2464
|
+
checkpoint = artifact_dir / "workflow-checkpoint.json"
|
|
2465
|
+
checkpoint.write_text(json.dumps({"steps": [{"id": "bad", "skill": "done", "name": "Done", "status": "completed"}]}), encoding="utf-8")
|
|
2466
|
+
raw = AISessionResult(
|
|
2467
|
+
command=AISessionCommand(("fake",), None, "claude", "stdin"),
|
|
2468
|
+
exit_code=1,
|
|
2469
|
+
timed_out=False,
|
|
2470
|
+
termination_reason=None,
|
|
2471
|
+
stale_marker=None,
|
|
2472
|
+
fatal_error_code="context_overflow",
|
|
2473
|
+
progress_summary=ProgressSummary(fatal_error_code="context_overflow"),
|
|
2474
|
+
session_log=tmp_path / "session.log",
|
|
2475
|
+
backup_log=tmp_path / "backup.log",
|
|
2476
|
+
log_recovery=None,
|
|
2477
|
+
started_epoch=0,
|
|
2478
|
+
ended_epoch=1,
|
|
2479
|
+
)
|
|
2480
|
+
raw.session_log.write_text("context overflow", encoding="utf-8")
|
|
2481
|
+
prompt = PromptGenerationResult(output_path=tmp_path / "prompt.md", checkpoint_path=checkpoint, artifact_path=artifact_dir)
|
|
2482
|
+
|
|
2483
|
+
classification = classify_session(raw, project_root=tmp_path, base_head=base_head, prompt=prompt)
|
|
2484
|
+
|
|
2485
|
+
assert classification.session_status == "crashed"
|
|
2486
|
+
assert classification.reason == "invalid_workflow_checkpoint:malformed_step_id"
|
|
2487
|
+
|
|
2488
|
+
|
|
2489
|
+
def test_invalid_existing_checkpoint_blocks_workspace_auto_commit_success(tmp_path):
|
|
2490
|
+
from prizmkit_runtime.runner_classification import classify_session
|
|
2491
|
+
from prizmkit_runtime.runner_models import PromptGenerationResult
|
|
2492
|
+
from prizmkit_runtime.sessions import AISessionCommand, AISessionResult
|
|
2493
|
+
from prizmkit_runtime.status import ProgressSummary
|
|
2494
|
+
|
|
2495
|
+
base_head = _init_git_repo_for_invalid_checkpoint_test(tmp_path)
|
|
2496
|
+
artifact_dir = tmp_path / ".prizmkit" / "specs" / "001-invalid"
|
|
2497
|
+
artifact_dir.mkdir(parents=True)
|
|
2498
|
+
checkpoint = artifact_dir / "workflow-checkpoint.json"
|
|
2499
|
+
checkpoint.write_text(json.dumps({"steps": []}), encoding="utf-8")
|
|
2500
|
+
(tmp_path / "dirty.txt").write_text("dirty", encoding="utf-8")
|
|
2501
|
+
log = tmp_path / "session.log"
|
|
2502
|
+
log.write_text("clean exit but dirty workspace", encoding="utf-8")
|
|
2503
|
+
raw = AISessionResult(
|
|
2504
|
+
command=AISessionCommand(("fake",), None, "claude", "stdin"),
|
|
2505
|
+
exit_code=0,
|
|
2506
|
+
timed_out=False,
|
|
2507
|
+
termination_reason=None,
|
|
2508
|
+
stale_marker=None,
|
|
2509
|
+
fatal_error_code="",
|
|
2510
|
+
progress_summary=ProgressSummary(result_subtype="", terminal_result_text=""),
|
|
2511
|
+
session_log=log,
|
|
2512
|
+
backup_log=tmp_path / "backup.log",
|
|
2513
|
+
log_recovery=None,
|
|
2514
|
+
started_epoch=0,
|
|
2515
|
+
ended_epoch=1,
|
|
2516
|
+
)
|
|
2517
|
+
prompt = PromptGenerationResult(output_path=tmp_path / "prompt.md", checkpoint_path=checkpoint, artifact_path=artifact_dir)
|
|
2518
|
+
|
|
2519
|
+
classification = classify_session(raw, project_root=tmp_path, base_head=base_head, prompt=prompt)
|
|
2520
|
+
|
|
2521
|
+
assert classification.session_status == "crashed"
|
|
2522
|
+
assert classification.reason == "invalid_workflow_checkpoint:empty_steps"
|
|
@@ -793,9 +793,6 @@ def test_session_preamble_writes_redacted_start_command_for_all_launch_profiles(
|
|
|
793
793
|
|
|
794
794
|
def test_headless_subagent_prompt_guidance_keeps_no_worktree_boundary_visible():
|
|
795
795
|
guidance_files = [
|
|
796
|
-
REPO_ROOT / "dev-pipeline" / "templates" / "sections" / "phase-critic-plan.md",
|
|
797
|
-
REPO_ROOT / "dev-pipeline" / "templates" / "sections" / "phase-critic-plan-full.md",
|
|
798
|
-
REPO_ROOT / "dev-pipeline" / "templates" / "agent-prompts" / "critic-plan-challenge.md",
|
|
799
796
|
REPO_ROOT / "dev-pipeline" / "templates" / "bootstrap-tier2.md",
|
|
800
797
|
REPO_ROOT / "dev-pipeline" / "templates" / "bootstrap-tier3.md",
|
|
801
798
|
REPO_ROOT / "core" / "skills" / "prizmkit-skill" / "prizmkit-code-review" / "SKILL.md",
|
|
@@ -1029,12 +1026,13 @@ def test_compact_progress_line_hides_stale_until_no_progress(tmp_path):
|
|
|
1029
1026
|
|
|
1030
1027
|
session_log = tmp_path / "session.log"
|
|
1031
1028
|
progress = tmp_path / "progress.json"
|
|
1029
|
+
checkpoint = tmp_path / "workflow-checkpoint.json"
|
|
1032
1030
|
progress.write_text(
|
|
1033
1031
|
json.dumps(
|
|
1034
1032
|
{
|
|
1035
1033
|
"child_total_bytes": 2 * 1024 * 1024,
|
|
1036
1034
|
"subagent_spawn_count": 5,
|
|
1037
|
-
"current_phase": "
|
|
1035
|
+
"current_phase": "stream-derived-phase",
|
|
1038
1036
|
"current_tool": "Read",
|
|
1039
1037
|
"message_count": 465,
|
|
1040
1038
|
"total_tool_calls": 412,
|
|
@@ -1042,20 +1040,28 @@ def test_compact_progress_line_hides_stale_until_no_progress(tmp_path):
|
|
|
1042
1040
|
),
|
|
1043
1041
|
encoding="utf-8",
|
|
1044
1042
|
)
|
|
1043
|
+
checkpoint.write_text(
|
|
1044
|
+
json.dumps({"steps": [
|
|
1045
|
+
{"id": "S01", "skill": "plan", "name": "Plan", "status": "completed"},
|
|
1046
|
+
{"id": "S02", "skill": "retro", "name": "Retrospective", "status": "pending", "depends_on": "S01"},
|
|
1047
|
+
]}),
|
|
1048
|
+
encoding="utf-8",
|
|
1049
|
+
)
|
|
1045
1050
|
running = HeartbeatState(last_seen_epoch=1.0, stale=False, details="progress", stale_seconds=0, log_size=4 * 1024 * 1024)
|
|
1046
1051
|
brief_pause = HeartbeatState(last_seen_epoch=1.0, stale=False, details="no progress", stale_seconds=125, log_size=4 * 1024 * 1024)
|
|
1047
1052
|
paused = HeartbeatState(last_seen_epoch=1.0, stale=True, details="no progress", stale_seconds=15 * 60, log_size=4 * 1024 * 1024)
|
|
1048
1053
|
resumed = HeartbeatState(last_seen_epoch=3.0, stale=False, details="progress", stale_seconds=0, log_size=5 * 1024 * 1024)
|
|
1049
1054
|
|
|
1050
|
-
running_line = _compact_progress_line(0, session_log, progress, running, stale_threshold_seconds=15 * 60)
|
|
1051
|
-
brief_pause_line = _compact_progress_line(0, session_log, progress, brief_pause, stale_threshold_seconds=15 * 60)
|
|
1052
|
-
paused_line = _compact_progress_line(0, session_log, progress, paused, stale_threshold_seconds=15 * 60)
|
|
1053
|
-
resumed_line = _compact_progress_line(0, session_log, progress, resumed, stale_threshold_seconds=15 * 60)
|
|
1055
|
+
running_line = _compact_progress_line(0, session_log, progress, running, checkpoint_path=checkpoint, stale_threshold_seconds=15 * 60)
|
|
1056
|
+
brief_pause_line = _compact_progress_line(0, session_log, progress, brief_pause, checkpoint_path=checkpoint, stale_threshold_seconds=15 * 60)
|
|
1057
|
+
paused_line = _compact_progress_line(0, session_log, progress, paused, checkpoint_path=checkpoint, stale_threshold_seconds=15 * 60)
|
|
1058
|
+
resumed_line = _compact_progress_line(0, session_log, progress, resumed, checkpoint_path=checkpoint, stale_threshold_seconds=15 * 60)
|
|
1054
1059
|
|
|
1055
1060
|
assert "▶ [HEARTBEAT]" in running_line
|
|
1056
1061
|
assert "log: 4MB" in running_line
|
|
1057
1062
|
assert "child: 2MB/5" in running_line
|
|
1058
|
-
assert "phase:
|
|
1063
|
+
assert "phase: Retrospective" in running_line
|
|
1064
|
+
assert "stream-derived-phase" not in running_line
|
|
1059
1065
|
assert "tool: Read" in running_line
|
|
1060
1066
|
assert "msgs: 465" in running_line
|
|
1061
1067
|
assert "412 tool calls" in running_line
|
|
@@ -1068,6 +1074,31 @@ def test_compact_progress_line_hides_stale_until_no_progress(tmp_path):
|
|
|
1068
1074
|
assert "log: 5MB" in resumed_line
|
|
1069
1075
|
assert "stale:" not in resumed_line
|
|
1070
1076
|
|
|
1077
|
+
def test_compact_progress_line_omits_phase_without_valid_checkpoint(tmp_path):
|
|
1078
|
+
from prizmkit_runtime.heartbeat import HeartbeatState
|
|
1079
|
+
from prizmkit_runtime.sessions import _compact_progress_line
|
|
1080
|
+
|
|
1081
|
+
session_log = tmp_path / "session.log"
|
|
1082
|
+
progress = tmp_path / "progress.json"
|
|
1083
|
+
malformed = tmp_path / "bad-checkpoint.json"
|
|
1084
|
+
invalid = tmp_path / "invalid-checkpoint.json"
|
|
1085
|
+
progress.write_text(
|
|
1086
|
+
json.dumps({"current_phase": "legacy-plan", "current_tool": "Edit"}),
|
|
1087
|
+
encoding="utf-8",
|
|
1088
|
+
)
|
|
1089
|
+
malformed.write_text("{bad", encoding="utf-8")
|
|
1090
|
+
invalid.write_text(json.dumps({"steps": [{"id": "S01", "skill": "x", "name": "X", "status": "done"}]}), encoding="utf-8")
|
|
1091
|
+
state = HeartbeatState(last_seen_epoch=1.0, stale=False, details="progress", stale_seconds=0, log_size=12)
|
|
1092
|
+
|
|
1093
|
+
missing_line = _compact_progress_line(0, session_log, progress, state, checkpoint_path=tmp_path / "missing.json")
|
|
1094
|
+
malformed_line = _compact_progress_line(0, session_log, progress, state, checkpoint_path=malformed)
|
|
1095
|
+
invalid_line = _compact_progress_line(0, session_log, progress, state, checkpoint_path=invalid)
|
|
1096
|
+
|
|
1097
|
+
for line in (missing_line, malformed_line, invalid_line):
|
|
1098
|
+
assert "phase:" not in line
|
|
1099
|
+
assert "legacy-plan" not in line
|
|
1100
|
+
assert "tool: Edit" in line
|
|
1101
|
+
|
|
1071
1102
|
|
|
1072
1103
|
def test_heartbeat_observation_stale_accumulates_by_elapsed_time_and_resets_on_progress(tmp_path, monkeypatch):
|
|
1073
1104
|
import prizmkit_runtime.heartbeat as heartbeat
|
|
@@ -414,14 +414,15 @@ Bug List (by priority):
|
|
|
414
414
|
|
|
415
415
|
---
|
|
416
416
|
|
|
417
|
-
##
|
|
417
|
+
## Verification Defaults
|
|
418
418
|
|
|
419
|
-
When configuring
|
|
419
|
+
When configuring bug verification, read `${SKILL_DIR}/references/verification.md` for verification type and browser verification guidance.
|
|
420
420
|
|
|
421
421
|
Key points:
|
|
422
|
-
-
|
|
423
|
-
-
|
|
422
|
+
- Critical/high severity bugs require clear automated, manual, or hybrid verification evidence.
|
|
423
|
+
- Medium/low severity bugs use the smallest verification that proves the fix and guards against regression.
|
|
424
424
|
- `browser_interaction` is supported for UI-reproducible bugs; `verification_type` still declares whether verification is automated, manual, or hybrid.
|
|
425
|
+
- Do not generate `critic` or `critic_count`; those fields are fully removed and validators reject them.
|
|
425
426
|
|
|
426
427
|
---
|
|
427
428
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Bug Verification Guidance
|
|
2
|
+
|
|
3
|
+
## Verification Defaults
|
|
4
|
+
|
|
5
|
+
All bug fixes need verification that matches severity, reproduction clarity, and risk.
|
|
6
|
+
|
|
7
|
+
| Severity | Default verification expectation | Rationale |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| critical | Automated or hybrid verification with explicit regression coverage | Crash/data-loss/security bugs need durable proof. |
|
|
10
|
+
| high | Automated or hybrid verification for the broken core path | Core-feature failures need regression protection. |
|
|
11
|
+
| medium | Focused automated or manual verification | Workaround cases need proof without over-scoping. |
|
|
12
|
+
| low | Smallest meaningful check | Cosmetic or minor edge cases should stay lightweight. |
|
|
13
|
+
|
|
14
|
+
## Verification Type
|
|
15
|
+
|
|
16
|
+
- `automated`: use when a unit, integration, or E2E test can reliably reproduce the bug and prove the fix.
|
|
17
|
+
- `manual`: use when reproduction needs external state or human judgment and no stable automated check is practical.
|
|
18
|
+
- `hybrid`: use when automated regression coverage is possible but final confidence needs manual/browser confirmation.
|
|
19
|
+
|
|
20
|
+
## Browser Verification
|
|
21
|
+
|
|
22
|
+
Use `browser_interaction` for UI-reproducible bugs.
|
|
23
|
+
|
|
24
|
+
- `tool: auto` lets the runtime choose the best browser tool.
|
|
25
|
+
- `tool: playwright-cli` is best for local dev-server verification in an isolated browser.
|
|
26
|
+
- `tool: opencli` is best when an existing Chrome login/session or third-party integration cookies are needed.
|
|
27
|
+
- `verify_steps` should describe what to prove, not low-level click scripts.
|
|
28
|
+
|
|
29
|
+
Do not emit `critic` or `critic_count`; those fields are retired and validators reject them.
|
|
@@ -139,6 +139,14 @@ def validate(data, feature_ids=None):
|
|
|
139
139
|
for i, bug in enumerate(bugs):
|
|
140
140
|
prefix = "bugs[{}]".format(i)
|
|
141
141
|
|
|
142
|
+
for retired_field in ("critic", "critic_count"):
|
|
143
|
+
if retired_field in bug:
|
|
144
|
+
errors.append(
|
|
145
|
+
"{}: '{}' field is retired; remove it from bug-fix-list data".format(
|
|
146
|
+
prefix, retired_field
|
|
147
|
+
)
|
|
148
|
+
)
|
|
149
|
+
|
|
142
150
|
# Required fields
|
|
143
151
|
bug_id = bug.get("id", "")
|
|
144
152
|
if not bug_id:
|
|
@@ -123,7 +123,7 @@ Detect user intent from their message, then follow the corresponding workflow:
|
|
|
123
123
|
```
|
|
124
124
|
Selected-options example:
|
|
125
125
|
```bash
|
|
126
|
-
VERBOSE=1
|
|
126
|
+
VERBOSE=1 MAX_RETRIES=5 STOP_ON_FAILURE=1 ENABLE_DEPLOY=1 PRIZMKIT_EFFORT=high \
|
|
127
127
|
python3 ./.prizmkit/dev-pipeline/cli.py bugfix run .prizmkit/plans/bug-fix-list.json
|
|
128
128
|
```
|
|
129
129
|
|
|
@@ -134,7 +134,7 @@ Detect user intent from their message, then follow the corresponding workflow:
|
|
|
134
134
|
Selected-options example:
|
|
135
135
|
```bash
|
|
136
136
|
python3 ./.prizmkit/dev-pipeline/cli.py daemon bugfix start .prizmkit/plans/bug-fix-list.json \
|
|
137
|
-
--env "VERBOSE=1
|
|
137
|
+
--env "VERBOSE=1 MAX_RETRIES=5 STOP_ON_FAILURE=1 ENABLE_DEPLOY=1 PRIZMKIT_EFFORT=high"
|
|
138
138
|
```
|
|
139
139
|
|
|
140
140
|
**Manual mode**: Print the assembled command(s) and **stop here**. Do not execute anything. Do not proceed to step 7.
|
|
@@ -11,7 +11,6 @@ Translating user responses to env vars:
|
|
|
11
11
|
| Verbose: Off | `VERBOSE=0` |
|
|
12
12
|
| Verbose: On | `VERBOSE=1` |
|
|
13
13
|
| Max retries: N | `MAX_RETRIES=N` |
|
|
14
|
-
| Critic: On | `ENABLE_CRITIC=1` |
|
|
15
14
|
| Stop on failure: On | `STOP_ON_FAILURE=1` |
|
|
16
15
|
| Deploy: Yes | `ENABLE_DEPLOY=1` |
|
|
17
16
|
| Effort: value | `PRIZMKIT_EFFORT=<value>` |
|
|
@@ -43,15 +42,11 @@ Present these via `AskUserQuestion` in step 5. Round 1 is one `AskUserQuestion`
|
|
|
43
42
|
- 1
|
|
44
43
|
- 5
|
|
45
44
|
|
|
46
|
-
**Question 3 — Critic review** (multiSelect: false):
|
|
47
|
-
- Off (default) — Skip adversarial review (`ENABLE_CRITIC=0`)
|
|
48
|
-
- On — Enable adversarial critic review (`ENABLE_CRITIC=1`): an independent AI agent reviews the diagnosis/plan for completeness and the fix for defects, edge cases, and regression risks. Adds ~5-10 min per bug.
|
|
49
|
-
|
|
50
45
|
**Question 4 — Advanced config?** (multiSelect: false):
|
|
51
46
|
- No (default) — Use defaults for failure behavior
|
|
52
47
|
- Yes — Configure stop-on-failure, deploy, and reasoning effort options
|
|
53
48
|
|
|
54
|
-
Note: Bug filter defaults to all bugs. Runtime selects eligible bugs in stable list order after dependencies are completed; planners should order unrelated bugs by severity/priority before launch.
|
|
49
|
+
Note: Bug filter defaults to all bugs. Runtime selects eligible bugs in stable list order after dependencies are completed; planners should order unrelated bugs by severity/priority before launch. If the user selects "Other" on any option, handle their custom input.
|
|
55
50
|
|
|
56
51
|
### Round 2 (only if Advanced config = Yes)
|
|
57
52
|
|