prizmkit 1.1.3 → 1.1.5
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/run-bugfix.sh +35 -0
- package/bundled/dev-pipeline/run-feature.sh +33 -0
- package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +221 -0
- package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +124 -0
- package/bundled/dev-pipeline/templates/agent-prompts/dev-implement.md +2 -1
- package/bundled/dev-pipeline/templates/bootstrap-tier1.md +4 -0
- package/bundled/dev-pipeline/templates/bootstrap-tier2.md +5 -2
- package/bundled/dev-pipeline/templates/bootstrap-tier3.md +5 -2
- package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +20 -1
- package/bundled/dev-pipeline/templates/sections/checkpoint-system.md +36 -0
- package/bundled/dev-pipeline/templates/sections/failure-log-check.md +2 -1
- package/bundled/dev-pipeline/templates/sections/phase-analyze-agent.md +3 -0
- package/bundled/dev-pipeline/templates/sections/phase-analyze-full.md +3 -0
- package/bundled/dev-pipeline/templates/sections/phase-browser-verification.md +3 -0
- package/bundled/dev-pipeline/templates/sections/phase-commit-full.md +3 -0
- package/bundled/dev-pipeline/templates/sections/phase-commit.md +3 -0
- package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-agent-suffix.md +3 -0
- package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-lite-suffix.md +3 -0
- package/bundled/dev-pipeline/templates/sections/phase-critic-code.md +3 -0
- package/bundled/dev-pipeline/templates/sections/phase-critic-plan-full.md +3 -0
- package/bundled/dev-pipeline/templates/sections/phase-critic-plan.md +3 -0
- package/bundled/dev-pipeline/templates/sections/phase-deploy-verification.md +3 -0
- package/bundled/dev-pipeline/templates/sections/phase-implement-agent.md +3 -0
- package/bundled/dev-pipeline/templates/sections/phase-implement-full.md +3 -0
- package/bundled/dev-pipeline/templates/sections/phase-implement-lite.md +3 -0
- package/bundled/dev-pipeline/templates/sections/phase-plan-agent.md +3 -0
- package/bundled/dev-pipeline/templates/sections/phase-plan-lite.md +3 -0
- package/bundled/dev-pipeline/templates/sections/phase-review-agent.md +3 -0
- package/bundled/dev-pipeline/templates/sections/phase-review-full.md +3 -0
- package/bundled/dev-pipeline/templates/sections/phase-specify-plan-full.md +3 -0
- package/bundled/dev-pipeline/templates/sections/phase0-init.md +3 -0
- package/bundled/dev-pipeline/templates/sections/phase0-test-baseline.md +3 -0
- package/bundled/dev-pipeline/templates/sections/resume-header.md +4 -1
- package/bundled/rules/prizm/prizm-commit-workflow.md +1 -0
- package/bundled/rules/prizm/prizm-documentation.md +15 -15
- package/bundled/rules/prizm/prizm-progressive-loading.md +2 -1
- package/bundled/skills/_metadata.json +1 -1
- package/bundled/skills/prizm-kit/assets/project-memory-template.md +4 -2
- package/bundled/skills/prizmkit-committer/SKILL.md +28 -0
- package/bundled/skills/prizmkit-implement/SKILL.md +2 -1
- package/bundled/skills/prizmkit-implement/references/deploy-guide-protocol.md +69 -0
- package/bundled/skills/prizmkit-init/SKILL.md +2 -1
- package/bundled/skills/prizmkit-init/references/config-schema.md +64 -0
- package/bundled/skills/prizmkit-init/references/tech-stack-catalog.md +13 -0
- package/bundled/skills/prizmkit-init/references/update-supplement.md +9 -0
- package/bundled/skills/prizmkit-plan/assets/spec-template.md +56 -0
- package/bundled/skills/prizmkit-plan/references/clarify-guide.md +67 -0
- package/bundled/skills/prizmkit-prizm-docs/SKILL.md +7 -4
- package/bundled/skills/prizmkit-prizm-docs/assets/PRIZM-SPEC.md +31 -1
- package/bundled/skills/prizmkit-retrospective/references/knowledge-injection-steps.md +49 -0
- package/bundled/skills/prizmkit-retrospective/references/structural-sync-steps.md +63 -0
- package/bundled/skills/recovery-workflow/SKILL.md +4 -2
- package/bundled/team/prizm-dev-team.json +1 -1
- package/package.json +1 -1
- package/src/config.js +504 -0
- package/src/prompts.js +210 -0
package/bundled/VERSION.json
CHANGED
|
@@ -224,6 +224,41 @@ spawn_and_wait_session() {
|
|
|
224
224
|
|
|
225
225
|
log_info "Session result: $session_status"
|
|
226
226
|
|
|
227
|
+
# Validate checkpoint completeness after successful session
|
|
228
|
+
if [[ "$session_status" == "success" ]]; then
|
|
229
|
+
local _ckpt_root
|
|
230
|
+
_ckpt_root="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
231
|
+
local checkpoint_file="$_ckpt_root/.prizmkit/bugfix/${bug_id}/workflow-checkpoint.json"
|
|
232
|
+
if [[ -f "$checkpoint_file" ]]; then
|
|
233
|
+
local checkpoint_result
|
|
234
|
+
checkpoint_result=$(python3 -c "
|
|
235
|
+
import json, sys
|
|
236
|
+
try:
|
|
237
|
+
with open(sys.argv[1]) as f:
|
|
238
|
+
data = json.load(f)
|
|
239
|
+
except json.JSONDecodeError as e:
|
|
240
|
+
print('CORRUPTED: {}'.format(e))
|
|
241
|
+
sys.exit(2)
|
|
242
|
+
incomplete = [s for s in data['steps'] if s['status'] not in ('completed', 'skipped')]
|
|
243
|
+
if incomplete:
|
|
244
|
+
for s in incomplete:
|
|
245
|
+
print('INCOMPLETE: {} {} = {}'.format(s['id'], s['skill'], s['status']))
|
|
246
|
+
sys.exit(1)
|
|
247
|
+
print('ALL_COMPLETE')
|
|
248
|
+
sys.exit(0)
|
|
249
|
+
" "$checkpoint_file" 2>&1)
|
|
250
|
+
local check_exit=$?
|
|
251
|
+
if [[ $check_exit -eq 2 ]]; then
|
|
252
|
+
log_warn "CHECKPOINT_CORRUPTED: workflow-checkpoint.json is not valid JSON"
|
|
253
|
+
elif [[ $check_exit -eq 1 ]]; then
|
|
254
|
+
log_warn "CHECKPOINT_INCOMPLETE: Not all workflow steps completed:"
|
|
255
|
+
echo "$checkpoint_result" | while read -r line; do log_warn " $line"; done
|
|
256
|
+
else
|
|
257
|
+
log_info "CHECKPOINT: All workflow steps completed"
|
|
258
|
+
fi
|
|
259
|
+
fi
|
|
260
|
+
fi
|
|
261
|
+
|
|
227
262
|
# Subagent detection
|
|
228
263
|
prizm_detect_subagents "$session_log"
|
|
229
264
|
|
|
@@ -287,6 +287,39 @@ sys.exit(1)
|
|
|
287
287
|
if [[ ! -f "$plan_file" ]]; then
|
|
288
288
|
log_warn "ARTIFACT_MISSING: plan.md not found at $plan_file"
|
|
289
289
|
fi
|
|
290
|
+
|
|
291
|
+
# Validate checkpoint completeness
|
|
292
|
+
local checkpoint_file="$project_root_for_artifacts/.prizmkit/specs/${feature_slug}/workflow-checkpoint.json"
|
|
293
|
+
if [[ -f "$checkpoint_file" ]]; then
|
|
294
|
+
local checkpoint_result
|
|
295
|
+
checkpoint_result=$(python3 -c "
|
|
296
|
+
import json, sys
|
|
297
|
+
try:
|
|
298
|
+
with open(sys.argv[1]) as f:
|
|
299
|
+
data = json.load(f)
|
|
300
|
+
except json.JSONDecodeError as e:
|
|
301
|
+
print('CORRUPTED: {}'.format(e))
|
|
302
|
+
sys.exit(2)
|
|
303
|
+
incomplete = [s for s in data['steps'] if s['status'] not in ('completed', 'skipped')]
|
|
304
|
+
if incomplete:
|
|
305
|
+
for s in incomplete:
|
|
306
|
+
print('INCOMPLETE: {} {} = {}'.format(s['id'], s['skill'], s['status']))
|
|
307
|
+
sys.exit(1)
|
|
308
|
+
print('ALL_COMPLETE')
|
|
309
|
+
sys.exit(0)
|
|
310
|
+
" "$checkpoint_file" 2>&1)
|
|
311
|
+
local check_exit=$?
|
|
312
|
+
if [[ $check_exit -eq 2 ]]; then
|
|
313
|
+
log_warn "CHECKPOINT_CORRUPTED: workflow-checkpoint.json is not valid JSON"
|
|
314
|
+
elif [[ $check_exit -eq 1 ]]; then
|
|
315
|
+
log_warn "CHECKPOINT_INCOMPLETE: Not all workflow steps completed:"
|
|
316
|
+
echo "$checkpoint_result" | while read -r line; do log_warn " $line"; done
|
|
317
|
+
else
|
|
318
|
+
log_info "CHECKPOINT: All workflow steps completed"
|
|
319
|
+
fi
|
|
320
|
+
else
|
|
321
|
+
log_info "CHECKPOINT: No workflow-checkpoint.json found (checkpoint system not active)"
|
|
322
|
+
fi
|
|
290
323
|
fi
|
|
291
324
|
|
|
292
325
|
# Check if session produced a failure-log for future retries
|
|
@@ -530,6 +530,171 @@ def determine_pipeline_mode(complexity):
|
|
|
530
530
|
return mapping.get(complexity, "lite")
|
|
531
531
|
|
|
532
532
|
|
|
533
|
+
# ============================================================
|
|
534
|
+
# Checkpoint generation
|
|
535
|
+
# ============================================================
|
|
536
|
+
|
|
537
|
+
# Mapping: section name -> (skill_key, display_name, required_artifacts)
|
|
538
|
+
# skill_key is a unique identifier in the checkpoint, not necessarily the
|
|
539
|
+
# prizmkit skill name. This ensures each section has a distinct key so
|
|
540
|
+
# merge_checkpoint_state() never collides.
|
|
541
|
+
SECTION_TO_SKILL = {
|
|
542
|
+
"phase0-init": ("prizmkit-init", "Project Bootstrap",
|
|
543
|
+
[".prizm-docs/root.prizm", ".prizmkit/config.json"]),
|
|
544
|
+
"phase0-test-baseline": ("test-baseline", "Test Baseline", []),
|
|
545
|
+
"phase-context-snapshot": ("context-snapshot", "Build Context Snapshot",
|
|
546
|
+
[".prizmkit/specs/{slug}/context-snapshot.md"]),
|
|
547
|
+
"phase-specify-plan": ("context-snapshot-and-plan", "Specify & Plan",
|
|
548
|
+
[".prizmkit/specs/{slug}/context-snapshot.md",
|
|
549
|
+
".prizmkit/specs/{slug}/plan.md"]),
|
|
550
|
+
"phase-plan": ("prizmkit-plan", "Plan & Tasks",
|
|
551
|
+
[".prizmkit/specs/{slug}/plan.md"]),
|
|
552
|
+
"phase-analyze": ("prizmkit-analyze", "Analyze", []),
|
|
553
|
+
"phase-critic-plan": ("critic-plan-review", "Critic: Plan Review", []),
|
|
554
|
+
"phase-implement": ("prizmkit-implement", "Implement + Test", []),
|
|
555
|
+
"phase-critic-code": ("critic-code-review", "Critic: Code Review", []),
|
|
556
|
+
"phase-review": ("prizmkit-code-review", "Code Review", []),
|
|
557
|
+
"phase-browser": ("browser-verification", "Browser Verification", []),
|
|
558
|
+
"phase-deploy": ("deploy-verification", "Deploy Verification", []),
|
|
559
|
+
"phase-commit": None, # special: split into retrospective + committer
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
# phase-commit is split into two steps
|
|
563
|
+
_COMMIT_STEPS = [
|
|
564
|
+
("prizmkit-retrospective", "Retrospective", []),
|
|
565
|
+
("prizmkit-committer", "Commit", []),
|
|
566
|
+
]
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
def _resolve_artifacts(artifact_templates, slug):
|
|
570
|
+
"""Replace {slug} placeholder with the actual feature slug."""
|
|
571
|
+
return [a.replace("{slug}", slug) for a in artifact_templates]
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
def generate_checkpoint_definition(sections, pipeline_mode, workflow_type,
|
|
575
|
+
item_id, item_slug, session_id,
|
|
576
|
+
init_done=False):
|
|
577
|
+
"""Derive checkpoint step definitions from the assembled sections list.
|
|
578
|
+
|
|
579
|
+
Args:
|
|
580
|
+
sections: list of (name, content) tuples from assemble_sections()
|
|
581
|
+
pipeline_mode: "lite" | "standard" | "full"
|
|
582
|
+
workflow_type: "feature-pipeline"
|
|
583
|
+
item_id: feature ID (e.g. "F-001")
|
|
584
|
+
item_slug: feature slug (e.g. "001-user-auth")
|
|
585
|
+
session_id: current session ID
|
|
586
|
+
init_done: whether project is already initialized (Phase 0 skip)
|
|
587
|
+
|
|
588
|
+
Returns:
|
|
589
|
+
dict suitable for writing as workflow-checkpoint.json
|
|
590
|
+
"""
|
|
591
|
+
steps = []
|
|
592
|
+
step_counter = 1
|
|
593
|
+
prev_step_id = None
|
|
594
|
+
|
|
595
|
+
for section_name, _content in sections:
|
|
596
|
+
if section_name not in SECTION_TO_SKILL:
|
|
597
|
+
continue
|
|
598
|
+
|
|
599
|
+
mapping = SECTION_TO_SKILL[section_name]
|
|
600
|
+
|
|
601
|
+
if mapping is None:
|
|
602
|
+
# phase-commit -> split into retrospective + committer
|
|
603
|
+
for skill, name, artifacts in _COMMIT_STEPS:
|
|
604
|
+
step_id = "S{:02d}".format(step_counter)
|
|
605
|
+
steps.append({
|
|
606
|
+
"id": step_id,
|
|
607
|
+
"skill": skill,
|
|
608
|
+
"name": name,
|
|
609
|
+
"status": "pending",
|
|
610
|
+
"required_artifacts": _resolve_artifacts(artifacts, item_slug),
|
|
611
|
+
"depends_on": prev_step_id,
|
|
612
|
+
})
|
|
613
|
+
prev_step_id = step_id
|
|
614
|
+
step_counter += 1
|
|
615
|
+
continue
|
|
616
|
+
|
|
617
|
+
skill, name, artifacts = mapping
|
|
618
|
+
step_id = "S{:02d}".format(step_counter)
|
|
619
|
+
|
|
620
|
+
status = "pending"
|
|
621
|
+
if init_done and section_name in ("phase0-init", "phase0-test-baseline"):
|
|
622
|
+
status = "skipped"
|
|
623
|
+
|
|
624
|
+
steps.append({
|
|
625
|
+
"id": step_id,
|
|
626
|
+
"skill": skill,
|
|
627
|
+
"name": name,
|
|
628
|
+
"status": status,
|
|
629
|
+
"required_artifacts": _resolve_artifacts(artifacts, item_slug),
|
|
630
|
+
"depends_on": prev_step_id,
|
|
631
|
+
})
|
|
632
|
+
|
|
633
|
+
prev_step_id = step_id
|
|
634
|
+
step_counter += 1
|
|
635
|
+
|
|
636
|
+
return {
|
|
637
|
+
"version": 1,
|
|
638
|
+
"workflow_type": workflow_type,
|
|
639
|
+
"pipeline_mode": pipeline_mode,
|
|
640
|
+
"item_id": item_id,
|
|
641
|
+
"item_slug": item_slug,
|
|
642
|
+
"session_id": session_id,
|
|
643
|
+
"steps": steps,
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
|
|
647
|
+
def merge_checkpoint_state(existing, fresh, project_root):
|
|
648
|
+
"""Merge existing checkpoint state into a freshly generated definition.
|
|
649
|
+
|
|
650
|
+
Matching is by skill_key (not step ID), since tier changes across retries
|
|
651
|
+
may shift step IDs.
|
|
652
|
+
|
|
653
|
+
Merge rules:
|
|
654
|
+
1. Only keep completed steps whose required_artifacts all exist on disk
|
|
655
|
+
2. Keep skipped steps unconditionally
|
|
656
|
+
3. Once a step is NOT completed/skipped, break the dependency chain:
|
|
657
|
+
all subsequent steps reset to pending
|
|
658
|
+
"""
|
|
659
|
+
existing_status = {}
|
|
660
|
+
existing_artifacts = {}
|
|
661
|
+
for step in existing.get("steps", []):
|
|
662
|
+
existing_status[step["skill"]] = step["status"]
|
|
663
|
+
existing_artifacts[step["skill"]] = step.get("required_artifacts", [])
|
|
664
|
+
|
|
665
|
+
# Determine which completed steps have valid artifacts
|
|
666
|
+
valid_completed = set()
|
|
667
|
+
for skill_key, status in existing_status.items():
|
|
668
|
+
if status == "completed":
|
|
669
|
+
artifacts = existing_artifacts.get(skill_key, [])
|
|
670
|
+
if all(os.path.exists(os.path.join(project_root, a))
|
|
671
|
+
for a in artifacts):
|
|
672
|
+
valid_completed.add(skill_key)
|
|
673
|
+
else:
|
|
674
|
+
LOGGER.warning(
|
|
675
|
+
"Step '%s' was completed but artifacts missing — "
|
|
676
|
+
"resetting to pending", skill_key
|
|
677
|
+
)
|
|
678
|
+
elif status == "skipped":
|
|
679
|
+
valid_completed.add(skill_key)
|
|
680
|
+
|
|
681
|
+
# Apply to fresh steps; break chain on first non-valid step
|
|
682
|
+
chain_broken = False
|
|
683
|
+
for step in fresh["steps"]:
|
|
684
|
+
if chain_broken:
|
|
685
|
+
step["status"] = "pending"
|
|
686
|
+
continue
|
|
687
|
+
|
|
688
|
+
prev = existing_status.get(step["skill"])
|
|
689
|
+
if step["skill"] in valid_completed:
|
|
690
|
+
step["status"] = prev # completed or skipped
|
|
691
|
+
else:
|
|
692
|
+
chain_broken = True
|
|
693
|
+
step["status"] = "pending"
|
|
694
|
+
|
|
695
|
+
return fresh
|
|
696
|
+
|
|
697
|
+
|
|
533
698
|
# ============================================================
|
|
534
699
|
# Section Assembly (new modular approach)
|
|
535
700
|
# ============================================================
|
|
@@ -721,6 +886,12 @@ def assemble_sections(pipeline_mode, sections_dir, init_done, is_resume,
|
|
|
721
886
|
load_section(sections_dir,
|
|
722
887
|
"subagent-timeout-recovery.md")))
|
|
723
888
|
|
|
889
|
+
# --- Checkpoint System ---
|
|
890
|
+
checkpoint_section_path = os.path.join(sections_dir, "checkpoint-system.md")
|
|
891
|
+
if os.path.isfile(checkpoint_section_path):
|
|
892
|
+
sections.append(("checkpoint-system",
|
|
893
|
+
load_section(sections_dir, "checkpoint-system.md")))
|
|
894
|
+
|
|
724
895
|
# --- Execution header ---
|
|
725
896
|
sections.append(("execution-header", "---\n\n## Execution\n"))
|
|
726
897
|
|
|
@@ -1143,6 +1314,9 @@ def build_replacements(args, feature, features, global_context, script_dir):
|
|
|
1143
1314
|
"{{SESSION_STATUS_PATH}}": session_status_abs,
|
|
1144
1315
|
"{{PROJECT_ROOT}}": project_root,
|
|
1145
1316
|
"{{FEATURE_SLUG}}": feature_slug,
|
|
1317
|
+
"{{CHECKPOINT_PATH}}": os.path.join(
|
|
1318
|
+
".prizmkit", "specs", feature_slug, "workflow-checkpoint.json",
|
|
1319
|
+
),
|
|
1146
1320
|
"{{PIPELINE_MODE}}": pipeline_mode,
|
|
1147
1321
|
"{{COMPLEXITY}}": complexity,
|
|
1148
1322
|
"{{CRITIC_ENABLED}}": "true" if critic_enabled else "false",
|
|
@@ -1314,6 +1488,52 @@ def main():
|
|
|
1314
1488
|
if err:
|
|
1315
1489
|
emit_failure(err)
|
|
1316
1490
|
|
|
1491
|
+
# ── Generate checkpoint file ──────────────────────────────────────
|
|
1492
|
+
project_root = resolve_project_root(
|
|
1493
|
+
os.path.dirname(os.path.abspath(__file__))
|
|
1494
|
+
)
|
|
1495
|
+
feature_slug = replacements.get("{{FEATURE_SLUG}}", "")
|
|
1496
|
+
checkpoint_path = ""
|
|
1497
|
+
|
|
1498
|
+
if use_sections and feature_slug:
|
|
1499
|
+
checkpoint = generate_checkpoint_definition(
|
|
1500
|
+
sections=sections,
|
|
1501
|
+
pipeline_mode=pipeline_mode,
|
|
1502
|
+
workflow_type="feature-pipeline",
|
|
1503
|
+
item_id=args.feature_id,
|
|
1504
|
+
item_slug=feature_slug,
|
|
1505
|
+
session_id=args.session_id,
|
|
1506
|
+
init_done=init_done,
|
|
1507
|
+
)
|
|
1508
|
+
|
|
1509
|
+
checkpoint_dir = os.path.join(
|
|
1510
|
+
project_root, ".prizmkit", "specs", feature_slug,
|
|
1511
|
+
)
|
|
1512
|
+
os.makedirs(checkpoint_dir, exist_ok=True)
|
|
1513
|
+
checkpoint_path = os.path.join(
|
|
1514
|
+
checkpoint_dir, "workflow-checkpoint.json",
|
|
1515
|
+
)
|
|
1516
|
+
|
|
1517
|
+
# On resume, merge existing completed state (with artifact validation)
|
|
1518
|
+
if is_resume and os.path.exists(checkpoint_path):
|
|
1519
|
+
try:
|
|
1520
|
+
with open(checkpoint_path, "r", encoding="utf-8") as f:
|
|
1521
|
+
existing = json.load(f)
|
|
1522
|
+
checkpoint = merge_checkpoint_state(
|
|
1523
|
+
existing, checkpoint, project_root,
|
|
1524
|
+
)
|
|
1525
|
+
LOGGER.info("Merged existing checkpoint state from %s",
|
|
1526
|
+
checkpoint_path)
|
|
1527
|
+
except (json.JSONDecodeError, KeyError) as exc:
|
|
1528
|
+
LOGGER.warning(
|
|
1529
|
+
"Existing checkpoint corrupted (%s) — generating fresh",
|
|
1530
|
+
exc,
|
|
1531
|
+
)
|
|
1532
|
+
|
|
1533
|
+
with open(checkpoint_path, "w", encoding="utf-8") as f:
|
|
1534
|
+
json.dump(checkpoint, f, indent=2, ensure_ascii=False)
|
|
1535
|
+
LOGGER.info("Wrote checkpoint to %s", checkpoint_path)
|
|
1536
|
+
|
|
1317
1537
|
# ── Success JSON ───────────────────────────────────────────────────
|
|
1318
1538
|
feature_model = feature.get("model", "")
|
|
1319
1539
|
mode_agent_counts = {"lite": 1, "standard": 3, "full": 3}
|
|
@@ -1331,6 +1551,7 @@ def main():
|
|
|
1331
1551
|
"render_mode": "sections" if use_sections else "legacy",
|
|
1332
1552
|
"validation_warnings": len(warnings),
|
|
1333
1553
|
"validation_errors": len(errors),
|
|
1554
|
+
"checkpoint_path": checkpoint_path,
|
|
1334
1555
|
}
|
|
1335
1556
|
print(json.dumps(output, indent=2, ensure_ascii=False))
|
|
1336
1557
|
sys.exit(0)
|
|
@@ -324,6 +324,95 @@ def emit_failure(message):
|
|
|
324
324
|
sys.exit(1)
|
|
325
325
|
|
|
326
326
|
|
|
327
|
+
# ============================================================
|
|
328
|
+
# Checkpoint generation for bugfix pipeline
|
|
329
|
+
# ============================================================
|
|
330
|
+
|
|
331
|
+
BUGFIX_STEPS = [
|
|
332
|
+
("prizmkit-init", "Initialize", []),
|
|
333
|
+
("bug-diagnosis", "Bug Diagnosis & Fix Plan",
|
|
334
|
+
[".prizmkit/bugfix/{slug}/fix-plan.md"]),
|
|
335
|
+
("bug-reproduce", "Write Reproduction Test", []),
|
|
336
|
+
("bug-fix", "Implement Fix", []),
|
|
337
|
+
("prizmkit-code-review", "Code Review", []),
|
|
338
|
+
("prizmkit-committer", "Commit", []),
|
|
339
|
+
("bug-report", "Generate Fix Report & Update TRAPS",
|
|
340
|
+
[".prizmkit/bugfix/{slug}/fix-report.md"]),
|
|
341
|
+
]
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def generate_bugfix_checkpoint(bug_id, session_id):
|
|
345
|
+
"""Generate a checkpoint definition for bugfix pipeline.
|
|
346
|
+
|
|
347
|
+
Returns a dict suitable for writing as workflow-checkpoint.json.
|
|
348
|
+
"""
|
|
349
|
+
steps = []
|
|
350
|
+
prev_id = None
|
|
351
|
+
for i, (skill, name, artifacts) in enumerate(BUGFIX_STEPS, 1):
|
|
352
|
+
step_id = "S{:02d}".format(i)
|
|
353
|
+
steps.append({
|
|
354
|
+
"id": step_id,
|
|
355
|
+
"skill": skill,
|
|
356
|
+
"name": name,
|
|
357
|
+
"status": "pending",
|
|
358
|
+
"required_artifacts": [a.replace("{slug}", bug_id) for a in artifacts],
|
|
359
|
+
"depends_on": prev_id,
|
|
360
|
+
})
|
|
361
|
+
prev_id = step_id
|
|
362
|
+
|
|
363
|
+
return {
|
|
364
|
+
"version": 1,
|
|
365
|
+
"workflow_type": "bugfix-pipeline",
|
|
366
|
+
"pipeline_mode": "single",
|
|
367
|
+
"item_id": bug_id,
|
|
368
|
+
"item_slug": bug_id,
|
|
369
|
+
"session_id": session_id,
|
|
370
|
+
"steps": steps,
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def merge_bugfix_checkpoint_state(existing, fresh, project_root):
|
|
375
|
+
"""Merge existing bugfix checkpoint state into fresh definition.
|
|
376
|
+
|
|
377
|
+
Same logic as feature pipeline: validate artifacts, break chain on
|
|
378
|
+
first invalid step.
|
|
379
|
+
"""
|
|
380
|
+
existing_status = {}
|
|
381
|
+
existing_artifacts = {}
|
|
382
|
+
for step in existing.get("steps", []):
|
|
383
|
+
existing_status[step["skill"]] = step["status"]
|
|
384
|
+
existing_artifacts[step["skill"]] = step.get("required_artifacts", [])
|
|
385
|
+
|
|
386
|
+
valid_completed = set()
|
|
387
|
+
for skill_key, status in existing_status.items():
|
|
388
|
+
if status == "completed":
|
|
389
|
+
artifacts = existing_artifacts.get(skill_key, [])
|
|
390
|
+
if all(os.path.exists(os.path.join(project_root, a))
|
|
391
|
+
for a in artifacts):
|
|
392
|
+
valid_completed.add(skill_key)
|
|
393
|
+
else:
|
|
394
|
+
LOGGER.warning(
|
|
395
|
+
"Step '%s' was completed but artifacts missing — "
|
|
396
|
+
"resetting to pending", skill_key
|
|
397
|
+
)
|
|
398
|
+
elif status == "skipped":
|
|
399
|
+
valid_completed.add(skill_key)
|
|
400
|
+
|
|
401
|
+
chain_broken = False
|
|
402
|
+
for step in fresh["steps"]:
|
|
403
|
+
if chain_broken:
|
|
404
|
+
step["status"] = "pending"
|
|
405
|
+
continue
|
|
406
|
+
prev = existing_status.get(step["skill"])
|
|
407
|
+
if step["skill"] in valid_completed:
|
|
408
|
+
step["status"] = prev
|
|
409
|
+
else:
|
|
410
|
+
chain_broken = True
|
|
411
|
+
step["status"] = "pending"
|
|
412
|
+
|
|
413
|
+
return fresh
|
|
414
|
+
|
|
415
|
+
|
|
327
416
|
def main():
|
|
328
417
|
args = parse_args()
|
|
329
418
|
|
|
@@ -366,6 +455,12 @@ def main():
|
|
|
366
455
|
# Build replacements
|
|
367
456
|
replacements = build_replacements(args, bug, global_context, script_dir)
|
|
368
457
|
|
|
458
|
+
# Add checkpoint path to replacements
|
|
459
|
+
checkpoint_rel = os.path.join(
|
|
460
|
+
".prizmkit", "bugfix", args.bug_id, "workflow-checkpoint.json",
|
|
461
|
+
)
|
|
462
|
+
replacements["{{CHECKPOINT_PATH}}"] = checkpoint_rel
|
|
463
|
+
|
|
369
464
|
# Render the template
|
|
370
465
|
rendered = render_template(template_content, replacements, bug)
|
|
371
466
|
|
|
@@ -374,10 +469,39 @@ def main():
|
|
|
374
469
|
if err:
|
|
375
470
|
emit_failure(err)
|
|
376
471
|
|
|
472
|
+
# Generate checkpoint file
|
|
473
|
+
project_root = resolve_project_root(script_dir)
|
|
474
|
+
checkpoint_path = os.path.join(project_root, checkpoint_rel)
|
|
475
|
+
checkpoint_dir = os.path.dirname(checkpoint_path)
|
|
476
|
+
os.makedirs(checkpoint_dir, exist_ok=True)
|
|
477
|
+
|
|
478
|
+
checkpoint = generate_bugfix_checkpoint(args.bug_id, args.session_id)
|
|
479
|
+
|
|
480
|
+
is_resume = args.resume_phase != "null"
|
|
481
|
+
if is_resume and os.path.exists(checkpoint_path):
|
|
482
|
+
try:
|
|
483
|
+
with open(checkpoint_path, "r", encoding="utf-8") as f:
|
|
484
|
+
existing = json.load(f)
|
|
485
|
+
checkpoint = merge_bugfix_checkpoint_state(
|
|
486
|
+
existing, checkpoint, project_root,
|
|
487
|
+
)
|
|
488
|
+
LOGGER.info("Merged existing bugfix checkpoint from %s",
|
|
489
|
+
checkpoint_path)
|
|
490
|
+
except (json.JSONDecodeError, KeyError) as exc:
|
|
491
|
+
LOGGER.warning(
|
|
492
|
+
"Existing bugfix checkpoint corrupted (%s) — generating fresh",
|
|
493
|
+
exc,
|
|
494
|
+
)
|
|
495
|
+
|
|
496
|
+
with open(checkpoint_path, "w", encoding="utf-8") as f:
|
|
497
|
+
json.dump(checkpoint, f, indent=2, ensure_ascii=False)
|
|
498
|
+
LOGGER.info("Wrote bugfix checkpoint to %s", checkpoint_path)
|
|
499
|
+
|
|
377
500
|
# Success
|
|
378
501
|
output = {
|
|
379
502
|
"success": True,
|
|
380
503
|
"output_path": os.path.abspath(args.output),
|
|
504
|
+
"checkpoint_path": checkpoint_path,
|
|
381
505
|
}
|
|
382
506
|
print(json.dumps(output, indent=2, ensure_ascii=False))
|
|
383
507
|
sys.exit(0)
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
⚠️ DO NOT re-read source files already listed in Section 4 File Manifest unless you need implementation detail beyond the interface summary.
|
|
4
4
|
1. Read `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md` for full context.
|
|
5
5
|
2. Run `/prizmkit-implement` to execute the tasks in plan.md. Run tests with: `{{TEST_CMD}}`. Known baseline failures (pre-existing, not your fault): `{{BASELINE_FAILURES}}`.
|
|
6
|
-
3.
|
|
6
|
+
3. If plan.md has more than 5 tasks: run `/compact` after completing every 3 tasks to manage context budget. If `/compact` is unavailable, continue without it.
|
|
7
|
+
4. After implement completes, verify the '## Implementation Log' section was written to context-snapshot.md.
|
|
7
8
|
|
|
8
9
|
## Acceptance Criteria Verification
|
|
9
10
|
|
|
@@ -152,6 +152,8 @@ $TEST_CMD 2>&1 | tee /tmp/test-baseline.txt | tail -20
|
|
|
152
152
|
- Runs tests using `TEST_CMD` after each task
|
|
153
153
|
- Writes '## Implementation Log' to `context-snapshot.md`
|
|
154
154
|
|
|
155
|
+
**3b-compact.** Context management — if plan.md has more than 5 tasks, run `/compact` after completing every 3 tasks during implementation. This prevents context window exhaustion in long sessions. If `/compact` is not available (non-Claude CLI), skip this step.
|
|
156
|
+
|
|
155
157
|
**3c.** After implement completes, verify:
|
|
156
158
|
1. All tasks in plan.md are `[x]`
|
|
157
159
|
2. Run the full test suite to ensure nothing is broken
|
|
@@ -285,6 +287,8 @@ git status --short
|
|
|
285
287
|
```
|
|
286
288
|
Working tree MUST be clean after this step. If any feature-related files remain, stage them into the SAME commit via `git add <file> && git commit --amend --no-edit`, do NOT create a separate commit.
|
|
287
289
|
|
|
290
|
+
**Exception**: `session-summary.md` in the artifact directory is a local cross-session artifact generated by `/prizmkit-committer` — it is NOT committed to git. Ignore it in the clean-tree check.
|
|
291
|
+
|
|
288
292
|
## Critical Paths
|
|
289
293
|
|
|
290
294
|
| Resource | Path |
|
|
@@ -215,8 +215,9 @@ Prompt:
|
|
|
215
215
|
> ⚠️ DO NOT re-read source files already listed in Section 4 File Manifest unless you need implementation detail beyond the interface summary.
|
|
216
216
|
> 1. Read `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md` for full context.
|
|
217
217
|
> 2. Run `/prizmkit-implement` to execute the tasks in plan.md. Run tests with: `{{TEST_CMD}}`. Known baseline failures (pre-existing, not your fault): `{{BASELINE_FAILURES}}`.
|
|
218
|
-
> 3.
|
|
219
|
-
> 4.
|
|
218
|
+
> 3. If plan.md has more than 5 tasks: run `/compact` after completing every 3 tasks to manage context budget. If `/compact` is unavailable, continue without it.
|
|
219
|
+
> 4. After implement completes, verify the '## Implementation Log' section was written to context-snapshot.md.
|
|
220
|
+
> 5. Do NOT execute any git commands (no git add/commit/reset/push).
|
|
220
221
|
> Do NOT exit until all tasks are [x] and the '## Implementation Log' section is written in context-snapshot.md."
|
|
221
222
|
|
|
222
223
|
Wait for Dev to return. All tasks must be `[x]`, tests pass.
|
|
@@ -383,6 +384,8 @@ git status --short
|
|
|
383
384
|
```
|
|
384
385
|
Working tree MUST be clean after this step. If any feature-related files remain, stage them into the SAME commit via `git add <file> && git commit --amend --no-edit`, do NOT create a separate commit.
|
|
385
386
|
|
|
387
|
+
**Exception**: `session-summary.md` in the artifact directory is a local cross-session artifact generated by `/prizmkit-committer` — it is NOT committed to git. Ignore it in the clean-tree check.
|
|
388
|
+
|
|
386
389
|
## Critical Paths
|
|
387
390
|
|
|
388
391
|
| Resource | Path |
|
|
@@ -280,8 +280,9 @@ Prompt:
|
|
|
280
280
|
> ⚠️ DO NOT re-read source files already listed in Section 4 File Manifest unless you need implementation detail beyond the interface summary.
|
|
281
281
|
> 1. Read `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md` for full context.
|
|
282
282
|
> 2. Run `/prizmkit-implement` to execute the tasks in plan.md. Run tests with: `{{TEST_CMD}}`. Known baseline failures (pre-existing, not your fault): `{{BASELINE_FAILURES}}`.
|
|
283
|
-
> 3.
|
|
284
|
-
> 4.
|
|
283
|
+
> 3. If plan.md has more than 5 tasks: run `/compact` after completing every 3 tasks to manage context budget. If `/compact` is unavailable, continue without it.
|
|
284
|
+
> 4. After implement completes, verify the '## Implementation Log' section was written to context-snapshot.md.
|
|
285
|
+
> 5. Do NOT execute any git commands (no git add/commit/reset/push).
|
|
285
286
|
> Do NOT exit until all tasks are [x] and the '## Implementation Log' section is written in context-snapshot.md."
|
|
286
287
|
|
|
287
288
|
**Gate Check — Implementation Log**:
|
|
@@ -476,6 +477,8 @@ git status --short
|
|
|
476
477
|
```
|
|
477
478
|
Working tree MUST be clean after this step. If any feature-related files remain, stage them into the SAME commit via `git add <file> && git commit --amend --no-edit`, do NOT create a separate commit.
|
|
478
479
|
|
|
480
|
+
**Exception**: `session-summary.md` in the artifact directory is a local cross-session artifact generated by `/prizmkit-committer` — it is NOT committed to git. Ignore it in the clean-tree check.
|
|
481
|
+
|
|
479
482
|
## Critical Paths
|
|
480
483
|
|
|
481
484
|
| Resource | Path |
|
|
@@ -67,6 +67,19 @@ You are the **bug fix session orchestrator**. Fix Bug {{BUG_ID}}: "{{BUG_TITLE}}
|
|
|
67
67
|
|
|
68
68
|
**IMPORTANT**: Only 2 artifact files per bug, NEVER more. This is a fixed convention.
|
|
69
69
|
|
|
70
|
+
## Workflow Checkpoint System
|
|
71
|
+
|
|
72
|
+
A checkpoint file tracks your progress through this workflow:
|
|
73
|
+
|
|
74
|
+
**Path**: `{{CHECKPOINT_PATH}}`
|
|
75
|
+
|
|
76
|
+
**Rules**:
|
|
77
|
+
1. **Before each step**: Read `workflow-checkpoint.json`, verify the previous step has `status: "completed"` or `status: "skipped"`. If not, complete it first.
|
|
78
|
+
2. **Starting a step**: Update the current step to `status: "in_progress"`.
|
|
79
|
+
3. **After each step completes**: Update the step to `status: "completed"`. Verify JSON is valid after writing.
|
|
80
|
+
4. **On failure**: Set the step to `status: "failed"` and halt.
|
|
81
|
+
5. **On resume**: Steps already `"completed"` are skipped. Start from the first `"pending"` or `"in_progress"` step.
|
|
82
|
+
|
|
70
83
|
## Execution Instructions
|
|
71
84
|
|
|
72
85
|
**YOU are the orchestrator. Execute each phase by spawning the appropriate team agent with run_in_background=false.**
|
|
@@ -109,6 +122,7 @@ Reference `{{TEAM_CONFIG_PATH}}` for agent definitions:
|
|
|
109
122
|
"
|
|
110
123
|
- **Wait for Dev to return**
|
|
111
124
|
- **CP-BF-1**: `.prizmkit/bugfix/{{BUG_ID}}/fix-plan.md` exists
|
|
125
|
+
- **Checkpoint update**: set step `bug-diagnosis` to `"completed"` in `{{CHECKPOINT_PATH}}`
|
|
112
126
|
|
|
113
127
|
**DECISION GATE — Fast Path Check**:
|
|
114
128
|
- If severity is LOW or MEDIUM, AND root cause is obvious (high confidence), AND fix is < 10 lines:
|
|
@@ -141,6 +155,7 @@ Reference `{{TEAM_CONFIG_PATH}}` for agent definitions:
|
|
|
141
155
|
- Write session-status.json with status="partial", errors=["reproduction_failed"]
|
|
142
156
|
- Set bug status to `needs_info` and STOP
|
|
143
157
|
- **CP-BF-2**: Reproduction test exists and FAILS
|
|
158
|
+
- **Checkpoint update**: set step `bug-reproduce` to `"completed"` in `{{CHECKPOINT_PATH}}`
|
|
144
159
|
|
|
145
160
|
---
|
|
146
161
|
|
|
@@ -158,11 +173,13 @@ Reference `{{TEAM_CONFIG_PATH}}` for agent definitions:
|
|
|
158
173
|
- Do NOT refactor — fix the bug only
|
|
159
174
|
4. Run the reproduction test → MUST PASS
|
|
160
175
|
5. Run the module's test suite → MUST PASS (no regression)
|
|
161
|
-
6. If fix
|
|
176
|
+
6. If the fix involves multiple files or steps: run `/compact` after completing the core fix and before running the full test suite, to free context budget. If `/compact` is unavailable, continue without it.
|
|
177
|
+
7. If fix fails after 3 rounds, report detailed analysis
|
|
162
178
|
"
|
|
163
179
|
- **Wait for Dev to return**
|
|
164
180
|
- If fix fails after 3 rounds: escalate to user, write status="failed"
|
|
165
181
|
- **CP-BF-3**: Reproduction test passes, module tests pass
|
|
182
|
+
- **Checkpoint update**: set step `bug-fix` to `"completed"` in `{{CHECKPOINT_PATH}}`
|
|
166
183
|
|
|
167
184
|
---
|
|
168
185
|
|
|
@@ -185,6 +202,7 @@ Reference `{{TEAM_CONFIG_PATH}}` for agent definitions:
|
|
|
185
202
|
- **Wait for Reviewer to return**
|
|
186
203
|
- If NEEDS_FIXES: return to Phase 3 for refinement following Fix Instructions (max 2 review rounds)
|
|
187
204
|
- **CP-BF-4**: Code review passes, all tests green
|
|
205
|
+
- **Checkpoint update**: set step `prizmkit-code-review` to `"completed"` in `{{CHECKPOINT_PATH}}`
|
|
188
206
|
|
|
189
207
|
{{IF_VERIFICATION_MANUAL_OR_HYBRID}}
|
|
190
208
|
**MANUAL VERIFICATION GATE**:
|
|
@@ -222,6 +240,7 @@ Reference `{{TEAM_CONFIG_PATH}}` for agent definitions:
|
|
|
222
240
|
- Acceptance Criteria Verification (checklist with pass/fail for each criterion)
|
|
223
241
|
|
|
224
242
|
- **CP-BF-5**: Commit recorded, fix-report.md written, TRAPS updated (if applicable)
|
|
243
|
+
- **Checkpoint update**: set steps `prizmkit-committer` and `bug-report` to `"completed"` in `{{CHECKPOINT_PATH}}`
|
|
225
244
|
|
|
226
245
|
### Step 3: Report Session Status
|
|
227
246
|
|