prizmkit 1.1.148 → 1.1.150
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/README.md +20 -17
- package/bundled/dev-pipeline/prizmkit_runtime/checkpoint_state.py +77 -0
- package/bundled/dev-pipeline/prizmkit_runtime/gitops.py +234 -39
- package/bundled/dev-pipeline/prizmkit_runtime/reset.py +57 -30
- package/bundled/dev-pipeline/prizmkit_runtime/reset_preserve.py +822 -0
- package/bundled/dev-pipeline/prizmkit_runtime/runner_bookkeeping.py +28 -11
- package/bundled/dev-pipeline/prizmkit_runtime/runners.py +71 -28
- package/bundled/dev-pipeline/prizmkit_runtime/task_checkout.py +18 -0
- package/bundled/dev-pipeline/tests/test_python_runner_parity.py +67 -13
- package/bundled/dev-pipeline/tests/test_reset_preserve.py +959 -0
- package/bundled/dev-pipeline/tests/test_unified_cli.py +325 -5
- package/bundled/skills/_metadata.json +1 -1
- package/bundled/skills/app-planner/SKILL.md +27 -27
- package/bundled/skills/app-planner/references/architecture-decisions.md +5 -11
- package/bundled/skills/app-planner/references/frontend-design-guide.md +1 -1
- package/bundled/skills/app-planner/references/generated-plan-review.md +2 -2
- package/bundled/skills/app-planner/references/infrastructure-convention-discovery.md +2 -2
- package/bundled/skills/app-planner/references/project-conventions-discovery.md +1 -1
- package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +21 -9
- package/bundled/skills/feature-pipeline-launcher/SKILL.md +21 -9
- package/bundled/skills/refactor-pipeline-launcher/SKILL.md +21 -9
- package/bundled/templates/project-memory-template.md +38 -0
- package/package.json +1 -1
|
@@ -2412,8 +2412,16 @@ def test_git_branch_worktree_plans_preserve_shell_invariants(tmp_path):
|
|
|
2412
2412
|
assert any(command.args[:2] == ("rebase", "--abort") for command in ensure.commands)
|
|
2413
2413
|
assert any(command.args[:2] == ("add", "-A") for command in ensure.commands)
|
|
2414
2414
|
assert not any(command.args and command.args[0] == "stash" for command in ensure.commands)
|
|
2415
|
+
assert not any(command.args and command.args[0] in {"merge", "push"} for command in save_wip.commands + ensure.commands)
|
|
2416
|
+
assert not any(command.args[:2] == ("branch", "-d") for command in save_wip.commands + ensure.commands)
|
|
2415
2417
|
assert ensure.never_fails is False
|
|
2416
|
-
assert any("
|
|
2418
|
+
assert any(command.args == ("add", "-A", "--", ".") for command in save_wip.commands)
|
|
2419
|
+
assert not any(
|
|
2420
|
+
"exclude" in arg
|
|
2421
|
+
for command in save_wip.commands
|
|
2422
|
+
if command.args[:2] == ("add", "-A")
|
|
2423
|
+
for arg in command.args
|
|
2424
|
+
)
|
|
2417
2425
|
assert worktree.commands[0].args[:2] == ("worktree", "add")
|
|
2418
2426
|
|
|
2419
2427
|
allowed_failure_result = GitPlanResult(
|
|
@@ -2587,16 +2595,27 @@ def test_branch_return_ignores_installed_runtime_paths_without_blocking_wip_comm
|
|
|
2587
2595
|
from prizmkit_runtime.gitops import branch_ensure_return
|
|
2588
2596
|
|
|
2589
2597
|
_init_merge_repo(tmp_path)
|
|
2590
|
-
(tmp_path / ".gitignore").write_text(
|
|
2598
|
+
(tmp_path / ".gitignore").write_text(
|
|
2599
|
+
".prizmkit/*\n.claude/\n.codebuddy/\n.agents/\n.pi\n",
|
|
2600
|
+
encoding="utf-8",
|
|
2601
|
+
)
|
|
2591
2602
|
subprocess.run(["git", "add", ".gitignore"], cwd=tmp_path, check=True)
|
|
2592
2603
|
subprocess.run(["git", "commit", "-m", "ignore installed runtime"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2604
|
+
ignored_files = (
|
|
2605
|
+
tmp_path / ".prizmkit" / "dev-pipeline" / "cli.py",
|
|
2606
|
+
tmp_path / ".claude" / "runtime.json",
|
|
2607
|
+
tmp_path / ".codebuddy" / "runtime.json",
|
|
2608
|
+
tmp_path / ".agents" / "runtime.json",
|
|
2609
|
+
tmp_path / ".pi" / "runtime.json",
|
|
2610
|
+
)
|
|
2611
|
+
for ignored_file in ignored_files:
|
|
2612
|
+
ignored_file.parent.mkdir(parents=True, exist_ok=True)
|
|
2613
|
+
ignored_file.write_text("ignored support\n", encoding="utf-8")
|
|
2596
2614
|
manifest = tmp_path / ".prizmkit" / "manifest.json"
|
|
2597
2615
|
manifest.write_text('{}\n', encoding="utf-8")
|
|
2598
2616
|
subprocess.run(["git", "checkout", "-b", "dev/interrupted"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
2599
2617
|
(tmp_path / "interrupted.txt").write_text("preserved\n", encoding="utf-8")
|
|
2618
|
+
(tmp_path / "interrupted [literal]*.txt").write_text("special path\n", encoding="utf-8")
|
|
2600
2619
|
|
|
2601
2620
|
result = branch_ensure_return(tmp_path, "main", "dev/interrupted")
|
|
2602
2621
|
|
|
@@ -2607,6 +2626,168 @@ def test_branch_return_ignores_installed_runtime_paths_without_blocking_wip_comm
|
|
|
2607
2626
|
assert subprocess.run(
|
|
2608
2627
|
["git", "show", "dev/interrupted:interrupted.txt"], cwd=tmp_path, check=True, stdout=subprocess.PIPE, text=True,
|
|
2609
2628
|
).stdout == "preserved\n"
|
|
2629
|
+
assert subprocess.run(
|
|
2630
|
+
["git", "show", "dev/interrupted:interrupted [literal]*.txt"],
|
|
2631
|
+
cwd=tmp_path,
|
|
2632
|
+
check=True,
|
|
2633
|
+
stdout=subprocess.PIPE,
|
|
2634
|
+
text=True,
|
|
2635
|
+
).stdout == "special path\n"
|
|
2636
|
+
committed_paths = subprocess.run(
|
|
2637
|
+
["git", "ls-tree", "-r", "--name-only", "dev/interrupted"],
|
|
2638
|
+
cwd=tmp_path,
|
|
2639
|
+
check=True,
|
|
2640
|
+
stdout=subprocess.PIPE,
|
|
2641
|
+
text=True,
|
|
2642
|
+
).stdout.splitlines()
|
|
2643
|
+
assert not any(path.startswith((".claude/", ".codebuddy/", ".agents/", ".pi/")) for path in committed_paths)
|
|
2644
|
+
|
|
2645
|
+
|
|
2646
|
+
def test_clean_branch_return_skips_wip_commit_and_checks_out_base(tmp_path):
|
|
2647
|
+
from prizmkit_runtime.gitops import branch_ensure_return
|
|
2648
|
+
|
|
2649
|
+
_init_merge_repo(tmp_path)
|
|
2650
|
+
base_head = subprocess.run(
|
|
2651
|
+
["git", "rev-parse", "HEAD"],
|
|
2652
|
+
cwd=tmp_path,
|
|
2653
|
+
check=True,
|
|
2654
|
+
stdout=subprocess.PIPE,
|
|
2655
|
+
text=True,
|
|
2656
|
+
).stdout.strip()
|
|
2657
|
+
subprocess.run(["git", "checkout", "-b", "dev/clean-interrupt"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
2658
|
+
|
|
2659
|
+
result = branch_ensure_return(tmp_path, "main", "dev/clean-interrupt")
|
|
2660
|
+
|
|
2661
|
+
assert result.ok is True
|
|
2662
|
+
assert not any(command_result.args[:1] == ("commit",) for command_result in result.results)
|
|
2663
|
+
assert subprocess.run(
|
|
2664
|
+
["git", "branch", "--show-current"],
|
|
2665
|
+
cwd=tmp_path,
|
|
2666
|
+
check=True,
|
|
2667
|
+
stdout=subprocess.PIPE,
|
|
2668
|
+
text=True,
|
|
2669
|
+
).stdout.strip() == "main"
|
|
2670
|
+
assert subprocess.run(
|
|
2671
|
+
["git", "rev-parse", "dev/clean-interrupt"],
|
|
2672
|
+
cwd=tmp_path,
|
|
2673
|
+
check=True,
|
|
2674
|
+
stdout=subprocess.PIPE,
|
|
2675
|
+
text=True,
|
|
2676
|
+
).stdout.strip() == base_head
|
|
2677
|
+
|
|
2678
|
+
|
|
2679
|
+
def test_branch_return_commits_staged_unstaged_deleted_renamed_and_special_paths(tmp_path):
|
|
2680
|
+
from prizmkit_runtime.gitops import branch_ensure_return
|
|
2681
|
+
|
|
2682
|
+
_init_merge_repo(tmp_path)
|
|
2683
|
+
tracked_paths = {
|
|
2684
|
+
"mixed.txt": "base mixed\n",
|
|
2685
|
+
"delete me.txt": "delete me\n",
|
|
2686
|
+
"rename source.txt": "rename me\n",
|
|
2687
|
+
}
|
|
2688
|
+
for relative, content in tracked_paths.items():
|
|
2689
|
+
(tmp_path / relative).write_text(content, encoding="utf-8")
|
|
2690
|
+
subprocess.run(["git", "add", "--", *tracked_paths], cwd=tmp_path, check=True)
|
|
2691
|
+
subprocess.run(["git", "commit", "-m", "wip matrix base"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
2692
|
+
subprocess.run(["git", "checkout", "-b", "dev/wip-matrix"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
2693
|
+
|
|
2694
|
+
(tmp_path / "mixed.txt").write_text("staged version\n", encoding="utf-8")
|
|
2695
|
+
subprocess.run(["git", "add", "mixed.txt"], cwd=tmp_path, check=True)
|
|
2696
|
+
(tmp_path / "mixed.txt").write_text("final unstaged version\n", encoding="utf-8")
|
|
2697
|
+
(tmp_path / "delete me.txt").unlink()
|
|
2698
|
+
subprocess.run(["git", "mv", "rename source.txt", "renamed [literal]*.txt"], cwd=tmp_path, check=True)
|
|
2699
|
+
(tmp_path / "new [literal]*.txt").write_text("new special\n", encoding="utf-8")
|
|
2700
|
+
|
|
2701
|
+
result = branch_ensure_return(tmp_path, "main", "dev/wip-matrix")
|
|
2702
|
+
|
|
2703
|
+
assert result.ok is True
|
|
2704
|
+
assert subprocess.run(
|
|
2705
|
+
["git", "branch", "--show-current"], cwd=tmp_path, check=True, stdout=subprocess.PIPE, text=True,
|
|
2706
|
+
).stdout.strip() == "main"
|
|
2707
|
+
assert subprocess.run(
|
|
2708
|
+
["git", "show", "dev/wip-matrix:mixed.txt"], cwd=tmp_path, check=True, stdout=subprocess.PIPE, text=True,
|
|
2709
|
+
).stdout == "final unstaged version\n"
|
|
2710
|
+
assert subprocess.run(
|
|
2711
|
+
["git", "show", "dev/wip-matrix:renamed [literal]*.txt"],
|
|
2712
|
+
cwd=tmp_path,
|
|
2713
|
+
check=True,
|
|
2714
|
+
stdout=subprocess.PIPE,
|
|
2715
|
+
text=True,
|
|
2716
|
+
).stdout == "rename me\n"
|
|
2717
|
+
assert subprocess.run(
|
|
2718
|
+
["git", "show", "dev/wip-matrix:new [literal]*.txt"],
|
|
2719
|
+
cwd=tmp_path,
|
|
2720
|
+
check=True,
|
|
2721
|
+
stdout=subprocess.PIPE,
|
|
2722
|
+
text=True,
|
|
2723
|
+
).stdout == "new special\n"
|
|
2724
|
+
task_tree = subprocess.run(
|
|
2725
|
+
["git", "ls-tree", "-r", "--name-only", "dev/wip-matrix"],
|
|
2726
|
+
cwd=tmp_path,
|
|
2727
|
+
check=True,
|
|
2728
|
+
stdout=subprocess.PIPE,
|
|
2729
|
+
text=True,
|
|
2730
|
+
).stdout.splitlines()
|
|
2731
|
+
assert "delete me.txt" not in task_tree
|
|
2732
|
+
assert "rename source.txt" not in task_tree
|
|
2733
|
+
|
|
2734
|
+
|
|
2735
|
+
def test_branch_return_rejects_unmerged_paths_without_commit_or_checkout(tmp_path):
|
|
2736
|
+
from prizmkit_runtime.gitops import branch_ensure_return
|
|
2737
|
+
|
|
2738
|
+
_init_merge_repo(tmp_path)
|
|
2739
|
+
subprocess.run(["git", "checkout", "-b", "dev/conflicted"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
2740
|
+
(tmp_path / "app.txt").write_text("task side\n", encoding="utf-8")
|
|
2741
|
+
subprocess.run(["git", "commit", "-am", "task side"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
2742
|
+
subprocess.run(["git", "checkout", "main"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
2743
|
+
(tmp_path / "app.txt").write_text("base side\n", encoding="utf-8")
|
|
2744
|
+
subprocess.run(["git", "commit", "-am", "base side"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
2745
|
+
subprocess.run(["git", "checkout", "dev/conflicted"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
2746
|
+
merge = subprocess.run(
|
|
2747
|
+
["git", "merge", "main"],
|
|
2748
|
+
cwd=tmp_path,
|
|
2749
|
+
check=False,
|
|
2750
|
+
stdout=subprocess.PIPE,
|
|
2751
|
+
stderr=subprocess.PIPE,
|
|
2752
|
+
text=True,
|
|
2753
|
+
)
|
|
2754
|
+
assert merge.returncode != 0
|
|
2755
|
+
head_before = subprocess.run(
|
|
2756
|
+
["git", "rev-parse", "HEAD"], cwd=tmp_path, check=True, stdout=subprocess.PIPE, text=True,
|
|
2757
|
+
).stdout.strip()
|
|
2758
|
+
|
|
2759
|
+
result = branch_ensure_return(tmp_path, "main", "dev/conflicted")
|
|
2760
|
+
|
|
2761
|
+
assert result.ok is False
|
|
2762
|
+
assert "unresolved Git paths" in result.results[-1].stderr
|
|
2763
|
+
assert subprocess.run(
|
|
2764
|
+
["git", "branch", "--show-current"], cwd=tmp_path, check=True, stdout=subprocess.PIPE, text=True,
|
|
2765
|
+
).stdout.strip() == "dev/conflicted"
|
|
2766
|
+
assert subprocess.run(
|
|
2767
|
+
["git", "rev-parse", "HEAD"], cwd=tmp_path, check=True, stdout=subprocess.PIPE, text=True,
|
|
2768
|
+
).stdout.strip() == head_before
|
|
2769
|
+
assert subprocess.run(
|
|
2770
|
+
["git", "ls-files", "-u"], cwd=tmp_path, check=True, stdout=subprocess.PIPE, text=True,
|
|
2771
|
+
).stdout.strip()
|
|
2772
|
+
|
|
2773
|
+
|
|
2774
|
+
def test_branch_save_wip_rejects_clean_recorded_branch_mismatch(tmp_path):
|
|
2775
|
+
from prizmkit_runtime.gitops import branch_save_wip
|
|
2776
|
+
|
|
2777
|
+
_init_merge_repo(tmp_path)
|
|
2778
|
+
subprocess.run(["git", "checkout", "-b", "dev/unrelated"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
2779
|
+
|
|
2780
|
+
result = branch_save_wip(tmp_path, "dev/recorded-task")
|
|
2781
|
+
|
|
2782
|
+
assert result.ok is False
|
|
2783
|
+
assert result.results[-1].stderr == "Active branch dev/unrelated does not match task branch dev/recorded-task"
|
|
2784
|
+
assert subprocess.run(
|
|
2785
|
+
["git", "branch", "--show-current"],
|
|
2786
|
+
cwd=tmp_path,
|
|
2787
|
+
check=True,
|
|
2788
|
+
stdout=subprocess.PIPE,
|
|
2789
|
+
text=True,
|
|
2790
|
+
).stdout.strip() == "dev/unrelated"
|
|
2610
2791
|
|
|
2611
2792
|
|
|
2612
2793
|
def test_branch_return_commit_failure_does_not_checkout_base(monkeypatch, tmp_path):
|
|
@@ -3104,3 +3285,142 @@ def test_daemon_status_cli_preserves_pure_json_stdout(tmp_path):
|
|
|
3104
3285
|
assert "Python feature daemon" not in result.stdout
|
|
3105
3286
|
assert "Transition command contract" not in result.stdout
|
|
3106
3287
|
|
|
3288
|
+
|
|
3289
|
+
def test_active_checkout_replacement_is_guarded_and_never_writes_reset_tombstone(tmp_path):
|
|
3290
|
+
from prizmkit_runtime.task_checkout import (
|
|
3291
|
+
TaskCheckout,
|
|
3292
|
+
TaskCheckoutError,
|
|
3293
|
+
load_task_checkout,
|
|
3294
|
+
persist_task_checkout,
|
|
3295
|
+
replace_active_task_checkout_locked,
|
|
3296
|
+
)
|
|
3297
|
+
|
|
3298
|
+
state_dir = tmp_path / "state"
|
|
3299
|
+
expected = TaskCheckout.active("feature", "F-001", "dev/F-001-old", "main")
|
|
3300
|
+
replacement = TaskCheckout.active("feature", "F-001", "dev/F-001-resume", "main")
|
|
3301
|
+
persist_task_checkout(state_dir, expected)
|
|
3302
|
+
|
|
3303
|
+
path = replace_active_task_checkout_locked(state_dir, expected, replacement)
|
|
3304
|
+
|
|
3305
|
+
assert path == state_dir / "F-001" / "checkout.json"
|
|
3306
|
+
loaded = load_task_checkout(state_dir, "feature", "F-001")
|
|
3307
|
+
assert loaded == replacement
|
|
3308
|
+
assert loaded is not None and loaded.is_reset is False
|
|
3309
|
+
with pytest.raises(TaskCheckoutError, match="changed before recovery replacement"):
|
|
3310
|
+
replace_active_task_checkout_locked(state_dir, expected, replacement)
|
|
3311
|
+
|
|
3312
|
+
|
|
3313
|
+
def test_checkpoint_rewind_preserves_planning_and_resets_implementation_downstream():
|
|
3314
|
+
from prizmkit_runtime.checkpoint_state import (
|
|
3315
|
+
rewind_checkpoint_for_implementation,
|
|
3316
|
+
validate_checkpoint_data,
|
|
3317
|
+
)
|
|
3318
|
+
|
|
3319
|
+
skills = [
|
|
3320
|
+
"prizmkit-plan",
|
|
3321
|
+
"prizmkit-implement",
|
|
3322
|
+
"prizmkit-code-review",
|
|
3323
|
+
"prizmkit-test",
|
|
3324
|
+
"prizmkit-retrospective",
|
|
3325
|
+
"prizmkit-committer",
|
|
3326
|
+
"completion-summary",
|
|
3327
|
+
]
|
|
3328
|
+
steps = []
|
|
3329
|
+
previous = None
|
|
3330
|
+
for index, skill in enumerate(skills, start=1):
|
|
3331
|
+
status = "pending"
|
|
3332
|
+
stage_result = None
|
|
3333
|
+
if skill == "prizmkit-plan":
|
|
3334
|
+
status, stage_result = "completed", "PLAN_READY"
|
|
3335
|
+
elif skill == "prizmkit-implement":
|
|
3336
|
+
status, stage_result = "completed", "IMPLEMENTED"
|
|
3337
|
+
elif skill == "prizmkit-code-review":
|
|
3338
|
+
status, stage_result = "failed", "REVIEW_NEEDS_FIXES"
|
|
3339
|
+
step = {
|
|
3340
|
+
"id": f"S{index:02d}",
|
|
3341
|
+
"skill": skill,
|
|
3342
|
+
"name": skill,
|
|
3343
|
+
"status": status,
|
|
3344
|
+
"depends_on": previous,
|
|
3345
|
+
"required_artifacts": [],
|
|
3346
|
+
}
|
|
3347
|
+
if stage_result:
|
|
3348
|
+
step["stage_result"] = stage_result
|
|
3349
|
+
steps.append(step)
|
|
3350
|
+
previous = step["id"]
|
|
3351
|
+
payload = {
|
|
3352
|
+
"version": 1,
|
|
3353
|
+
"workflow_type": "feature-pipeline",
|
|
3354
|
+
"steps": steps,
|
|
3355
|
+
"feature_state": {
|
|
3356
|
+
"schema_version": 1,
|
|
3357
|
+
"artifact_dir": ".prizmkit/specs/example",
|
|
3358
|
+
"orchestrator": "prizmkit-l4",
|
|
3359
|
+
"stage": "code-review",
|
|
3360
|
+
"current_stage": "prizmkit-code-review",
|
|
3361
|
+
"status": "failed",
|
|
3362
|
+
"stage_result": "REVIEW_NEEDS_FIXES",
|
|
3363
|
+
"repair_scope": "production",
|
|
3364
|
+
"repair_round": 2,
|
|
3365
|
+
"next_stage": "prizmkit-implement",
|
|
3366
|
+
"resume_from": "prizmkit-implement",
|
|
3367
|
+
"blocked_reason": "review_needs_fixes",
|
|
3368
|
+
"terminal_status": "WORKFLOW_BLOCKED",
|
|
3369
|
+
"completed_stages": ["plan", "implement"],
|
|
3370
|
+
},
|
|
3371
|
+
}
|
|
3372
|
+
|
|
3373
|
+
rewound = rewind_checkpoint_for_implementation(payload)
|
|
3374
|
+
|
|
3375
|
+
assert rewound is not payload
|
|
3376
|
+
assert rewound["steps"][0] == payload["steps"][0]
|
|
3377
|
+
assert all(step["status"] == "pending" for step in rewound["steps"][1:])
|
|
3378
|
+
assert all("stage_result" not in step for step in rewound["steps"][1:])
|
|
3379
|
+
semantic = rewound["feature_state"]
|
|
3380
|
+
assert semantic["stage"] == "implement"
|
|
3381
|
+
assert semantic["current_stage"] == "prizmkit-implement"
|
|
3382
|
+
assert semantic["status"] == "pending"
|
|
3383
|
+
assert semantic["repair_round"] == 0
|
|
3384
|
+
assert semantic["completed_stages"] == ["plan"]
|
|
3385
|
+
assert semantic["blocked_reason"] is None
|
|
3386
|
+
assert semantic["terminal_status"] is None
|
|
3387
|
+
assert "stage_result" not in semantic
|
|
3388
|
+
validated = validate_checkpoint_data(rewound)
|
|
3389
|
+
assert validated.valid is True
|
|
3390
|
+
assert validated.current_step is not None
|
|
3391
|
+
assert validated.current_step.skill == "prizmkit-implement"
|
|
3392
|
+
|
|
3393
|
+
|
|
3394
|
+
def test_git_recovery_helpers_create_branch_without_checkout_and_report_registration(tmp_path):
|
|
3395
|
+
from prizmkit_runtime.gitops import (
|
|
3396
|
+
create_branch_from_base,
|
|
3397
|
+
local_branch_exists,
|
|
3398
|
+
registered_worktrees,
|
|
3399
|
+
)
|
|
3400
|
+
|
|
3401
|
+
subprocess.run(["git", "init", "-b", "main"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
3402
|
+
subprocess.run(["git", "config", "user.email", "test@example.com"], cwd=tmp_path, check=True)
|
|
3403
|
+
subprocess.run(["git", "config", "user.name", "Test"], cwd=tmp_path, check=True)
|
|
3404
|
+
(tmp_path / "base.txt").write_text("base\n", encoding="utf-8")
|
|
3405
|
+
subprocess.run(["git", "add", "base.txt"], cwd=tmp_path, check=True)
|
|
3406
|
+
subprocess.run(["git", "commit", "-m", "base"], cwd=tmp_path, check=True, stdout=subprocess.DEVNULL)
|
|
3407
|
+
|
|
3408
|
+
assert local_branch_exists(tmp_path, "main") is True
|
|
3409
|
+
assert local_branch_exists(tmp_path, "missing") is False
|
|
3410
|
+
result = create_branch_from_base(tmp_path, "dev/F-001-resume", "main")
|
|
3411
|
+
|
|
3412
|
+
assert result.return_code == 0
|
|
3413
|
+
assert local_branch_exists(tmp_path, "dev/F-001-resume") is True
|
|
3414
|
+
assert subprocess.run(
|
|
3415
|
+
["git", "branch", "--show-current"],
|
|
3416
|
+
cwd=tmp_path,
|
|
3417
|
+
check=True,
|
|
3418
|
+
stdout=subprocess.PIPE,
|
|
3419
|
+
text=True,
|
|
3420
|
+
).stdout.strip() == "main"
|
|
3421
|
+
registrations = registered_worktrees(tmp_path)
|
|
3422
|
+
assert any(
|
|
3423
|
+
registration.path == tmp_path.resolve() and registration.branch == "main"
|
|
3424
|
+
for registration in registrations
|
|
3425
|
+
)
|
|
3426
|
+
|
|
@@ -36,18 +36,18 @@ If you believe the task is better suited for a different workflow, you MUST:
|
|
|
36
36
|
**Your ONLY writable outputs are:**
|
|
37
37
|
1. `.prizmkit/plans/project-brief.md` (`.prizmkit/plans/` — accumulated project context brief)
|
|
38
38
|
2. `.prizmkit/plans/project-brief.draft.md` — working source during planning and saved draft when the user exits before completion
|
|
39
|
-
3.
|
|
40
|
-
4.
|
|
41
|
-
5. `.prizmkit/
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
-
|
|
48
|
-
-
|
|
49
|
-
- `
|
|
50
|
-
-
|
|
39
|
+
3. The resolved project instruction target for the current AI host (with user consent) — only project conventions, infrastructure configuration (database conventions, deployment config, **cloud services**), frontend design direction, and architecture decisions
|
|
40
|
+
4. `.prizmkit/rules/<layer>-rules.md` — per-layer development rules generated by Rules Configuration
|
|
41
|
+
5. `.prizmkit/prizm-docs/root.prizm` — `RULES:` pointer line only (not other root.prizm content; created minimally if absent)
|
|
42
|
+
|
|
43
|
+
**Current-host project instruction target resolution**:
|
|
44
|
+
- Identify the current AI host from direct runtime/session evidence and the host's own project conventions. Installed project resources, existing project files, and `.prizmkit/manifest.json` are supporting evidence only; they are not a platform identity or path allowlist and must not override stronger current-session evidence.
|
|
45
|
+
- Resolve the host's conventional main project-level AI instruction/context file: a project-local, durable file that the current host loads or reads to guide AI work in this project.
|
|
46
|
+
- Named paths such as `AGENTS.md`, `CLAUDE.md`, `CODEBUDDY.md`, and `.pi/APPEND_SYSTEM.md` are non-exhaustive examples only. They do not define a platform-to-path mapping or a compliance condition.
|
|
47
|
+
- The target must stay inside the project, represent the current host's main user-facing project instruction context, and must not be a private or framework-protocol-only import file.
|
|
48
|
+
- If the conventional target is absent, create it only when current-host evidence establishes one unambiguous conventional project path and the user consents to the write.
|
|
49
|
+
- If no target can be identified confidently or multiple plausible targets remain, use `AskUserQuestion` to confirm the intended project instruction file before writing. Never guess, and never treat semantic detection as permission to write an arbitrary file.
|
|
50
|
+
- Resolve the target once before project-convention discovery and reuse the same resolved target for conventions, infrastructure, frontend design direction, architecture decisions, prerequisites, checkpoints, generated-plan review, and completion reporting. Do not mirror content to every installed host merely because multiple platform resources exist.
|
|
51
51
|
|
|
52
52
|
**After planning is complete**, you MUST:
|
|
53
53
|
1. Present the summary of captured project-level context (vision, conventions, architecture decisions, project brief)
|
|
@@ -130,7 +130,7 @@ Do NOT use this skill when:
|
|
|
130
130
|
- During brainstorm Phase C → also read `${SKILL_DIR}/references/red-team-checklist.md`
|
|
131
131
|
|
|
132
132
|
3. **Project conventions discovery** — after Intent Confirmation, before brainstorm or vision work:
|
|
133
|
-
→
|
|
133
|
+
→ Resolve the current host's project instruction target through **Current-host project instruction target resolution**, then read that exact target and check for a `### Project Conventions` section
|
|
134
134
|
→ If section exists and covers the project well → skip silently
|
|
135
135
|
→ If section is missing or incomplete → run the **AI-driven convention discovery** below:
|
|
136
136
|
|
|
@@ -138,14 +138,14 @@ Do NOT use this skill when:
|
|
|
138
138
|
|
|
139
139
|
→ Read `${SKILL_DIR}/references/project-conventions-discovery.md` for the full AI-driven discovery procedure (Analyze → Reason → Present via AskUserQuestion).
|
|
140
140
|
|
|
141
|
-
→ Normalize each proposed convention into a standalone domain rule, run the Pre-Generation Headless Context Completeness Gate, then save only passing convention meanings to
|
|
141
|
+
→ Normalize each proposed convention into a standalone domain rule, run the Pre-Generation Headless Context Completeness Gate, then save only passing convention meanings to the resolved project instruction target under `### Project Conventions` (one bullet per convention; no raw answers or planning provenance)
|
|
142
142
|
→ Output format will naturally vary per project — that is the intended behavior
|
|
143
143
|
|
|
144
144
|
**Infrastructure Convention Discovery (Database + Deployment)**
|
|
145
145
|
|
|
146
|
-
After project conventions are captured, check the project instruction
|
|
146
|
+
After project conventions are captured, check the resolved project instruction target for `### Infrastructure` section status. Read `${SKILL_DIR}/references/infrastructure-convention-discovery.md` for the full database (6 topics — table naming, field naming, migrations, primary keys, indexes, environment separation), deployment (4 topics — target refinement, existing infrastructure, AI-assisted deploy, env var management), and cloud services (2-round AskUserQuestion) inquiry procedures. Follow the inquiry flow there — use `AskUserQuestion` for each batch. Each question MUST include a "Skip — decide later" option.
|
|
147
147
|
|
|
148
|
-
After inquiry, run the Pre-Generation Headless Context Completeness Gate on every proposed infrastructure decision, then update the `### Infrastructure` section in the project instruction
|
|
148
|
+
After inquiry, run the Pre-Generation Headless Context Completeness Gate on every proposed infrastructure decision, then update the `### Infrastructure` section in the resolved project instruction target with passing content (see reference file for full output format). Items still marked "Skip — decide later" remain as `<!-- [topic]: deferred -->` for `prizmkit-deploy` to pick up later.
|
|
149
149
|
|
|
150
150
|
4. **Project brief accumulation** — throughout all interactive phases:
|
|
151
151
|
→ Read `${SKILL_DIR}/references/project-brief-guide.md` for template and rules
|
|
@@ -168,8 +168,8 @@ Proceed to Prerequisites and continue the app-planner workflow.
|
|
|
168
168
|
Before questions, check optional context files (never block if absent):
|
|
169
169
|
- `.prizmkit/prizm-docs/root.prizm` (architecture/project context)
|
|
170
170
|
- `.prizmkit/config.json` (existing stack preferences and detected tech stack)
|
|
171
|
-
-
|
|
172
|
-
-
|
|
171
|
+
- Resolved project instruction target `### Project Conventions` section (previously answered project conventions)
|
|
172
|
+
- Resolved project instruction target `### Infrastructure` section (database and deployment config from prizmkit-init or previous app-planner run)
|
|
173
173
|
|
|
174
174
|
**Tech stack auto-population from config.json:**
|
|
175
175
|
- If `.prizmkit/config.json` contains a `tech_stack` object, use it to pre-fill tech assumptions.
|
|
@@ -287,8 +287,8 @@ Checkpoints catch cascading errors early — skipping one means the next phase b
|
|
|
287
287
|
| Checkpoint | Artifact/State | Criteria | Phase |
|
|
288
288
|
|-----------|----------------|----------|-------|
|
|
289
289
|
| **CP-AP-0** | Intent Confirmed | User confirmed session goal (produce / explore) | 1 |
|
|
290
|
-
| **CP-AP-1** | Conventions Checked |
|
|
291
|
-
| **CP-AP-1.5** | Infrastructure Checked |
|
|
290
|
+
| **CP-AP-1** | Conventions Checked | Current-host project instruction target resolved; its `### Project Conventions` section is loaded or asked and up to date | 1 |
|
|
291
|
+
| **CP-AP-1.5** | Infrastructure Checked | Resolved project instruction target `### Infrastructure` section addressed — database, deployment **and cloud services** each configured or explicitly deferred | 1-2 |
|
|
292
292
|
| **CP-AP-1.6** | Rules Configured | For each detected layer (from config.json or self-detection), rules file exists in `.prizmkit/rules/` or user explicitly skipped. `root.prizm` `RULES:` pointer up-to-date. | 2 |
|
|
293
293
|
| **CP-AP-2** | Vision Summary | Goal/users/differentiators confirmed by user. For brownfield: existing purpose confirmed or refined. | 1-2 |
|
|
294
294
|
| **CP-AP-3** | Frontend Design Evaluated | For frontend projects: checked for existing UI/UX design system; user was asked if missing. **Auto-pass** for backend-only or non-UI projects. | 2 |
|
|
@@ -302,10 +302,10 @@ Checkpoints catch cascading errors early — skipping one means the next phase b
|
|
|
302
302
|
Run this gate before any final handoff whenever the current `app-planner` session wrote or rewrote final app-level planning content. In full produce mode this runs after CP-AP-5.5 and the passing content's final writes; in Quick Context Mode it runs after the confirmed `.prizmkit/plans/project-brief.md` write; in conventions, infrastructure, architecture-decision, or rules-only final-write flows it runs after those writes and before the completion summary. This gate reviews generated planning artifacts directly, not implementation diffs.
|
|
303
303
|
|
|
304
304
|
1. **Load the local reference**: read `${SKILL_DIR}/references/generated-plan-review.md` and follow it as the source of truth for app-planner generated content review.
|
|
305
|
-
2. **Read actual planning artifacts directly**: inspect current-session final outputs such as `.prizmkit/plans/project-brief.md`,
|
|
305
|
+
2. **Read actual planning artifacts directly**: inspect current-session final outputs such as `.prizmkit/plans/project-brief.md`, changed sections in the resolved project instruction target, `.prizmkit/rules/<layer>-rules.md`, and `.prizmkit/prizm-docs/root.prizm` `RULES:` pointer line. Do not rely on `git status`, `git diff`, or `git diff --cached`, because `.prizmkit` planning artifacts are often gitignored or untracked.
|
|
306
306
|
3. **Scope to new/changed app-planning content**: compare against pre-session snapshots or in-memory drafts when available. Preserve unchanged historical brief items, instruction-file sections, rules files, and Prizm doc content. For explore-only sessions, draft-save exits, or flows with no new final planning content, report: `Local generated-plan review: not applicable — no new final planning content written.`
|
|
307
307
|
4. **Run the local checklist and fresh-session simulation** from the reference: project brief completeness, consistency of conventions/infrastructure/architecture decisions, rules pointer consistency, normalization of selected decisions, purpose-tagged references, app-planner writable-boundary compliance, and downstream `feature-planner` readiness to produce `.prizmkit/plans/feature-list.json` without guessing or seeing the original conversation.
|
|
308
|
-
5. **Apply accepted fixes through the source/writer path**: project brief fixes go through the draft/checklist source, instruction-file section fixes go through the
|
|
308
|
+
5. **Apply accepted fixes through the source/writer path**: project brief fixes go through the draft/checklist source, instruction-file section fixes go through the resolved project instruction target's section source with user confirmation when changing approved wording, rules fixes re-render from rules configuration answers/templates when available, and `root.prizm` fixes are limited to the `RULES:` pointer line.
|
|
309
309
|
6. **Recheck changed sections directly** after every accepted fix batch.
|
|
310
310
|
7. **Report CP-AP-6** in the completion summary: include local generated-plan review verdict, reviewed app-planning artifacts/sections, accepted fixes (or "none"), and final writer/validation result.
|
|
311
311
|
|
|
@@ -314,8 +314,8 @@ Run this gate before any final handoff whenever the current `app-planner` sessio
|
|
|
314
314
|
After Phase 2, if framework-shaping architecture decisions emerged during planning (tech stack, communication patterns, data model strategies — not individual feature details), read `${SKILL_DIR}/references/architecture-decisions.md` and follow the capture flow. Most sessions will NOT produce architecture decisions — only capture when genuinely impactful.
|
|
315
315
|
|
|
316
316
|
**How it works**:
|
|
317
|
-
1. If decisions are captured → run the Pre-Generation Headless Context Completeness Gate on each decision, then append passing decisions to
|
|
318
|
-
2. Downstream skills (feature-planner, prizmkit-plan, etc.) read
|
|
317
|
+
1. If decisions are captured → run the Pre-Generation Headless Context Completeness Gate on each decision, then append passing decisions to the resolved project instruction target under `### Architecture Decisions`
|
|
318
|
+
2. Downstream skills (feature-planner, prizmkit-plan, etc.) read the current host's project instruction context, so they automatically receive these decisions
|
|
319
319
|
3. Do NOT write architecture decision content directly to `.prizmkit/prizm-docs/root.prizm` — that file is maintained by `prizmkit-prizm-docs` and `prizmkit-retrospective`. (The `RULES:` pointer line is managed by Rules Configuration, not by this section.) If the project needs `.prizmkit/prizm-docs/`, recommend the user run `prizmkit-prizm-docs` init after planning.
|
|
320
320
|
|
|
321
321
|
## Project Brief Accumulation
|
|
@@ -348,10 +348,10 @@ When the session appears to be ending:
|
|
|
348
348
|
After CP-AP-6 passes or is reported not applicable, present a summary and end the session:
|
|
349
349
|
|
|
350
350
|
1. **Summary** (as text): List all project-level artifacts produced:
|
|
351
|
-
- Project conventions →
|
|
352
|
-
- Infrastructure config →
|
|
351
|
+
- Project conventions → resolved project instruction target `### Project Conventions`
|
|
352
|
+
- Infrastructure config → resolved project instruction target `### Infrastructure` (database conventions + deployment config)
|
|
353
353
|
- Tech stack assumptions → captured in `.prizmkit/plans/project-brief.md` and reused from `.prizmkit/config.json` when it already exists
|
|
354
|
-
- Architecture decisions (if any) →
|
|
354
|
+
- Architecture decisions (if any) → resolved project instruction target `### Architecture Decisions`
|
|
355
355
|
- Dev rules (if configured) → `.prizmkit/rules/<layer>-rules.md` (with `root.prizm` `RULES:` pointer)
|
|
356
356
|
- Project brief → `.prizmkit/plans/project-brief.md`
|
|
357
357
|
- Local generated-plan review → verdict, reviewed app-planning artifacts/sections, accepted fixes (or "none"), and final writer/validation result
|
|
@@ -24,15 +24,9 @@ After Phase 2 (Confirm constraints and tech assumptions), before Phase 3 (Captur
|
|
|
24
24
|
|
|
25
25
|
## How to Capture
|
|
26
26
|
|
|
27
|
-
1. **
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
- `claude` → append to `CLAUDE.md`
|
|
31
|
-
- `codebuddy` → append to `CODEBUDDY.md`
|
|
32
|
-
- `all` → append to all three files.
|
|
33
|
-
- If no platform can be determined, skip (no project instruction file).
|
|
34
|
-
|
|
35
|
-
2. **Check for existing section** — read the target file and look for `### Architecture Decisions` heading:
|
|
27
|
+
1. **Use the resolved project instruction target** — consume the exact current-host target already selected by `SKILL.md` before project-convention discovery. Do not redetect the host, reinterpret manifest identity, or choose from named platform files inside this reference. If the target was not resolved, return to the parent Skill's current-host resolution rule; ask the user when evidence is missing or ambiguous rather than guessing or silently skipping.
|
|
28
|
+
|
|
29
|
+
2. **Check for existing section** — read the resolved target and look for `### Architecture Decisions` heading:
|
|
36
30
|
- If heading exists → append new entries below it (avoid duplicates with existing entries)
|
|
37
31
|
- If heading does not exist → create it at the end of the file
|
|
38
32
|
|
|
@@ -44,7 +38,7 @@ After Phase 2 (Confirm constraints and tech assumptions), before Phase 3 (Captur
|
|
|
44
38
|
- Monorepo structure: shared types between frontend and backend
|
|
45
39
|
```
|
|
46
40
|
|
|
47
|
-
4. **User confirmation** — before writing, show the collected decisions and ask:
|
|
48
|
-
> "These architecture decisions were identified during planning. Record them
|
|
41
|
+
4. **User confirmation** — before writing, show the collected decisions and the exact resolved target path, then ask:
|
|
42
|
+
> "These architecture decisions were identified during planning. Record them in `<resolved-project-instruction-target>`? (Y/n)"
|
|
49
43
|
|
|
50
44
|
If user declines, skip without further prompting.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> Create distinctive, production-grade frontend interfaces that avoid generic "AI slop" aesthetics.
|
|
4
4
|
|
|
5
|
-
**app-planner context**: In the planning phase, use this guide to establish **design direction decisions** (aesthetic tone, typography approach, color strategy, layout philosophy). Do NOT produce CSS, code, or implementation artifacts — capture the design direction
|
|
5
|
+
**app-planner context**: In the planning phase, use this guide to establish **design direction decisions** (aesthetic tone, typography approach, color strategy, layout philosophy). Do NOT produce CSS, code, or implementation artifacts — capture the design direction in the current session's resolved project instruction target selected by `SKILL.md`. Do not redetect a platform or choose a named file in this reference. Downstream implementation skills will consume these decisions when building features.
|
|
6
6
|
|
|
7
7
|
Load this guide **only when the feature involves frontend/UI work**. Skip for backend-only or infrastructure features.
|
|
8
8
|
|
|
@@ -14,7 +14,7 @@ Planning artifacts may be gitignored, untracked, or absent from staged and unsta
|
|
|
14
14
|
|
|
15
15
|
- Project brief: `.prizmkit/plans/project-brief.md`
|
|
16
16
|
- Draft brief when present: `.prizmkit/plans/project-brief.draft.md`
|
|
17
|
-
-
|
|
17
|
+
- The exact project instruction target selected for the current session, limited to sections written or changed by app-planner
|
|
18
18
|
- Generated rule files: `.prizmkit/rules/<layer>-rules.md`
|
|
19
19
|
- Prizm docs root pointer: `.prizmkit/prizm-docs/root.prizm` `RULES:` line only
|
|
20
20
|
- In-memory answers, draft brief data, rules configuration answers, or section snapshots captured before writes
|
|
@@ -32,7 +32,7 @@ Do not rely on `git status`, `git diff`, or `git diff --cached` to decide whethe
|
|
|
32
32
|
## Source-of-Truth Map
|
|
33
33
|
|
|
34
34
|
- `project-brief.md` fixes go through the draft/checklist project brief source representation described in `project-brief-guide.md`, then rewrite the final brief through the existing project brief writer path.
|
|
35
|
-
- Project conventions, infrastructure, and architecture decisions are source sections in
|
|
35
|
+
- Project conventions, infrastructure, frontend design direction, and architecture decisions are source sections in the exact project instruction target selected for the current session. If a fix changes user-approved wording or meaning, ask for user confirmation before rewriting.
|
|
36
36
|
- `.prizmkit/rules/<layer>-rules.md` fixes should re-render from the rules configuration answers and template inputs when available. If no structured source remains, update the source rule section explicitly and report that source path.
|
|
37
37
|
- `root.prizm` fixes are limited to correcting or adding the `RULES:` pointer line; do not rewrite unrelated root content.
|
|
38
38
|
|
|
@@ -4,7 +4,7 @@ Detailed Q&A flows for database, deployment, and cloud services conventions duri
|
|
|
4
4
|
|
|
5
5
|
## Infrastructure Section Check
|
|
6
6
|
|
|
7
|
-
Check
|
|
7
|
+
Check the current session's resolved project instruction target for a `### Infrastructure` section. Consume the exact target selected by `SKILL.md`; do not redetect a platform or choose a named file in this reference:
|
|
8
8
|
|
|
9
9
|
- If `### Infrastructure` section does not exist → this project was not initialized with prizmkit-init's Phase 4.6. Treat as if both database and deployment are undecided — run full inquiry below.
|
|
10
10
|
- If `<!-- infrastructure: deferred -->` → user explicitly skipped at init time. Ask: "During project init you deferred infrastructure decisions. Would you like to configure them now?" (options: "Yes — configure now (Recommended)", "Skip — decide later")
|
|
@@ -105,4 +105,4 @@ After infrastructure inquiry, update the `### Infrastructure` section in the pro
|
|
|
105
105
|
<!-- If user picked "None" in Round 1, replace this block with: cloud-services: none -->
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
-
Items still marked "Skip — decide later" remain as `<!-- [topic]: deferred -->` in the
|
|
108
|
+
Items still marked "Skip — decide later" remain as `<!-- [topic]: deferred -->` in the resolved project instruction target for `prizmkit-deploy` to pick up later.
|
|
@@ -55,5 +55,5 @@ Then ask in text: "Anything I missed that you'd like to standardize?" — if the
|
|
|
55
55
|
|
|
56
56
|
## After Discovery
|
|
57
57
|
|
|
58
|
-
→ Save resolved convention meanings to
|
|
58
|
+
→ Save resolved convention meanings to the current session's resolved project instruction target under `### Project Conventions` (one standalone domain rule per bullet). Consume the target selected by `SKILL.md`; do not redetect a platform or choose a named file in this reference. Do not copy raw answers, option labels, recommendation markers, question IDs, or dialogue provenance.
|
|
59
59
|
→ Output format will naturally vary per project — that is the intended behavior
|