prizmkit 1.1.102 → 1.1.105

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.
Files changed (23) hide show
  1. package/bundled/VERSION.json +3 -3
  2. package/bundled/dev-pipeline/README.md +6 -7
  3. package/bundled/dev-pipeline/assets/prizm-dev-team-integration.md +3 -3
  4. package/bundled/dev-pipeline/prizmkit_runtime/daemon.py +1 -1
  5. package/bundled/dev-pipeline/prizmkit_runtime/runner_models.py +4 -2
  6. package/bundled/dev-pipeline/prizmkit_runtime/runners.py +357 -157
  7. package/bundled/dev-pipeline/prizmkit_runtime/sessions.py +146 -7
  8. package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +37 -28
  9. package/bundled/dev-pipeline/scripts/generate-recovery-prompt.py +4 -3
  10. package/bundled/dev-pipeline/templates/bootstrap-tier2.md +62 -38
  11. package/bundled/dev-pipeline/templates/bootstrap-tier3.md +57 -49
  12. package/bundled/dev-pipeline/templates/sections/critical-paths-agent.md +0 -1
  13. package/bundled/dev-pipeline/templates/sections/critical-paths-full.md +0 -1
  14. package/bundled/dev-pipeline/templates/sections/directory-convention-agent.md +2 -2
  15. package/bundled/dev-pipeline/templates/sections/directory-convention-full.md +2 -2
  16. package/bundled/dev-pipeline/templates/sections/phase-implement-agent.md +1 -0
  17. package/bundled/dev-pipeline/templates/sections/phase-implement-full.md +1 -0
  18. package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +46 -0
  19. package/bundled/dev-pipeline/tests/test_python_runner_parity.py +118 -10
  20. package/bundled/dev-pipeline/tests/test_unified_cli.py +76 -0
  21. package/bundled/skills/_metadata.json +1 -1
  22. package/package.json +1 -1
  23. package/bundled/dev-pipeline/templates/agent-prompts/dev-implement.md +0 -71
@@ -1,4 +1,4 @@
1
- # Dev-Pipeline Session Bootstrap — Tier 3 (Full Team)
1
+ # Dev-Pipeline Session Bootstrap — Tier 3 (Orchestrator + Critic/Reviewer)
2
2
 
3
3
  ## Session Context
4
4
 
@@ -10,14 +10,14 @@
10
10
 
11
11
  You are the **session orchestrator**. Implement Feature {{FEATURE_ID}}: "{{FEATURE_TITLE}}".
12
12
 
13
- **CRITICAL**: You MUST NOT exit until ALL work is complete and committed. When you spawn normal work subagents, wait for each to finish (run_in_background=false). Do NOT spawn work agents in background and exit — that kills the session.
13
+ **CRITICAL**: You MUST NOT exit until ALL work is complete and committed. When you spawn Critic or Reviewer agents, wait for each to finish (run_in_background=false). Do NOT spawn agents in background and exit — that kills the session.
14
14
 
15
- **Tier 3 — Full Team**: For complex features, use the full pipeline (Phase 0–6) with Dev + Reviewer agents spawned via the Agent tool.
15
+ **Tier 3 — Orchestrator + Critic/Reviewer**: For complex features, the main orchestrator handles context, planning, and implementation directly. Critic challenges the plan and Reviewer reviews. Do not spawn a top-level Dev implementation agent.
16
16
 
17
17
  **Agent spawn failure policy (all Agent tool calls)**:
18
- - If spawning Dev, Reviewer, or Critic fails with team/config/lock errors, retry at most once.
18
+ - If spawning Reviewer or Critic fails with team/config/lock errors, retry at most once.
19
19
  - If the second attempt fails, do not keep spawning variants and do not enter artifact polling for Implementation Log, challenge report, or review report markers.
20
- - Use the documented inline/recovery fallback for that phase: write the required report yourself where possible, complete remaining Dev work directly in the orchestrator when safe, or write `failure-log.md` with the spawn error and last observable state before stopping for recovery.
20
+ - Use the documented inline/recovery fallback for that phase: write the required report yourself where possible, complete remaining work directly in the orchestrator when safe, or write `failure-log.md` with the spawn error and last observable state before stopping for recovery.
21
21
  - Apply the same cap to any re-spawn for report repair or resume prompts; do not burn multiple minutes on identical team/config/lock failures.
22
22
 
23
23
  ### Feature Description
@@ -71,12 +71,12 @@ You are running in **headless non-interactive mode** with a FINITE context windo
71
71
  ## PrizmKit Directory Convention
72
72
 
73
73
  ```
74
- .prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md ← orchestrator writes Sections 1-4; Dev appends Implementation Log; Reviewer appends Review Notes
74
+ .prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md ← orchestrator writes Sections 1-4 + Implementation Log; Reviewer appends Review Notes
75
75
  .prizmkit/specs/{{FEATURE_SLUG}}/spec.md
76
76
  .prizmkit/specs/{{FEATURE_SLUG}}/plan.md ← includes Tasks section
77
77
  ```
78
78
 
79
- **`context-snapshot.md`** is the shared knowledge base. Orchestrator writes Sections 1-4; Dev appends Implementation Log; Reviewer appends Review Notes. This eliminates redundant I/O across all agents.
79
+ **`context-snapshot.md`** is the shared knowledge base. Orchestrator writes Sections 1-4 and the Implementation Log; Reviewer appends Review Notes. This eliminates redundant I/O across review and orchestration phases.
80
80
 
