prizmkit 1.1.113 → 1.1.115
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/heartbeat.py +0 -1
- 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 +14 -40
- 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 +128 -10
- package/bundled/dev-pipeline/tests/test_runtime_helper.py +23 -0
- package/bundled/dev-pipeline/tests/test_unified_cli.py +40 -9
- package/bundled/skills/_metadata.json +1 -1
- package/bundled/skills/app-planner/SKILL.md +18 -2
- package/bundled/skills/app-planner/references/generated-plan-review.md +67 -0
- package/bundled/skills/bug-planner/SKILL.md +24 -28
- package/bundled/skills/bug-planner/references/generated-plan-review.md +63 -0
- 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 +30 -34
- package/bundled/skills/feature-planner/assets/planning-guide.md +2 -2
- package/bundled/skills/feature-planner/references/generated-plan-review.md +63 -0
- package/bundled/skills/feature-planner/scripts/validate-and-generate.py +8 -11
- package/bundled/skills/prizmkit-init/SKILL.md +2 -2
- 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/prizmkit-prizm-docs/references/op-init.md +1 -1
- 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 +30 -34
- package/bundled/skills/refactor-planner/references/fast-path.md +2 -2
- package/bundled/skills/refactor-planner/references/generated-plan-review.md +63 -0
- 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/src/clean.js +60 -7
- package/src/config.js +61 -20
- package/src/gitignore-template.js +6 -3
- package/src/manifest.js +6 -2
- package/src/platforms.js +14 -0
- package/src/scaffold.js +198 -34
- package/src/upgrade.js +5 -2
- 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
|
@@ -14,6 +14,8 @@ import sys
|
|
|
14
14
|
from continuation import add_continuation_args, append_continuation_handoff, is_continuation
|
|
15
15
|
from prompt_framework import (
|
|
16
16
|
browser_enabled_from_item,
|
|
17
|
+
checkpoint_group,
|
|
18
|
+
checkpoint_step,
|
|
17
19
|
detect_dev_server_port,
|
|
18
20
|
generate_checkpoint_from_sections,
|
|
19
21
|
load_section,
|
|
@@ -34,26 +36,23 @@ LOGGER = setup_logging("generate-bugfix-prompt")
|
|
|
34
36
|
|
|
35
37
|
# Section name -> checkpoint contract.
|
|
36
38
|
BUGFIX_SECTION_TO_SKILL = {
|
|
37
|
-
"bugfix-phase-init": ("prizmkit-init", "Initialize", [".prizmkit/bugfix/{slug}"]),
|
|
38
|
-
"bugfix-phase-diagnose-plan": (
|
|
39
|
+
"bugfix-phase-init": checkpoint_step("prizmkit-init", "Initialize", [".prizmkit/bugfix/{slug}"]),
|
|
40
|
+
"bugfix-phase-diagnose-plan": checkpoint_step(
|
|
39
41
|
"bug-diagnosis-and-plan", "Diagnose & Plan",
|
|
40
42
|
[".prizmkit/bugfix/{slug}/spec.md", ".prizmkit/bugfix/{slug}/plan.md"],
|
|
41
43
|
),
|
|
42
|
-
"bugfix-phase-
|
|
43
|
-
"bugfix-phase-
|
|
44
|
-
"
|
|
45
|
-
"phase-
|
|
46
|
-
"bugfix-phase-commit-report": None,
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
BUGFIX_SPLIT_STEPS = {
|
|
50
|
-
"bugfix-phase-commit-report": [
|
|
44
|
+
"bugfix-phase-implement": checkpoint_step("prizmkit-implement", "Implement Fix", []),
|
|
45
|
+
"bugfix-phase-review": checkpoint_step("prizmkit-code-review", "Code Review", []),
|
|
46
|
+
"phase-browser": checkpoint_step("browser-verification", "Browser Verification", []),
|
|
47
|
+
"bugfix-phase-commit-report": checkpoint_group([
|
|
51
48
|
("prizmkit-retrospective", "Retrospective", []),
|
|
52
49
|
("prizmkit-committer", "Commit", []),
|
|
53
50
|
("bug-report", "Generate Fix Report", [".prizmkit/bugfix/{slug}/fix-report.md"]),
|
|
54
|
-
],
|
|
51
|
+
]),
|
|
55
52
|
}
|
|
56
53
|
|
|
54
|
+
BUGFIX_SPLIT_STEPS = {}
|
|
55
|
+
|
|
57
56
|
|
|
58
57
|
def parse_args():
|
|
59
58
|
parser = argparse.ArgumentParser(
|
|
@@ -73,7 +72,6 @@ def parse_args():
|
|
|
73
72
|
parser.add_argument("--project-root", default=None, help="AI-visible project root used for rendered prompt paths")
|
|
74
73
|
parser.add_argument("--template", default=None, help="Custom legacy template path. Defaults to section assembly.")
|
|
75
74
|
parser.add_argument("--mode", choices=["lite", "standard", "full"], default=None, help="Pipeline execution mode override")
|
|
76
|
-
parser.add_argument("--critic", choices=["true", "false"], default=None, help="Enable critic agent: true/false")
|
|
77
75
|
add_continuation_args(parser, "bugfix")
|
|
78
76
|
return parser.parse_args()
|
|
79
77
|
|
|
@@ -264,7 +262,6 @@ def build_replacements(args, bug, global_context, script_dir):
|
|
|
264
262
|
agent_ext = ".toml" if platform == "codex" else ".md"
|
|
265
263
|
dev_subagent = os.path.join(agents_dir, "prizm-dev-team-dev{}".format(agent_ext))
|
|
266
264
|
reviewer_subagent = os.path.join(agents_dir, "prizm-dev-team-reviewer{}".format(agent_ext))
|
|
267
|
-
critic_subagent = os.path.join(agents_dir, "prizm-dev-team-critic{}".format(agent_ext))
|
|
268
265
|
|
|
269
266
|
state_dir = (
|
|
270
267
|
os.path.abspath(args.state_dir)
|
|
@@ -284,14 +281,6 @@ def build_replacements(args, bug, global_context, script_dir):
|
|
|
284
281
|
dev_url = "http://localhost:{}".format(dev_port) if dev_port.isdigit() else "<UNKNOWN_DETECT_FROM_PROJECT_CONFIG>"
|
|
285
282
|
|
|
286
283
|
pipeline_mode = determine_bugfix_mode(args, bug)
|
|
287
|
-
critic_requested = args.critic == "true" if args.critic is not None else bool(bug.get("critic", False))
|
|
288
|
-
critic_enabled = critic_requested and pipeline_mode != "lite"
|
|
289
|
-
if critic_requested and pipeline_mode == "lite":
|
|
290
|
-
LOGGER.warning("Critic requested for bug %s but lite mode skips critic phases", args.bug_id)
|
|
291
|
-
if critic_enabled and not os.path.isfile(critic_subagent):
|
|
292
|
-
LOGGER.warning("Critic enabled but agent file not found: %s. Critic phase will be skipped.", critic_subagent)
|
|
293
|
-
critic_enabled = False
|
|
294
|
-
|
|
295
284
|
recovery_like = is_recovery_like(args)
|
|
296
285
|
manual_or_hybrid = verification_type in ("manual", "hybrid")
|
|
297
286
|
manual_policy = (
|
|
@@ -312,8 +301,6 @@ def build_replacements(args, bug, global_context, script_dir):
|
|
|
312
301
|
"{{BUG_TITLE}}": bug.get("title", ""),
|
|
313
302
|
"{{SEVERITY}}": bug.get("severity", "medium"),
|
|
314
303
|
"{{PIPELINE_MODE}}": pipeline_mode,
|
|
315
|
-
"{{CRITIC_ENABLED}}": "true" if critic_enabled else "false",
|
|
316
|
-
"{{CRITIC_COUNT}}": str(bug.get("critic_count", 1)),
|
|
317
304
|
"{{VERIFICATION_TYPE}}": verification_type,
|
|
318
305
|
"{{MANUAL_VERIFICATION_POLICY}}": manual_policy,
|
|
319
306
|
"{{BUG_DESCRIPTION}}": bug.get("description", ""),
|
|
@@ -327,7 +314,6 @@ def build_replacements(args, bug, global_context, script_dir):
|
|
|
327
314
|
"{{TEAM_CONFIG_PATH}}": team_config_path,
|
|
328
315
|
"{{DEV_SUBAGENT_PATH}}": dev_subagent,
|
|
329
316
|
"{{REVIEWER_SUBAGENT_PATH}}": reviewer_subagent,
|
|
330
|
-
"{{CRITIC_SUBAGENT_PATH}}": critic_subagent,
|
|
331
317
|
"{{SESSION_STATUS_PATH}}": session_status_path,
|
|
332
318
|
"{{PROJECT_ROOT}}": project_root,
|
|
333
319
|
"{{ARTIFACT_DIR}}": ".prizmkit/bugfix/{}".format(args.bug_id),
|
|
@@ -388,13 +374,13 @@ def _bugfix_header(pipeline_mode):
|
|
|
388
374
|
title = "# Dev-Pipeline Bug Fix Session Bootstrap — {}\n".format(pipeline_mode.title())
|
|
389
375
|
desc = {
|
|
390
376
|
"lite": "**Tier 1 — Single Agent**: direct root-cause fix by the main orchestrator.",
|
|
391
|
-
"standard": "**Tier 2 — Orchestrator +
|
|
392
|
-
"full": "**Tier 3 — Full Bugfix Guardrails**: direct implementation with stronger diagnosis and
|
|
377
|
+
"standard": "**Tier 2 — Orchestrator + Reviewer**: direct implementation with review gates.",
|
|
378
|
+
"full": "**Tier 3 — Full Bugfix Guardrails**: direct implementation with stronger diagnosis and review gates.",
|
|
393
379
|
}.get(pipeline_mode, "**Bugfix pipeline**")
|
|
394
380
|
return title + "\n" + desc + "\n"
|
|
395
381
|
|
|
396
382
|
|
|
397
|
-
def assemble_bugfix_sections(pipeline_mode, sections_dir,
|
|
383
|
+
def assemble_bugfix_sections(pipeline_mode, sections_dir, browser_enabled, manual_gate):
|
|
398
384
|
sections = [
|
|
399
385
|
("header", _bugfix_header(pipeline_mode)),
|
|
400
386
|
("bugfix-session-context", load_section(sections_dir, "bugfix-session-context.md")),
|
|
@@ -407,8 +393,6 @@ def assemble_bugfix_sections(pipeline_mode, sections_dir, critic_enabled, browse
|
|
|
407
393
|
("bugfix-phase-init", load_section(sections_dir, "bugfix-phase-init.md")),
|
|
408
394
|
("bugfix-phase-diagnose-plan", load_section(sections_dir, "bugfix-phase-diagnose-plan.md")),
|
|
409
395
|
]
|
|
410
|
-
if critic_enabled:
|
|
411
|
-
sections.append(("bugfix-phase-critic", load_section(sections_dir, "bugfix-phase-critic.md")))
|
|
412
396
|
sections.extend([
|
|
413
397
|
("bugfix-phase-implement", load_section(sections_dir, "bugfix-phase-implement.md")),
|
|
414
398
|
("bugfix-phase-review", load_section(sections_dir, "bugfix-phase-review.md")),
|
|
@@ -488,7 +472,6 @@ def main():
|
|
|
488
472
|
|
|
489
473
|
replacements = build_replacements(args, bug, global_context, script_dir)
|
|
490
474
|
pipeline_mode = replacements["{{PIPELINE_MODE}}"]
|
|
491
|
-
critic_enabled = replacements["{{CRITIC_ENABLED}}"] == "true"
|
|
492
475
|
browser_enabled = replacements["{{BROWSER_ENABLED}}"] == "true"
|
|
493
476
|
manual_gate = bug.get("verification_type", "automated") in ("manual", "hybrid") and not is_recovery_like(args)
|
|
494
477
|
|
|
@@ -497,7 +480,7 @@ def main():
|
|
|
497
480
|
if use_sections:
|
|
498
481
|
try:
|
|
499
482
|
sections = assemble_bugfix_sections(
|
|
500
|
-
pipeline_mode, sections_dir,
|
|
483
|
+
pipeline_mode, sections_dir, browser_enabled, manual_gate,
|
|
501
484
|
)
|
|
502
485
|
rendered = render_from_sections(sections, replacements)
|
|
503
486
|
except FileNotFoundError as exc:
|
|
@@ -555,15 +538,13 @@ def main():
|
|
|
555
538
|
if err:
|
|
556
539
|
emit_failure(err)
|
|
557
540
|
|
|
558
|
-
critic_count = int(replacements.get("{{CRITIC_COUNT}}", "1"))
|
|
559
541
|
output = {
|
|
560
542
|
"success": True,
|
|
561
543
|
"output_path": os.path.abspath(args.output),
|
|
562
544
|
"checkpoint_path": checkpoint_path,
|
|
563
545
|
"model": bug.get("model", ""),
|
|
564
546
|
"pipeline_mode": pipeline_mode,
|
|
565
|
-
"agent_count": mode_agent_count(pipeline_mode
|
|
566
|
-
"critic_enabled": critic_enabled,
|
|
547
|
+
"agent_count": mode_agent_count(pipeline_mode),
|
|
567
548
|
"render_mode": "sections" if use_sections else "legacy",
|
|
568
549
|
"validation_warnings": len(warnings),
|
|
569
550
|
"validation_errors": len(errors),
|
|
@@ -14,6 +14,8 @@ import sys
|
|
|
14
14
|
from continuation import add_continuation_args, append_continuation_handoff, is_continuation
|
|
15
15
|
from prompt_framework import (
|
|
16
16
|
browser_enabled_from_item,
|
|
17
|
+
checkpoint_group,
|
|
18
|
+
checkpoint_step,
|
|
17
19
|
detect_dev_server_port,
|
|
18
20
|
generate_checkpoint_from_sections,
|
|
19
21
|
load_section,
|
|
@@ -33,33 +35,30 @@ LOGGER = setup_logging("generate-refactor-prompt")
|
|
|
33
35
|
|
|
34
36
|
|
|
35
37
|
REFACTOR_SECTION_TO_SKILL = {
|
|
36
|
-
"refactor-phase-init": ("prizmkit-init", "Initialize", [".prizmkit/refactor/{slug}"]),
|
|
37
|
-
"refactor-phase-plan": (
|
|
38
|
+
"refactor-phase-init": checkpoint_step("prizmkit-init", "Initialize", [".prizmkit/refactor/{slug}"]),
|
|
39
|
+
"refactor-phase-plan": checkpoint_step(
|
|
38
40
|
"prizmkit-plan", "Plan — Specification & Plan Generation",
|
|
39
41
|
[".prizmkit/refactor/{slug}/spec.md", ".prizmkit/refactor/{slug}/plan.md"],
|
|
40
42
|
),
|
|
41
|
-
"refactor-phase-
|
|
42
|
-
"refactor-phase-implement": (
|
|
43
|
+
"refactor-phase-implement": checkpoint_step(
|
|
43
44
|
"prizmkit-implement", "Implement — Behavior-Preserving Refactoring",
|
|
44
45
|
[".prizmkit/refactor/{slug}/plan.md"],
|
|
45
46
|
),
|
|
46
|
-
"refactor-phase-review": (
|
|
47
|
+
"refactor-phase-review": checkpoint_step(
|
|
47
48
|
"prizmkit-code-review", "Review — Code Review & Behavior Verification",
|
|
48
49
|
[".prizmkit/refactor/{slug}/review-report.md"],
|
|
49
50
|
),
|
|
50
|
-
"phase-browser": ("browser-verification", "Browser Verification", []),
|
|
51
|
-
"refactor-phase-commit-report":
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
REFACTOR_SPLIT_STEPS = {
|
|
55
|
-
"refactor-phase-commit-report": [
|
|
51
|
+
"phase-browser": checkpoint_step("browser-verification", "Browser Verification", []),
|
|
52
|
+
"refactor-phase-commit-report": checkpoint_group([
|
|
56
53
|
("prizmkit-retrospective", "Retrospective", []),
|
|
57
54
|
("prizmkit-committer", "Commit", []),
|
|
58
55
|
("refactor-report", "Generate Refactor Report", [".prizmkit/refactor/{slug}/refactor-report.md"]),
|
|
59
56
|
("completion-summary", "Completion Summary", [".prizmkit/refactor/{slug}/completion-summary.json"]),
|
|
60
|
-
],
|
|
57
|
+
]),
|
|
61
58
|
}
|
|
62
59
|
|
|
60
|
+
REFACTOR_SPLIT_STEPS = {}
|
|
61
|
+
|
|
63
62
|
|
|
64
63
|
def parse_args():
|
|
65
64
|
parser = argparse.ArgumentParser(
|
|
@@ -79,7 +78,6 @@ def parse_args():
|
|
|
79
78
|
parser.add_argument("--project-root", default=None, help="AI-visible project root used for rendered prompt paths")
|
|
80
79
|
parser.add_argument("--template", default=None, help="Custom legacy template path. Defaults to section assembly.")
|
|
81
80
|
parser.add_argument("--mode", choices=["lite", "standard", "full"], default=None, help="Pipeline execution mode override")
|
|
82
|
-
parser.add_argument("--critic", choices=["true", "false"], default=None, help="Enable critic agent: true/false")
|
|
83
81
|
add_continuation_args(parser, "refactor")
|
|
84
82
|
return parser.parse_args()
|
|
85
83
|
|
|
@@ -290,7 +288,6 @@ def build_replacements(args, refactor, refactors, global_context, script_dir):
|
|
|
290
288
|
agent_ext = ".toml" if platform == "codex" else ".md"
|
|
291
289
|
dev_subagent = os.path.join(agents_dir, "prizm-dev-team-dev{}".format(agent_ext))
|
|
292
290
|
reviewer_subagent = os.path.join(agents_dir, "prizm-dev-team-reviewer{}".format(agent_ext))
|
|
293
|
-
critic_subagent = os.path.join(agents_dir, "prizm-dev-team-critic{}".format(agent_ext))
|
|
294
291
|
|
|
295
292
|
state_dir = (
|
|
296
293
|
os.path.abspath(args.state_dir)
|
|
@@ -316,14 +313,6 @@ def build_replacements(args, refactor, refactors, global_context, script_dir):
|
|
|
316
313
|
dev_url = "http://localhost:{}".format(dev_port) if dev_port.isdigit() else "<UNKNOWN_DETECT_FROM_PROJECT_CONFIG>"
|
|
317
314
|
|
|
318
315
|
pipeline_mode = determine_refactor_mode(args, refactor)
|
|
319
|
-
critic_requested = args.critic == "true" if args.critic is not None else bool(refactor.get("critic", False))
|
|
320
|
-
critic_enabled = critic_requested and pipeline_mode != "lite"
|
|
321
|
-
if critic_requested and pipeline_mode == "lite":
|
|
322
|
-
LOGGER.warning("Critic requested for refactor %s but lite mode skips critic phases", args.refactor_id)
|
|
323
|
-
if critic_enabled and not os.path.isfile(critic_subagent):
|
|
324
|
-
LOGGER.warning("Critic enabled but agent file not found: %s. Critic phase will be skipped.", critic_subagent)
|
|
325
|
-
critic_enabled = False
|
|
326
|
-
|
|
327
316
|
replacements = {
|
|
328
317
|
"{{SESSION_LOG_PATH}}": os.path.join(".prizmkit", "state", "refactor", args.refactor_id, "sessions", args.session_id, "logs", "session.log"),
|
|
329
318
|
"{{RUN_ID}}": args.run_id,
|
|
@@ -342,8 +331,6 @@ def build_replacements(args, refactor, refactors, global_context, script_dir):
|
|
|
342
331
|
"{{PRIORITY}}": refactor.get("priority", "medium"),
|
|
343
332
|
"{{COMPLEXITY}}": refactor.get("complexity", "medium"),
|
|
344
333
|
"{{PIPELINE_MODE}}": pipeline_mode,
|
|
345
|
-
"{{CRITIC_ENABLED}}": "true" if critic_enabled else "false",
|
|
346
|
-
"{{CRITIC_COUNT}}": str(refactor.get("critic_count", 1)),
|
|
347
334
|
"{{REFACTOR_DESCRIPTION}}": refactor.get("description", ""),
|
|
348
335
|
"{{USER_CONTEXT}}": format_user_context(refactor.get("user_context", [])),
|
|
349
336
|
"{{ACCEPTANCE_CRITERIA}}": format_acceptance_criteria(refactor.get("acceptance_criteria", [])),
|
|
@@ -353,7 +340,6 @@ def build_replacements(args, refactor, refactors, global_context, script_dir):
|
|
|
353
340
|
"{{TEAM_CONFIG_PATH}}": team_config_path,
|
|
354
341
|
"{{DEV_SUBAGENT_PATH}}": dev_subagent,
|
|
355
342
|
"{{REVIEWER_SUBAGENT_PATH}}": reviewer_subagent,
|
|
356
|
-
"{{CRITIC_SUBAGENT_PATH}}": critic_subagent,
|
|
357
343
|
"{{SESSION_STATUS_PATH}}": session_status_path,
|
|
358
344
|
"{{PROJECT_ROOT}}": project_root,
|
|
359
345
|
"{{ARTIFACT_DIR}}": ".prizmkit/refactor/{}".format(args.refactor_id),
|
|
@@ -409,13 +395,13 @@ def _refactor_header(pipeline_mode):
|
|
|
409
395
|
title = "# Dev-Pipeline Refactor Session Bootstrap — {}\n".format(pipeline_mode.title())
|
|
410
396
|
desc = {
|
|
411
397
|
"lite": "**Tier 1 — Single Agent**: direct behavior-preserving refactor by the main orchestrator.",
|
|
412
|
-
"standard": "**Tier 2 — Orchestrator +
|
|
398
|
+
"standard": "**Tier 2 — Orchestrator + Reviewer**: direct refactor with review gates.",
|
|
413
399
|
"full": "**Tier 3 — Full Refactor Guardrails**: direct refactor with stronger behavior-preservation evidence.",
|
|
414
400
|
}.get(pipeline_mode, "**Refactor pipeline**")
|
|
415
401
|
return title + "\n" + desc + "\n"
|
|
416
402
|
|
|
417
403
|
|
|
418
|
-
def assemble_refactor_sections(pipeline_mode, sections_dir,
|
|
404
|
+
def assemble_refactor_sections(pipeline_mode, sections_dir, browser_enabled):
|
|
419
405
|
sections = [
|
|
420
406
|
("header", _refactor_header(pipeline_mode)),
|
|
421
407
|
("refactor-session-context", load_section(sections_dir, "refactor-session-context.md")),
|
|
@@ -428,8 +414,6 @@ def assemble_refactor_sections(pipeline_mode, sections_dir, critic_enabled, brow
|
|
|
428
414
|
("refactor-phase-init", load_section(sections_dir, "refactor-phase-init.md")),
|
|
429
415
|
("refactor-phase-plan", load_section(sections_dir, "refactor-phase-plan.md")),
|
|
430
416
|
]
|
|
431
|
-
if critic_enabled:
|
|
432
|
-
sections.append(("refactor-phase-critic", load_section(sections_dir, "refactor-phase-critic.md")))
|
|
433
417
|
sections.extend([
|
|
434
418
|
("refactor-phase-implement", load_section(sections_dir, "refactor-phase-implement.md")),
|
|
435
419
|
("refactor-phase-review", load_section(sections_dir, "refactor-phase-review.md")),
|
|
@@ -497,14 +481,13 @@ def main():
|
|
|
497
481
|
|
|
498
482
|
replacements = build_replacements(args, refactor, refactors, global_context, script_dir)
|
|
499
483
|
pipeline_mode = replacements["{{PIPELINE_MODE}}"]
|
|
500
|
-
critic_enabled = replacements["{{CRITIC_ENABLED}}"] == "true"
|
|
501
484
|
browser_enabled = replacements["{{BROWSER_ENABLED}}"] == "true"
|
|
502
485
|
|
|
503
486
|
use_sections = os.path.isdir(sections_dir) and not args.template
|
|
504
487
|
sections = []
|
|
505
488
|
if use_sections:
|
|
506
489
|
try:
|
|
507
|
-
sections = assemble_refactor_sections(pipeline_mode, sections_dir,
|
|
490
|
+
sections = assemble_refactor_sections(pipeline_mode, sections_dir, browser_enabled)
|
|
508
491
|
rendered = render_from_sections(sections, replacements)
|
|
509
492
|
except FileNotFoundError as exc:
|
|
510
493
|
LOGGER.warning("Refactor section assembly failed (%s), falling back to legacy template", exc)
|
|
@@ -561,15 +544,13 @@ def main():
|
|
|
561
544
|
if err:
|
|
562
545
|
emit_failure(err)
|
|
563
546
|
|
|
564
|
-
critic_count = int(replacements.get("{{CRITIC_COUNT}}", "1"))
|
|
565
547
|
output = {
|
|
566
548
|
"success": True,
|
|
567
549
|
"output_path": os.path.abspath(args.output),
|
|
568
550
|
"checkpoint_path": checkpoint_path,
|
|
569
551
|
"model": refactor.get("model", ""),
|
|
570
552
|
"pipeline_mode": pipeline_mode,
|
|
571
|
-
"agent_count": mode_agent_count(pipeline_mode
|
|
572
|
-
"critic_enabled": critic_enabled,
|
|
553
|
+
"agent_count": mode_agent_count(pipeline_mode),
|
|
573
554
|
"render_mode": "sections" if use_sections else "legacy",
|
|
574
555
|
"validation_warnings": len(warnings),
|
|
575
556
|
"validation_errors": len(errors),
|
|
@@ -114,6 +114,12 @@ def validate_bugs(bugs):
|
|
|
114
114
|
errors.append("Bug at index {} is not an object".format(i))
|
|
115
115
|
continue
|
|
116
116
|
|
|
117
|
+
for retired_field in ("critic", "critic_count"):
|
|
118
|
+
if retired_field in bug:
|
|
119
|
+
errors.append(
|
|
120
|
+
"Bug at index {} uses retired field '{}'".format(i, retired_field)
|
|
121
|
+
)
|
|
122
|
+
|
|
117
123
|
# Check required fields
|
|
118
124
|
for field in REQUIRED_BUG_FIELDS:
|
|
119
125
|
if field not in bug:
|
|
@@ -106,6 +106,12 @@ def validate_features(features):
|
|
|
106
106
|
errors.append("Feature at index {} is not an object".format(i))
|
|
107
107
|
continue
|
|
108
108
|
|
|
109
|
+
for retired_field in ("critic", "critic_count"):
|
|
110
|
+
if retired_field in feature:
|
|
111
|
+
errors.append(
|
|
112
|
+
"Feature at index {} uses retired field '{}'".format(i, retired_field)
|
|
113
|
+
)
|
|
114
|
+
|
|
109
115
|
# Check required fields
|
|
110
116
|
for field in REQUIRED_FEATURE_FIELDS:
|
|
111
117
|
if field not in feature:
|
|
@@ -109,6 +109,12 @@ def validate_refactors(refactors):
|
|
|
109
109
|
errors.append("Refactor at index {} is not an object".format(i))
|
|
110
110
|
continue
|
|
111
111
|
|
|
112
|
+
for retired_field in ("critic", "critic_count"):
|
|
113
|
+
if retired_field in refactor:
|
|
114
|
+
errors.append(
|
|
115
|
+
"Refactor at index {} uses retired field '{}'".format(i, retired_field)
|
|
116
|
+
)
|
|
117
|
+
|
|
112
118
|
# Check required fields
|
|
113
119
|
for field in REQUIRED_REFACTOR_FIELDS:
|
|
114
120
|
if field not in refactor:
|
|
@@ -959,7 +959,7 @@ class ProgressTracker:
|
|
|
959
959
|
summary = str(state.get("summary") or "")
|
|
960
960
|
return bool(
|
|
961
961
|
not task_type
|
|
962
|
-
and summary.lower().startswith(("dev:", "
|
|
962
|
+
and summary.lower().startswith(("dev:", "reviewer:", "agent:"))
|
|
963
963
|
)
|
|
964
964
|
|
|
965
965
|
def _has_tracked_claude_subagent_task(self):
|
|
@@ -113,6 +113,55 @@ def completed_step_artifacts_valid(project_root, artifacts):
|
|
|
113
113
|
return all(artifact_exists(project_root, artifact) for artifact in artifacts)
|
|
114
114
|
|
|
115
115
|
|
|
116
|
+
def checkpoint_skip():
|
|
117
|
+
"""Return an explicit no-checkpoint-step contract for a prompt section."""
|
|
118
|
+
return {"kind": "skip"}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def checkpoint_step(skill, name, artifacts=None):
|
|
122
|
+
"""Return an explicit one-step checkpoint contract."""
|
|
123
|
+
return {
|
|
124
|
+
"kind": "step",
|
|
125
|
+
"skill": skill,
|
|
126
|
+
"name": name,
|
|
127
|
+
"required_artifacts": list(artifacts or []),
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def checkpoint_group(steps):
|
|
132
|
+
"""Return an explicit multi-step checkpoint contract."""
|
|
133
|
+
normalized = []
|
|
134
|
+
for step in steps:
|
|
135
|
+
if isinstance(step, dict):
|
|
136
|
+
normalized.append(step)
|
|
137
|
+
else:
|
|
138
|
+
skill, name, artifacts = step
|
|
139
|
+
normalized.append(checkpoint_step(skill, name, artifacts))
|
|
140
|
+
return {"kind": "group", "steps": normalized}
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _normalize_checkpoint_contract(mapping):
|
|
144
|
+
"""Normalize explicit checkpoint contracts and legacy step tuples."""
|
|
145
|
+
if mapping is None:
|
|
146
|
+
return checkpoint_skip()
|
|
147
|
+
if isinstance(mapping, dict):
|
|
148
|
+
return mapping
|
|
149
|
+
skill, name, artifacts = mapping
|
|
150
|
+
return checkpoint_step(skill, name, artifacts)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _contract_steps(contract):
|
|
154
|
+
"""Return normalized step specs for a step/group contract."""
|
|
155
|
+
kind = contract.get("kind")
|
|
156
|
+
if kind == "skip":
|
|
157
|
+
return []
|
|
158
|
+
if kind == "step":
|
|
159
|
+
return [contract]
|
|
160
|
+
if kind == "group":
|
|
161
|
+
return [_normalize_checkpoint_contract(step) for step in contract.get("steps", [])]
|
|
162
|
+
raise ValueError("Unknown checkpoint contract kind: {}".format(kind))
|
|
163
|
+
|
|
164
|
+
|
|
116
165
|
def generate_checkpoint_from_sections(
|
|
117
166
|
sections,
|
|
118
167
|
step_mapping,
|
|
@@ -136,37 +185,32 @@ def generate_checkpoint_from_sections(
|
|
|
136
185
|
continue
|
|
137
186
|
|
|
138
187
|
mapping = step_mapping[section_name]
|
|
139
|
-
if mapping is None:
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
"required_artifacts": resolve_artifacts(artifacts, item_slug),
|
|
166
|
-
"depends_on": prev_step_id,
|
|
167
|
-
})
|
|
168
|
-
prev_step_id = step_id
|
|
169
|
-
step_counter += 1
|
|
188
|
+
if mapping is None and section_name in split_steps:
|
|
189
|
+
# Backward-compatible explicit group conversion for callers that
|
|
190
|
+
# have not yet moved split steps into their mapping. Plain None is
|
|
191
|
+
# always a skip, so no-op sections cannot expand accidentally.
|
|
192
|
+
mapping = checkpoint_group(split_steps[section_name])
|
|
193
|
+
contract = _normalize_checkpoint_contract(mapping)
|
|
194
|
+
|
|
195
|
+
for step_spec in _contract_steps(contract):
|
|
196
|
+
skill = step_spec["skill"]
|
|
197
|
+
name = step_spec["name"]
|
|
198
|
+
artifacts = step_spec.get("required_artifacts", [])
|
|
199
|
+
step_id = "S{:02d}".format(step_counter)
|
|
200
|
+
status = "pending"
|
|
201
|
+
if init_done and section_name in ("phase0-init", "phase0-test-baseline"):
|
|
202
|
+
status = "skipped"
|
|
203
|
+
|
|
204
|
+
steps.append({
|
|
205
|
+
"id": step_id,
|
|
206
|
+
"skill": skill,
|
|
207
|
+
"name": name,
|
|
208
|
+
"status": status,
|
|
209
|
+
"required_artifacts": resolve_artifacts(artifacts, item_slug),
|
|
210
|
+
"depends_on": prev_step_id,
|
|
211
|
+
})
|
|
212
|
+
prev_step_id = step_id
|
|
213
|
+
step_counter += 1
|
|
170
214
|
|
|
171
215
|
return {
|
|
172
216
|
"version": 1,
|
|
@@ -182,42 +226,50 @@ def generate_checkpoint_from_sections(
|
|
|
182
226
|
def merge_checkpoint_state(existing, fresh, project_root, validator=None, logger=None):
|
|
183
227
|
"""Merge existing checkpoint state into a freshly generated definition."""
|
|
184
228
|
validator = validator or (lambda _skill, artifacts: completed_step_artifacts_valid(project_root, artifacts))
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
fresh_artifacts = {
|
|
188
|
-
step["skill"]: step.get("required_artifacts", [])
|
|
189
|
-
for step in fresh.get("steps", [])
|
|
190
|
-
}
|
|
191
|
-
|
|
229
|
+
existing_by_id = {}
|
|
230
|
+
existing_by_skill = {}
|
|
192
231
|
for step in existing.get("steps", []):
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
232
|
+
if not isinstance(step, dict):
|
|
233
|
+
continue
|
|
234
|
+
sid = step.get("id")
|
|
235
|
+
skill = step.get("skill")
|
|
236
|
+
if sid:
|
|
237
|
+
existing_by_id[sid] = step
|
|
238
|
+
if skill:
|
|
239
|
+
existing_by_skill.setdefault(skill, []).append(step)
|
|
240
|
+
|
|
241
|
+
def matching_existing(fresh_step):
|
|
242
|
+
sid = fresh_step.get("id")
|
|
243
|
+
skill = fresh_step.get("skill")
|
|
244
|
+
if sid in existing_by_id and existing_by_id[sid].get("skill") == skill:
|
|
245
|
+
return existing_by_id[sid]
|
|
246
|
+
skill_matches = existing_by_skill.get(skill, [])
|
|
247
|
+
if len(skill_matches) == 1:
|
|
248
|
+
return skill_matches[0]
|
|
249
|
+
return None
|
|
209
250
|
|
|
210
251
|
chain_broken = False
|
|
211
252
|
for step in fresh.get("steps", []):
|
|
212
253
|
if chain_broken:
|
|
213
254
|
step["status"] = "pending"
|
|
214
255
|
continue
|
|
215
|
-
previous =
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
256
|
+
previous = matching_existing(step)
|
|
257
|
+
previous_status = previous.get("status", "pending") if isinstance(previous, dict) else "pending"
|
|
258
|
+
if previous_status == "completed":
|
|
259
|
+
artifacts = step.get("required_artifacts", [])
|
|
260
|
+
if validator(step.get("skill", ""), artifacts):
|
|
261
|
+
step["status"] = "completed"
|
|
262
|
+
continue
|
|
263
|
+
if logger is not None:
|
|
264
|
+
logger.warning(
|
|
265
|
+
"Step '%s' was completed but artifacts missing — resetting to pending",
|
|
266
|
+
step.get("id") or step.get("skill"),
|
|
267
|
+
)
|
|
268
|
+
elif previous_status == "skipped":
|
|
269
|
+
step["status"] = "skipped"
|
|
270
|
+
continue
|
|
271
|
+
chain_broken = True
|
|
272
|
+
step["status"] = "pending"
|
|
221
273
|
|
|
222
274
|
return fresh
|
|
223
275
|
|
|
@@ -241,10 +293,9 @@ def normalize_mode(value, default="lite"):
|
|
|
241
293
|
return value if value in {"lite", "standard", "full"} else default
|
|
242
294
|
|
|
243
295
|
|
|
244
|
-
def mode_agent_count(pipeline_mode
|
|
296
|
+
def mode_agent_count(pipeline_mode):
|
|
245
297
|
"""Return the prompt metadata agent count for a mode."""
|
|
246
|
-
|
|
247
|
-
return base + (int(critic_count or 1) if critic_enabled else 0)
|
|
298
|
+
return {"lite": 1, "standard": 3, "full": 3}.get(pipeline_mode, 1)
|
|
248
299
|
|
|
249
300
|
|
|
250
301
|
def detect_dev_server_port(project_root):
|
|
@@ -232,11 +232,8 @@ def _read_bugfix_checkpoint_phase(project_root, bug_id):
|
|
|
232
232
|
|
|
233
233
|
|
|
234
234
|
def _checkpoint_phase_or_cached(project_root, bug_id, status_data):
|
|
235
|
-
"""Return live checkpoint phase
|
|
236
|
-
|
|
237
|
-
if checkpoint_phase is not None:
|
|
238
|
-
return checkpoint_phase
|
|
239
|
-
return status_data.get("resume_from_phase")
|
|
235
|
+
"""Return live checkpoint phase from the authoritative checkpoint only."""
|
|
236
|
+
return _read_bugfix_checkpoint_phase(project_root, bug_id)
|
|
240
237
|
|
|
241
238
|
|
|
242
239
|
def _sync_checkpoint_phase(project_root, bug_id, status_data):
|
|
@@ -758,8 +755,7 @@ def action_status(bug_list_data, state_dir, project_root=None):
|
|
|
758
755
|
bs = load_bug_status(state_dir, bid)
|
|
759
756
|
retry_count = bs.get("retry_count", 0)
|
|
760
757
|
max_retries_val = bs.get("max_retries", 3)
|
|
761
|
-
# Read phase cursor from checkpoint (
|
|
762
|
-
# status.json field is only a cache/fallback for missing checkpoints.
|
|
758
|
+
# Read phase cursor from checkpoint (authoritative when valid).
|
|
763
759
|
resume_phase = _checkpoint_phase_or_cached(project_root, bid, bs)
|
|
764
760
|
if resume_phase != bs.get("resume_from_phase"):
|
|
765
761
|
bs["resume_from_phase"] = resume_phase
|