81
81
  ---
82
82
 
@@ -268,55 +268,64 @@ After all critics return, read all 3 reports:
268
268
  **CP-2.5**: Plan challenges reviewed and resolved.
269
269
  {{END_IF_CRITIC_ENABLED}}
270
270
 
271
- ### Phase 4: Implement — Dev Agent
271
+ ### Implement — Orchestrator Direct (no Dev subagent)
272
272
 
273
- **Build artifacts rule** (passed to Dev): After any build/compile command (`go build`, `npm run build`, `tsc`, etc.), ensure the output binary or build directory is in `.gitignore`. Never commit compiled binaries, build output, or generated artifacts.
273
+ **Rationale**: Development is deterministic plan execution the orchestrator already built full context in Phase 1-2. Spawning a Dev subagent forces it to rebuild context (re-reading files the orchestrator already read), wastes context, and risks worktree double-edit. The orchestrator implements directly.
274
274
 
275
- Before spawning Dev, check plan.md Tasks section:
275
+ Before starting, check plan.md Tasks:
276
276
  ```bash
277
277
  grep -c '^\- \[ \]' .prizmkit/specs/{{FEATURE_SLUG}}/plan.md 2>/dev/null || true
278
278
  ```
279
- - If result is `0` (all tasks already `[x]`) → **SKIP Phase 4**, go directly to Phase 5. Do NOT spawn Dev.
280
- - If result is non-zero → spawn Dev agent below.
279
+ - If result is `0` (all tasks already `[x]`) → **SKIP to Review**.
280
+ - If non-zero → implement directly below.
281
281
 
282
- Spawn Dev agent (Agent tool, subagent_type="prizm-dev-team-dev", run_in_background=false).
282
+ **Build artifacts rule**: After any build/compile command, ensure output is in `.gitignore`. Never commit binaries/build output.
283
283
 
284
- Spawn failure cap: for team/config/lock errors, retry at most once for this Dev spawn. If the second attempt fails, do not poll for `## Implementation Log`; write `failure-log.md` and either implement remaining tasks directly in the orchestrator or stop for recovery.
285
-
286
- Prompt:
287
- > "Read {{DEV_SUBAGENT_PATH}}. Implement feature {{FEATURE_ID}} (slug: {{FEATURE_SLUG}}).
288
- > **IMPORTANT**: Read `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md` FIRST — Section 3 has Prizm Context (TRAPS/RULES), Section 4 has File Manifest with paths and interfaces.
289
- > ⚠️ DO NOT re-read source files already listed in Section 4 File Manifest unless you need implementation detail beyond the interface summary.
290
- > 1. Read `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md` for full context.
291
- > 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}}`.
292
- > 3. If plan.md has more than 5 tasks: update durable checkpoints/artifacts after every 3 tasks, minimize output, and rely on runner continuation if context overflow occurs. In an interactive Claude Code session operated manually outside the headless pipeline, `/compact` may be used as an optional convenience only.
293
- > 4. **Verification Gate Self-Check (BLOCKING)**: before returning, write a `### Gate Evidence` section to Implementation Log — one evidence line per gate (test name / file:line / fixture path). Any gate without evidence is BLOCKED, not success.
294
- > 5. **Layered test strategy**: debug with single-file tests (`npx vitest run <file> -t '<name>'`), only run full suite as gate when single-file is green. Never run full suite while a single-file test is still red.
295
- > 6. **Anti re-read**: 20-step window (full Read forbidden within 20 tool calls of last read), 3-strike (3 full Reads same file → BLOCKED). Use `grep -n`+`sed -n` for specific lines.
296
- > 7. **Read offset safety**: if Read returns "shorter than offset"/"empty"/"wasted" — run `wc -l` then `sed -n`, do NOT retry same offset. 2 consecutive → STOP Reading that file.
297
- > 8. **Hard Round Cap**: after 6 total test-fix rounds, STOP trial-and-error. Write failure-log. Switch to read-once-then-rewrite strategy. Do NOT exceed 7 rounds.
298
- > 9. After implement completes, verify the '## Implementation Log' section (with Gate Evidence) was written to context-snapshot.md.
299
- > 10. Do NOT execute any git commands (no git add/commit/reset/push).
300
- > Do NOT exit until all tasks are [x] and the '## Implementation Log' section (with Gate Evidence) is written in context-snapshot.md."
301
-
302
- **Gate Check — Implementation Log**:
303
- After Dev agent returns, verify the Implementation Log was written:
284
+ **3a.** Detect test commands and record baseline:
304
285
  ```bash
305
- grep -q "## Implementation Log" .prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md && echo "GATE:PASS" || echo "GATE:MISSING"
286
+ ($TEST_CMD) 2>&1 | tee /tmp/test-baseline.txt | tail -20
287
+ ```
288
+ Save pre-existing failing tests as `BASELINE_FAILURES`.
289
+
290
+ **3b.** Run `/prizmkit-implement` directly — you (the orchestrator) execute the full cycle:
291
+ - Read plan.md Tasks from `.prizmkit/specs/{{FEATURE_SLUG}}/`
292
+ - Use context-snapshot.md Section 4 File Manifest for targeted reads — do NOT re-read files already summarized there
293
+ - Implements task-by-task, marking each `[x]` immediately
294
+ - Creates/updates L2 `.prizm` docs when creating new modules
295
+ - Defers scoped/full test execution until after the code-review loop completes
296
+ - If plan.md has >5 tasks: update checkpoints after every 3 tasks
297
+
298
+ **3c. Focused checks only (no test gate here)**:
299
+ - During implementation, do not run mandatory periodic or full-suite tests.
300
+ - If a tiny targeted check is needed to understand a local failure, run only the smallest relevant command and capture output to `/tmp/test-out.txt`.
301
+ - The scoped feature test gate runs after code review; do not declare implementation success based on an early full-suite run.
302
+
303
+ **3d. Verification Gate Self-Check (BLOCKING — before declaring implement done)**:
304
+ For each Verification Gate from the Task Contract, write one evidence line to Implementation Log:
305
+ ```
306
+ ### Gate Evidence
307
+ - [x] G1: <gate text> — Evidence: <test name / file:line / fixture path>
308
+ - [ ] G3: <gate text> — BLOCKED: <reason> ← no evidence = BLOCKED, not success
306
309
  ```
307
- If GATE:MISSING — send message to Dev (re-spawn if needed): "Write the '## Implementation Log' section to context-snapshot.md before I can proceed to review. Include: files changed/created, key decisions, deviations from plan, notable discoveries."
308
310
 
309
- Wait for Dev to return. **If Dev times out before all tasks are `[x]`**:
310
- 1. Check progress: `grep -c '^\- \[ \]' .prizmkit/specs/{{FEATURE_SLUG}}/plan.md`
311
- 2. If any tasks remain: re-spawn Dev with this recovery prompt:
312
- > "Read {{DEV_SUBAGENT_PATH}}. You are resuming implementation of feature {{FEATURE_ID}} (slug: {{FEATURE_SLUG}}).
313
- > 1. Read `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md` Section 4 has File Manifest, 'Implementation Log' (if present) shows what was already done.
314
- > 2. Run `git diff HEAD` to see actual code changes already made.
315
- > 3. Run `/prizmkit-implement` to complete the remaining `[ ]` tasks. Run tests with: `{{TEST_CMD}}`.
316
- > 4. Do NOT execute any git commands."
317
- 3. Max 2 recovery retries. After 2 failures, orchestrator implements remaining tasks directly.
311
+ **3e.** Append `## Implementation Log` (with Gate Evidence) to `context-snapshot.md`:
312
+ - files changed/created
313
+ - key decisions
314
+ - deviations from plan
315
+ - test results
316
+ - Gate Evidence section
317
+ - unresolved blockers
318
+
319
+ **CP-2**: All tasks `[x]`, Implementation Log written with Gate Evidence, and blocked gates documented in `failure-log.md`. Test execution is deferred until after code review.
320
+
321
+ **Checkpoint update**:
322
+ ```bash
323
+ python3 $PIPELINE_DIR/scripts/update-checkpoint.py \
324
+ --checkpoint-path .prizmkit/specs/{{FEATURE_SLUG}}/workflow-checkpoint.json \
325
+ --step prizmkit-implement \
326
+ --status completed
327
+ ```
318
328
 
319
- All tasks `[x]`, tests pass.
320
329
 
321
330
  ### Scoped Feature Test Gate — PrizmKit Test
322
331
 
@@ -618,7 +627,6 @@ Rules for writing completion notes:
618
627
  | Feature Artifacts Dir | `.prizmkit/specs/{{FEATURE_SLUG}}/` |
619
628
  | Context Snapshot | `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md` |
620
629
  | Team Config | `{{TEAM_CONFIG_PATH}}` |
621
- | Dev Agent Def | {{DEV_SUBAGENT_PATH}} |
622
630
  | Reviewer Agent Def | {{REVIEWER_SUBAGENT_PATH}} |
623
631
  {{IF_CRITIC_ENABLED}}
624
632
  | Critic Agent Def | {{CRITIC_SUBAGENT_PATH}} |
@@ -645,10 +653,10 @@ If you encounter an unrecoverable error, context overflow, or are about to exit
645
653
 
646
654
  ## Reminders
647
655
 
648
- - Tier 3: full team Dev (implementation) Reviewer (review) — spawn agents directly via Agent tool
649
- - context-snapshot.md is append-only: orchestrator writes Sections 1-4, Dev appends Implementation Log, Reviewer appends Review Notes
656
+ - Tier 3: orchestrator implements directly; Critic challenges plan; Reviewer reviews
657
+ - context-snapshot.md is append-only: orchestrator writes Sections 1-4 + Implementation Log, Reviewer appends Review Notes
650
658
  - Gate checks enforce Implementation Log and Review Notes are written before proceeding
651
- - Do NOT use `run_in_background=true` when spawning normal work agents
659
+ - Do NOT use `run_in_background=true` when spawning Critic or Reviewer agents
652
660
  - Commit phase must use `/prizmkit-committer`; do NOT replace with manual git commit commands
653
661
  - **NEVER delete, modify, or touch any file under `.prizmkit/state/`** — those are pipeline runtime files managed by the runner
654
662
  - NEVER run `rm -rf`, `git clean`, or any destructive filesystem operations
@@ -4,7 +4,6 @@
4
4
  |----------|------|
5
5
  | Feature Artifacts Dir | `.prizmkit/specs/{{FEATURE_SLUG}}/` |
6
6
  | Context Snapshot | `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md` |
7
- | Dev Agent Def | {{DEV_SUBAGENT_PATH}} |
8
7
  | Reviewer Agent Def | {{REVIEWER_SUBAGENT_PATH}} |
9
8
  | Critic Agent Def (if enabled) | {{CRITIC_SUBAGENT_PATH}} |
10
9
  | Project Root | {{PROJECT_ROOT}} |
@@ -5,7 +5,6 @@
5
5
  | Feature Artifacts Dir | `.prizmkit/specs/{{FEATURE_SLUG}}/` |
6
6
  | Context Snapshot | `.prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md` |
7
7
  | Team Config | `{{TEAM_CONFIG_PATH}}` |
8
- | Dev Agent Def | {{DEV_SUBAGENT_PATH}} |
9
8
  | Reviewer Agent Def | {{REVIEWER_SUBAGENT_PATH}} |
10
9
  | Critic Agent Def | {{CRITIC_SUBAGENT_PATH}} |
11
10
  | Project Root | {{PROJECT_ROOT}} |
@@ -1,8 +1,8 @@
1
1
  ## PrizmKit Directory Convention
2
2
 
3
3
  ```
4
- .prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md ← orchestrator writes Sections 1-4; Dev appends Implementation Log; Reviewer appends Review Notes
4
+ .prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md ← orchestrator writes Sections 1-4 + Implementation Log; Reviewer appends Review Notes
5
5
  .prizmkit/specs/{{FEATURE_SLUG}}/plan.md ← includes Tasks section
6
6
  ```
7
7
 
8
- **`context-snapshot.md`** is the shared knowledge base. Orchestrator writes Sections 1-4; Dev appends Implementation Log; Reviewer appends Review Notes. Append-only after initial creation.
8
+ **`context-snapshot.md`** is the shared knowledge base. Orchestrator writes Sections 1-4 and the Implementation Log; Reviewer appends Review Notes. Append-only after initial creation.
@@ -1,9 +1,9 @@
1
1
  ## PrizmKit Directory Convention
2
2
 
3
3
  ```
4
- .prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md ← orchestrator writes Sections 1-4; Dev appends Implementation Log; Reviewer appends Review Notes
4
+ .prizmkit/specs/{{FEATURE_SLUG}}/context-snapshot.md ← orchestrator writes Sections 1-4 + Implementation Log; Reviewer appends Review Notes
5
5
  .prizmkit/specs/{{FEATURE_SLUG}}/spec.md
6
6
  .prizmkit/specs/{{FEATURE_SLUG}}/plan.md ← includes Tasks section
7
7
  ```
8
8
 
9
- **`context-snapshot.md`** is the shared knowledge base. Orchestrator writes Sections 1-4; Dev appends Implementation Log; Reviewer appends Review Notes. This eliminates redundant I/O across all agents.
9
+ **`context-snapshot.md`** is the shared knowledge base. Orchestrator writes Sections 1-4 and the Implementation Log; Reviewer appends Review Notes. This eliminates redundant I/O across review and orchestration phases.
@@ -22,6 +22,7 @@ grep -c '^\- \[ \]' .prizmkit/specs/{{FEATURE_SLUG}}/plan.md 2>/dev/null || true
22
22
  - Implements task-by-task, marking each `[x]` immediately
23
23
  - Creates/updates L2 `.prizm` docs when creating new modules
24
24
  - Defers scoped/full test execution until after the code-review loop completes
25
+ - Preserves Reviewer/Critic behavior; only the top-level implementation Dev handoff is removed
25
26
  - If plan.md has >5 tasks: update checkpoints after every 3 tasks
26
27
 
27
28
  **3c. Focused checks only (no test gate here)**:
@@ -23,6 +23,7 @@ Save pre-existing failing tests as `BASELINE_FAILURES`.
23
23
  - Implements task-by-task, marking each `[x]` immediately
24
24
  - Creates/updates L2 `.prizm` docs when creating new modules
25
25
  - Defers scoped/full test execution until after the code-review loop completes
26
+ - Preserves Reviewer/Critic behavior; only the top-level implementation Dev handoff is removed
26
27
  - If plan.md has >5 tasks: update checkpoints after every 3 tasks
27
28
 
28
29
  **3c. Focused checks only (no test gate here)**:
@@ -8,6 +8,7 @@ from argparse import Namespace
8
8
  from pathlib import Path
9
9
 
10
10
  from generate_bootstrap_prompt import (
11
+ ACTIVE_AGENT_PROMPTS,
11
12
  compute_feature_slug,
12
13
  find_feature,
13
14
  format_acceptance_criteria,
@@ -21,6 +22,7 @@ from generate_bootstrap_prompt import (
21
22
  build_replacements,
22
23
  generate_checkpoint_definition,
23
24
  merge_checkpoint_state,
25
+ load_active_agent_prompts,
24
26
  load_log_size_section,
25
27
  main as generate_bootstrap_main,
26
28
  )
@@ -809,6 +811,50 @@ class TestHeadlessRecoveryReframing:
809
811
  assert "run `/compact` after" not in rendered
810
812
 
811
813
 
814
+ class TestDirectOrchestratorImplementationPrompts:
815
+ def test_active_agent_prompt_loader_excludes_retired_dev_implement_prompt(self):
816
+ templates_dir = Path("dev-pipeline/templates").resolve()
817
+
818
+ replacements = load_active_agent_prompts(str(templates_dir))
819
+
820
+ assert "dev-implement.md" not in ACTIVE_AGENT_PROMPTS
821
+ assert "{{AGENT_PROMPT_DEV_IMPLEMENT}}" not in replacements
822
+ assert "{{AGENT_PROMPT_CRITIC_PLAN_CHALLENGE}}" in replacements
823
+ assert (templates_dir / "agent-prompts" / "dev-implement.md").exists() is False
824
+
825
+ def test_sections_all_modes_use_direct_orchestrator_implementation(self):
826
+ sections_dir = Path("dev-pipeline/templates/sections").resolve()
827
+
828
+ for mode in ("lite", "standard", "full"):
829
+ rendered = "\n".join(
830
+ section for _name, section in assemble_sections(
831
+ mode, str(sections_dir), init_done=True, is_resume=False,
832
+ critic_enabled=(mode == "full"), browser_enabled=False,
833
+ )
834
+ )
835
+
836
+ assert "/prizmkit-implement" in rendered
837
+ assert "orchestrator) execute" in rendered or "Run `/prizmkit-implement`" in rendered
838
+ assert "Spawn Dev subagent" not in rendered
839
+ assert "Spawn Dev agent" not in rendered
840
+ assert "Implement — Dev" not in rendered
841
+ assert "{{AGENT_PROMPT_DEV_IMPLEMENT}}" not in rendered
842
+ assert "dev-implement.md" not in rendered
843
+ assert "Reviewer Agent → filter → Dev Agent fix" in rendered
844
+
845
+ def test_legacy_tier_templates_use_direct_orchestrator_implementation(self):
846
+ for template_name in ("bootstrap-tier2.md", "bootstrap-tier3.md"):
847
+ rendered = (Path("dev-pipeline/templates") / template_name).read_text(encoding="utf-8")
848
+
849
+ assert "/prizmkit-implement" in rendered
850
+ assert "Implement — Orchestrator Direct" in rendered
851
+ assert "Spawn Dev subagent" not in rendered
852
+ assert "Spawn Dev agent" not in rendered
853
+ assert "Implement — Dev" not in rendered
854
+ assert "{{AGENT_PROMPT_DEV_IMPLEMENT}}" not in rendered
855
+ assert "dev-implement.md" not in rendered
856
+
857
+
812
858
  # ---------------------------------------------------------------------------
813
859
  # continuation handoff
814
860
  # ---------------------------------------------------------------------------
@@ -103,7 +103,7 @@ def test_python_parity_suite_declares_coverage_for_all_legacy_unix_snapshot_scen
103
103
  "envResolution": {
104
104
  "verboseAndStreamJson": ["test_python_env_resolution_scenarios_match_snapshot_contracts"],
105
105
  "effortModelAndCli": ["test_ai_session_command_builders_match_unix_runtime_snapshot"],
106
- "worktreeIgnored": ["test_runner_environment_enables_worktree_by_default_and_accepts_falsey_opt_out"],
106
+ "worktreeIgnored": ["test_runner_environment_disables_worktree_by_default_and_accepts_truthy_opt_in"],
107
107
  "autoPushFlag": ["test_successful_worktree_session_merges_cleans_branch_and_honors_auto_push"],
108
108
  },
109
109
  "aiCliCommands": {
@@ -320,13 +320,14 @@ def test_runner_models_parse_legacy_args_and_environment(tmp_path):
320
320
  assert env.strict_behavior_check is False
321
321
 
322
322
 
323
- def test_runner_environment_enables_worktree_by_default_and_accepts_falsey_opt_out():
323
+ def test_runner_environment_disables_worktree_by_default_and_accepts_truthy_opt_in():
324
324
  from prizmkit_runtime.runner_models import RunnerEnvironment
325
325
 
326
- assert RunnerEnvironment.from_env({}).use_worktree is True
327
- assert RunnerEnvironment.from_env({"USE_WORKTREE": "1"}).use_worktree is True
328
- assert RunnerEnvironment.from_env({"USE_WORKTREE": "true"}).use_worktree is True
329
- assert RunnerEnvironment.from_env({"USE_WORKTREE": "unexpected"}).use_worktree is True
326
+ assert RunnerEnvironment.from_env({}).use_worktree is False
327
+ assert RunnerEnvironment.from_env({"USE_WORKTREE": "unexpected"}).use_worktree is False
328
+
329
+ for truthy in ("1", "true", "yes", "on"):
330
+ assert RunnerEnvironment.from_env({"USE_WORKTREE": truthy}).use_worktree is True
330
331
 
331
332
  for falsey in ("0", "false", "no", "off"):
332
333
  assert RunnerEnvironment.from_env({"USE_WORKTREE": falsey}).use_worktree is False
@@ -639,10 +640,11 @@ def _install_runner_session_fakes(monkeypatch, runners, *, statuses=("success",)
639
640
 
640
641
 
641
642
  @pytest.mark.parametrize(("kind", "item_id", "branch"), [("feature", "F-001", "dev/F-001-test"), ("bugfix", "B-001", "bugfix/B-001-test")])
642
- def test_feature_and_bugfix_default_worktree_launches_from_worktree_and_renders_worktree_root(monkeypatch, tmp_path, kind, item_id, branch):
643
+ def test_feature_and_bugfix_explicit_worktree_launches_from_worktree_and_renders_worktree_root(monkeypatch, tmp_path, kind, item_id, branch):
643
644
  from prizmkit_runtime import runners
644
645
  from prizmkit_runtime.runner_models import family_for, parse_invocation
645
646
 
647
+ monkeypatch.setenv("USE_WORKTREE", "1")
646
648
  monkeypatch.setenv("DEV_BRANCH", branch)
647
649
  paths = _make_paths(tmp_path / kind)
648
650
  family = family_for(kind, paths)
@@ -672,6 +674,7 @@ def test_successful_worktree_session_merges_cleans_branch_and_honors_auto_push(m
672
674
  from prizmkit_runtime import runners
673
675
  from prizmkit_runtime.runner_models import family_for, parse_invocation
674
676
 
677
+ monkeypatch.setenv("USE_WORKTREE", "1")
675
678
  monkeypatch.setenv("DEV_BRANCH", "dev/F-001-test")
676
679
  monkeypatch.setenv("AUTO_PUSH", "1")
677
680
  paths = _make_paths(tmp_path)
@@ -692,6 +695,7 @@ def test_worktree_merge_conflict_writes_session_status_and_updates_merge_conflic
692
695
  from prizmkit_runtime import runners
693
696
  from prizmkit_runtime.runner_models import family_for, parse_invocation
694
697
 
698
+ monkeypatch.setenv("USE_WORKTREE", "1")
695
699
  monkeypatch.setenv("DEV_BRANCH", "dev/F-001-test")
696
700
  paths = _make_paths(tmp_path)
697
701
  family = family_for("feature", paths)
@@ -719,6 +723,7 @@ def test_terminal_worktree_failure_saves_wip_and_removes_worktree_without_deleti
719
723
  from prizmkit_runtime import runners
720
724
  from prizmkit_runtime.runner_models import family_for, parse_invocation
721
725
 
726
+ monkeypatch.setenv("USE_WORKTREE", "1")
722
727
  monkeypatch.setenv("DEV_BRANCH", "dev/F-001-test")
723
728
  paths = _make_paths(tmp_path)
724
729
  family = family_for("feature", paths)
@@ -735,7 +740,7 @@ def test_terminal_worktree_failure_saves_wip_and_removes_worktree_without_deleti
735
740
  assert observed.updates[-1][0] == "crashed"
736
741
 
737
742
 
738
- def test_default_worktree_materializes_ignored_support_assets_before_prompt_and_launch(monkeypatch, tmp_path):
743
+ def test_explicit_worktree_materializes_ignored_support_assets_before_prompt_and_launch(monkeypatch, tmp_path):
739
744
  from prizmkit_runtime import runners
740
745
  from prizmkit_runtime.paths import resolve_runtime_paths
741
746
  from prizmkit_runtime.runner_models import PromptGenerationResult, SessionClassification, family_for, parse_invocation
@@ -766,6 +771,7 @@ def test_default_worktree_materializes_ignored_support_assets_before_prompt_and_
766
771
  invocation = invocation.__class__(**{**invocation.__dict__, "list_path": paths.feature_plan})
767
772
  launch_cwds = []
768
773
 
774
+ monkeypatch.setenv("USE_WORKTREE", "1")
769
775
  monkeypatch.setenv("DEV_BRANCH", "dev/F-001-support-assets")
770
776
  monkeypatch.setattr(
771
777
  runners,
@@ -830,11 +836,10 @@ def test_default_worktree_materializes_ignored_support_assets_before_prompt_and_
830
836
  assert not launch_cwds[0].exists()
831
837
 
832
838
 
833
- def test_use_worktree_false_preserves_main_worktree_branch_launch(monkeypatch, tmp_path):
839
+ def test_default_no_worktree_preserves_main_worktree_branch_launch(monkeypatch, tmp_path):
834
840
  from prizmkit_runtime import runners
835
841
  from prizmkit_runtime.runner_models import family_for, parse_invocation
836
842
 
837
- monkeypatch.setenv("USE_WORKTREE", "0")
838
843
  monkeypatch.setenv("DEV_BRANCH", "dev/F-001-test")
839
844
  paths = _make_paths(tmp_path)
840
845
  family = family_for("feature", paths)
@@ -859,10 +864,103 @@ def test_use_worktree_false_preserves_main_worktree_branch_launch(monkeypatch, t
859
864
  assert "branch_merge" in plan_names
860
865
 
861
866
 
867
+ def test_no_worktree_runner_ignores_recovery_side_effect_branch(monkeypatch, tmp_path):
868
+ from prizmkit_runtime import runners
869
+ from prizmkit_runtime.runner_models import family_for, parse_invocation
870
+
871
+ monkeypatch.delenv("DEV_BRANCH", raising=False)
872
+ paths = _make_paths(tmp_path)
873
+ family = family_for("feature", paths)
874
+ invocation = parse_invocation(family, "run", ())
875
+ invocation = invocation.__class__(**{**invocation.__dict__, "list_path": paths.feature_plan})
876
+ observed = _install_runner_session_fakes(monkeypatch, runners)
877
+ branch_names = []
878
+
879
+ monkeypatch.setenv("DEV_BRANCH", "dev/F-001-recovery-20260707")
880
+ monkeypatch.setattr(runners, "current_branch", lambda _root: "dev/F-001-recovery-20260707")
881
+
882
+ def fake_run_git_plan(project_root, plan):
883
+ if plan.name == "branch_create":
884
+ branch_names.append(plan.commands[1].args[2])
885
+ return SimpleNamespace(ok=True, plan=plan, results=())
886
+
887
+ monkeypatch.setattr(runners, "run_git_plan", fake_run_git_plan)
888
+
889
+ status = runners._process_item(family, invocation, "F-001", paths, initial_metadata={})
890
+
891
+ assert status == "completed"
892
+ assert branch_names and branch_names[0].startswith("dev/F-001-")
893
+ assert "recovery" not in branch_names[0]
894
+ assert observed.updates[-1][2]["active_dev_branch"] == branch_names[0]
895
+ assert observed.updates[-1][2]["base_branch"] == "main"
896
+
897
+
898
+ def test_foreground_runner_emits_structured_feature_progress(monkeypatch, tmp_path, capsys):
899
+ from prizmkit_runtime import runners
900
+ from prizmkit_runtime.runner_models import family_for, parse_invocation
901
+
902
+ monkeypatch.setenv("DEV_BRANCH", "dev/F-001-output")
903
+ paths = _make_paths(tmp_path)
904
+ family = family_for("feature", paths)
905
+ invocation = parse_invocation(family, "run", ())
906
+ invocation = invocation.__class__(**{**invocation.__dict__, "list_path": paths.feature_plan})
907
+ _install_runner_session_fakes(monkeypatch, runners)
908
+ monkeypatch.setattr(runners, "run_git_plan", lambda project_root, plan: SimpleNamespace(ok=True, plan=plan, results=()))
909
+
910
+ status = runners._process_item(
911
+ family,
912
+ invocation,
913
+ "F-001",
914
+ paths,
915
+ initial_metadata={"title": "Readable output", "retry_count": 1, "infra_error_count": 2},
916
+ )
917
+
918
+ captured = capsys.readouterr()
919
+ assert status == "completed"
920
+ assert "────────────────────────────────────────────────────" in captured.err
921
+ assert "Feature: F-001 — Readable output" in captured.err
922
+ assert "Code retry: 1 / 3" in captured.err
923
+ assert "Infrastructure retry: 2 / 3" in captured.err
924
+ assert "Pipeline mode: lite (Tier 1 — Single Agent)" in captured.err
925
+ assert "Agents: 1 (critic: disabled)" in captured.err
926
+ assert "Spawning AI CLI session: F-001-session-1" in captured.err
927
+ assert "Session result: success" in captured.err
928
+
929
+
930
+ def test_no_worktree_interrupt_saves_wip_and_returns_to_base(monkeypatch, tmp_path):
931
+ from prizmkit_runtime import runners
932
+ from prizmkit_runtime.runner_models import family_for, parse_invocation
933
+
934
+ monkeypatch.setenv("DEV_BRANCH", "dev/F-001-interrupt")
935
+ paths = _make_paths(tmp_path)
936
+ family = family_for("feature", paths)
937
+ invocation = parse_invocation(family, "run", ())
938
+ invocation = invocation.__class__(**{**invocation.__dict__, "list_path": paths.feature_plan})
939
+ _install_runner_session_fakes(monkeypatch, runners)
940
+ ensure_calls = []
941
+ monkeypatch.setattr(runners, "run_git_plan", lambda project_root, plan: SimpleNamespace(ok=True, plan=plan, results=()))
942
+ monkeypatch.setattr(runners, "branch_ensure_return", lambda project_root, base, branch: ensure_calls.append((base, branch)))
943
+
944
+ class InterruptingLauncher:
945
+ def __init__(self, config):
946
+ self.config = config
947
+
948
+ def run(self):
949
+ raise KeyboardInterrupt
950
+
951
+ monkeypatch.setattr(runners, "AISessionLauncher", InterruptingLauncher)
952
+
953
+ with pytest.raises(KeyboardInterrupt):
954
+ runners._process_item(family, invocation, "F-001", paths, initial_metadata={})
955
+
956
+ assert ensure_calls == [("main", "dev/F-001-interrupt")]
957
+
958
+
862
959
  def test_context_overflow_reuses_worktree_and_skips_cleanup_until_terminal_outcome(monkeypatch, tmp_path):
863
960
  from prizmkit_runtime import runners
864
961
  from prizmkit_runtime.runner_models import family_for, parse_invocation
865
962
 
963
+ monkeypatch.setenv("USE_WORKTREE", "1")
866
964
  monkeypatch.setenv("DEV_BRANCH", "dev/F-001-test")
867
965
  paths = _make_paths(tmp_path)
868
966
  family = family_for("feature", paths)
@@ -958,6 +1056,14 @@ def test_classification_stalls_after_repeated_no_progress_context_overflow(tmp_p
958
1056
  assert classification.no_progress_count == 2
959
1057
 
960
1058
 
1059
+ def test_recovery_prompt_keeps_existing_branch_instead_of_recovery_suffix():
1060
+ text = (PIPELINE_ROOT / "scripts" / "generate-recovery-prompt.py").read_text(encoding="utf-8")
1061
+
1062
+ assert "fix/{bug_id}-recovery" not in text
1063
+ assert "do not create an additional" in text
1064
+ assert "last-resort fallback" in text
1065
+
1066
+
961
1067
  def test_recovery_detector_resolver_prefers_installed_platform_asset(tmp_path):
962
1068
  from prizmkit_runtime.paths import resolve_runtime_paths
963
1069
  from prizmkit_runtime.runner_recovery import RecoveryDetectorResolver
@@ -1117,6 +1223,7 @@ def test_python_daemon_controlled_start_status_stop_matches_snapshot(tmp_path):
1117
1223
  "#!/usr/bin/env python3\n"
1118
1224
  "import os, sys, time\n"
1119
1225
  "print('payload env=' + os.environ.get('FOO', ''))\n"
1226
+ "print('payload live=' + os.environ.get('PRIZMKIT_LIVE_OUTPUT', ''))\n"
1120
1227
  "print('payload args=' + ' '.join(sys.argv[1:]))\n"
1121
1228
  "sys.stdout.flush()\n"
1122
1229
  "time.sleep(30)\n",
@@ -1147,6 +1254,7 @@ def test_python_daemon_controlled_start_status_stop_matches_snapshot(tmp_path):
1147
1254
  assert json.loads(stop.render())["message"] in {"stopped", "already exited", "not running"}
1148
1255
  log_text = (paths.feature_state_dir / "pipeline-daemon.log").read_text(encoding="utf-8")
1149
1256
  assert "FOO=bar" in log_text or "payload env=bar" in log_text
1257
+ assert "payload live=0" in log_text
1150
1258
  assert "--project-root" in log_text
1151
1259
 
1152
1260
 
@@ -617,6 +617,82 @@ def test_session_dual_writes_progress_and_recovers_backup(tmp_path):
617
617
  assert second_log.read_text(encoding="utf-8") == "larger recovered log"
618
618
 
619
619
 
620
+ def test_session_live_output_prints_paths_and_verbose_cli_output(tmp_path, capsys):
621
+ from prizmkit_runtime.sessions import AISessionConfig, AISessionLauncher
622
+
623
+ fake_cli = tmp_path / "live_cli.py"
624
+ fake_cli.write_text(
625
+ "#!/usr/bin/env python3\n"
626
+ "print('live-line', flush=True)\n",
627
+ encoding="utf-8",
628
+ )
629
+ fake_cli.chmod(0o755)
630
+ prompt = tmp_path / "prompt.md"
631
+ prompt.write_text("hello", encoding="utf-8")
632
+ session_log = tmp_path / "state" / "sessions" / "sess-live" / "logs" / "session.log"
633
+ progress = session_log.with_name("progress.json")
634
+ heartbeat = session_log.with_name("heartbeat.json")
635
+
636
+ result = AISessionLauncher(
637
+ AISessionConfig(
638
+ cli_command=str(fake_cli),
639
+ platform="codebuddy",
640
+ model=None,
641
+ prompt_path=prompt,
642
+ cwd=tmp_path,
643
+ log_path=session_log,
644
+ progress_path=progress,
645
+ heartbeat_path=heartbeat,
646
+ verbose=True,
647
+ live_output=True,
648
+ )
649
+ ).run()
650
+
651
+ captured = capsys.readouterr()
652
+ assert result.exit_code == 0
653
+ assert "live-line" in captured.out
654
+ assert "[prizmkit] AI session started" in captured.err
655
+ assert f"[prizmkit] Prompt: {prompt}" in captured.err
656
+ assert f"[prizmkit] Session log: {session_log}" in captured.err
657
+ assert f"[prizmkit] Progress: {progress}" in captured.err
658
+ assert f"[prizmkit] Heartbeat: {heartbeat}" in captured.err
659
+ assert "[prizmkit] AI session finished" in captured.err
660
+
661
+
662
+ def test_session_live_output_does_not_forward_cli_output_without_verbose(tmp_path, capsys):
663
+ from prizmkit_runtime.sessions import AISessionConfig, AISessionLauncher
664
+
665
+ fake_cli = tmp_path / "quiet_cli.py"
666
+ fake_cli.write_text(
667
+ "#!/usr/bin/env python3\n"
668
+ "print('hidden-line', flush=True)\n",
669
+ encoding="utf-8",
670
+ )
671
+ fake_cli.chmod(0o755)
672
+ prompt = tmp_path / "prompt.md"
673
+ prompt.write_text("hello", encoding="utf-8")
674
+ session_log = tmp_path / "state" / "sessions" / "sess-quiet" / "logs" / "session.log"
675
+
676
+ result = AISessionLauncher(
677
+ AISessionConfig(
678
+ cli_command=str(fake_cli),
679
+ platform="codebuddy",
680
+ model=None,
681
+ prompt_path=prompt,
682
+ cwd=tmp_path,
683
+ log_path=session_log,
684
+ live_output=True,
685
+ )
686
+ ).run()
687
+
688
+ captured = capsys.readouterr()
689
+ assert result.exit_code == 0
690
+ assert "hidden-line" not in captured.out
691
+ assert "hidden-line" in session_log.read_text(encoding="utf-8")
692
+ assert "[prizmkit] AI session started" in captured.err
693
+ assert "[prizmkit] AI session finished" in captured.err
694
+
695
+
620
696
  def test_session_final_progress_merge_preserves_tracker_fatal_error(tmp_path):
621
697
  from prizmkit_runtime.sessions import AISessionConfig, AISessionLauncher
622
698
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.1.102",
2
+ "version": "1.1.105",
3
3
  "skills": {
4
4
  "prizm-kit": {
5
5
  "description": "Full-lifecycle dev toolkit. Covers spec-driven development, Prizm context docs, code quality, debugging, deployment, and knowledge management.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prizmkit",
3
- "version": "1.1.102",
3
+ "version": "1.1.105",
4
4
  "description": "Create a new PrizmKit-powered project with clean initialization — no framework dev files, just what you need.",
5
5
  "type": "module",
6
6
  "bin": {