prizmkit 1.1.110 → 1.1.111

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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "frameworkVersion": "1.1.110",
3
- "bundledAt": "2026-07-08T05:42:13.015Z",
4
- "bundledFrom": "84797b8"
2
+ "frameworkVersion": "1.1.111",
3
+ "bundledAt": "2026-07-08T07:06:52.766Z",
4
+ "bundledFrom": "1db214b"
5
5
  }
@@ -25,6 +25,8 @@ You are the team's "senior engineer doing code review" — you diagnose problems
25
25
  2. If no `context-snapshot.md`: read `spec.md`, `plan.md` from the artifact directory, then `.prizmkit/prizm-docs/root.prizm` and relevant L1/L2 docs
26
26
  3. Identify changed files from `## Implementation Log` or completed tasks in plan.md
27
27
 
28
+ **Worktree constraint**: You are spawned with default/no worktree isolation by the orchestrator. You review the active checkout's changes — the orchestrator provides workspace context (git status, diff summary, new-file paths). Do NOT request or create `.claude/worktrees/...` tool worktrees. Do NOT re-discover or re-index the whole repository; use the orchestrator-provided context as your review scope and request only targeted file reads.
29
+
28
30
  **File Reading Rule**: Read ONLY files listed in the Implementation Log for diagnosis — do not explore unrelated files. Exception: during Fix Strategy Formulation, you MAY read additional files to trace impact (callers, dependents, shared patterns).
29
31
 
30
32
  ### Review Workflow
@@ -74,6 +76,8 @@ When Dev has applied fixes and returns for re-review:
74
76
  - Do not modify source files to fix issues — produce Fix Instructions for Dev instead
75
77
  - **Do NOT run test suites** — the Orchestrator handles testing. You review code, not execute tests.
76
78
  - Do NOT re-read source files already listed in context-snapshot.md Section 4 File Manifest unless you need a specific code detail for a finding
79
+ - **Do NOT request or create worktrees** (`.claude/worktrees/...` or similar) — you review the active checkout's changes using the orchestrator-provided workspace context
80
+ - **Do NOT re-discover or broadly re-index the repository** — the orchestrator has already captured the diff scope; request only targeted file reads for lines you need
77
81
 
78
82
  ### Behavioral Rules
79
83
 
@@ -190,9 +190,42 @@ def detect_test_commands(project_root):
190
190
  """
191
191
  test_commands = []
192
192
 
193
- # Check for npm/package.json
194
- if os.path.exists(os.path.join(project_root, "package.json")):
195
- test_commands.append("npm test")
193
+ # Check for npm/package.json — use content-aware detection
194
+ pkg_json_path = os.path.join(project_root, "package.json")
195
+ if os.path.exists(pkg_json_path):
196
+ try:
197
+ with open(pkg_json_path, "r", encoding="utf-8") as f:
198
+ pkg_data = json.load(f)
199
+ scripts = pkg_data.get("scripts", {}) if isinstance(pkg_data, dict) else {}
200
+
201
+ # Detect package manager from lockfiles
202
+ if os.path.exists(os.path.join(project_root, "pnpm-lock.yaml")):
203
+ pm_cmd = "pnpm"
204
+ elif os.path.exists(os.path.join(project_root, "yarn.lock")):
205
+ pm_cmd = "yarn"
206
+ else:
207
+ pm_cmd = "npm"
208
+
209
+ # Find best test script: prefer "test", then "test:unit",
210
+ # then first script matching test-related patterns
211
+ if "test" in scripts:
212
+ test_commands.append("{} test".format(pm_cmd))
213
+ elif "test:unit" in scripts:
214
+ test_commands.append("{} run test:unit".format(pm_cmd))
215
+ else:
216
+ # Search for any test-like script name
217
+ test_like = None
218
+ for script_name in sorted(scripts.keys()):
219
+ if "test" in script_name.lower():
220
+ test_like = script_name
221
+ break
222
+ if test_like:
223
+ test_commands.append(
224
+ "{} run {}".format(pm_cmd, test_like)
225
+ )
226
+ except Exception:
227
+ # Corrupt/unreadable package.json → skip JS detection
228
+ pass
196
229
 
197
230
  # Check for Go
198
231
  if os.path.exists(os.path.join(project_root, "go.mod")):
@@ -8,7 +8,7 @@ A checkpoint file tracks your progress through this workflow:
8
8
 
9
9
  Use this helper to update checkpoint status. Do not hand-edit `workflow-checkpoint.json` directly.
10
10
 
11
- ```bash
11
+ ```text
12
12
  {{CHECKPOINT_PYTHON_CMD}} \
13
13
  --checkpoint-path {{CHECKPOINT_PATH}} \
14
14
  --step <step-id-or-skill-name> \
@@ -10,6 +10,7 @@ from pathlib import Path
10
10
  from generate_bootstrap_prompt import (
11
11
  ACTIVE_AGENT_PROMPTS,
12
12
  compute_feature_slug,
13
+ detect_test_commands,
13
14
  find_feature,
14
15
  format_acceptance_criteria,
15
16
  format_ac_checklist,
@@ -1214,6 +1215,19 @@ class TestHeadlessPromptCleanupF033:
1214
1215
  assert ".prizmkit-test-started" in prompt
1215
1216
  assert "{{RUNTIME_HELPER_CMD}}" not in prompt
1216
1217
  assert "prizmkit-runtime-helper.py" in prompt
1218
+ # Anti-Bash guardrails for bugfix/refactor branches (F-039)
1219
+ if "Bug ID" in prompt:
1220
+ assert "{{RUNTIME_HELPER_CMD}}" not in prompt
1221
+ assert "prizmkit-runtime-helper.py" in prompt
1222
+ assert "2>/dev/null" not in prompt
1223
+ assert "lsof -ti" not in prompt
1224
+ assert "which " not in prompt # no which-command shell glue
1225
+ if "Refactor ID" in prompt:
1226
+ assert "{{RUNTIME_HELPER_CMD}}" not in prompt
1227
+ assert "prizmkit-runtime-helper.py" in prompt
1228
+ assert "2>/dev/null" not in prompt
1229
+ assert "lsof -ti" not in prompt
1230
+ assert "which " not in prompt # no which-command shell glue
1217
1231
  assert "python3 {{INIT_SCRIPT_PATH}}" not in prompt
1218
1232
 
1219
1233
  def test_legacy_feature_templates_are_clean_when_rendered(self, tmp_path):
@@ -1436,3 +1450,94 @@ class TestFeatureBootstrapShellExtraction:
1436
1450
  assert "text search . --pattern \"123-prompt-cleanup\" --json" in tier3
1437
1451
  assert "text count .prizmkit/specs/123-prompt-cleanup/plan.md --pattern \"- [ ]\"" in tier3
1438
1452
  assert "Check if feature already committed by inspecting recent git history" in tier3
1453
+
1454
+
1455
+ # ---------------------------------------------------------------------------
1456
+ # detect_test_commands
1457
+ # ---------------------------------------------------------------------------
1458
+
1459
+ import json as _json
1460
+
1461
+ class TestDetectTestCommands:
1462
+ def test_no_package_json_returns_empty(self, tmp_path):
1463
+ """When no package.json exists, no npm test emitted."""
1464
+ result = detect_test_commands(str(tmp_path))
1465
+ assert "npm test" not in result
1466
+ assert "pnpm test" not in result
1467
+ assert "yarn test" not in result
1468
+
1469
+ def test_package_json_no_test_script(self, tmp_path):
1470
+ """package.json exists but has no test script → no npm test emitted."""
1471
+ pkg = tmp_path / "package.json"
1472
+ pkg.write_text(_json.dumps({"name": "test", "scripts": {"start": "node index.js"}}), encoding="utf-8")
1473
+ result = detect_test_commands(str(tmp_path))
1474
+ # Bug: current code returns "npm test" even with no test script
1475
+ assert "npm test" not in result, (
1476
+ "Should NOT emit npm test when package.json has no test script"
1477
+ )
1478
+
1479
+ def test_package_json_with_test_script(self, tmp_path):
1480
+ """package.json with scripts.test → emits npm test."""
1481
+ pkg = tmp_path / "package.json"
1482
+ pkg.write_text(_json.dumps({"name": "test", "scripts": {"test": "vitest run"}}), encoding="utf-8")
1483
+ result = detect_test_commands(str(tmp_path))
1484
+ # Current code returns "npm test" (hardcoded), but at minimum it should
1485
+ # still return something test-like. After fix, it becomes "npm test"
1486
+ assert "npm test" in result, (
1487
+ "Should emit npm test when package.json has test script"
1488
+ )
1489
+
1490
+ def test_package_json_non_test_scripts_only(self, tmp_path):
1491
+ """package.json exists but scripts only has non-test entries."""
1492
+ pkg = tmp_path / "package.json"
1493
+ pkg.write_text(_json.dumps({"name": "test", "scripts": {"build": "tsc", "start": "node dist/index.js"}}), encoding="utf-8")
1494
+ result = detect_test_commands(str(tmp_path))
1495
+ assert "npm test" not in result, (
1496
+ "Should NOT emit npm test when no test-related scripts exist"
1497
+ )
1498
+
1499
+ def test_pnpm_detected_from_lockfile(self, tmp_path):
1500
+ """pnpm-lock.yaml → emits pnpm test when test script exists."""
1501
+ pkg = tmp_path / "package.json"
1502
+ pkg.write_text(_json.dumps({"name": "test", "scripts": {"test": "vitest"}}), encoding="utf-8")
1503
+ (tmp_path / "pnpm-lock.yaml").write_text("", encoding="utf-8")
1504
+ result = detect_test_commands(str(tmp_path))
1505
+ assert "pnpm test" in result
1506
+
1507
+ def test_yarn_detected_from_lockfile(self, tmp_path):
1508
+ """yarn.lock → emits yarn test when test script exists."""
1509
+ pkg = tmp_path / "package.json"
1510
+ pkg.write_text(_json.dumps({"name": "test", "scripts": {"test": "jest"}}), encoding="utf-8")
1511
+ (tmp_path / "yarn.lock").write_text("", encoding="utf-8")
1512
+ result = detect_test_commands(str(tmp_path))
1513
+ assert "yarn test" in result
1514
+
1515
+ def test_corrupt_package_json_graceful_fallback(self, tmp_path):
1516
+ """Corrupt/unparseable package.json → skip JS detection gracefully."""
1517
+ (tmp_path / "package.json").write_text("not valid json {{{", encoding="utf-8")
1518
+ result = detect_test_commands(str(tmp_path))
1519
+ # Should not crash, should not emit npm test
1520
+ assert "npm test" not in result
1521
+
1522
+ def test_non_js_detections_preserved(self, tmp_path):
1523
+ """Go, Rust, Python, Makefile detections unchanged."""
1524
+ (tmp_path / "go.mod").write_text("", encoding="utf-8")
1525
+ (tmp_path / "Cargo.toml").write_text("", encoding="utf-8")
1526
+ (tmp_path / "setup.py").write_text("", encoding="utf-8")
1527
+ makefile = tmp_path / "Makefile"
1528
+ makefile.write_text("test:\n\tpytest\n", encoding="utf-8")
1529
+ result = detect_test_commands(str(tmp_path))
1530
+ assert "go test ./..." in result
1531
+ assert "cargo test" in result
1532
+ assert "pytest" in result
1533
+ assert "make test" in result
1534
+
1535
+ def test_package_json_test_unit_script_only(self, tmp_path):
1536
+ """package.json with test:unit but no test → fallback to available test script."""
1537
+ pkg = tmp_path / "package.json"
1538
+ pkg.write_text(_json.dumps({"name": "test", "scripts": {"test:unit": "vitest run"}}), encoding="utf-8")
1539
+ (tmp_path / "package-lock.json").write_text("", encoding="utf-8")
1540
+ result = detect_test_commands(str(tmp_path))
1541
+ # Should detect test:unit as a viable test script
1542
+ assert "npm run test:unit" in result
1543
+
@@ -2,6 +2,8 @@
2
2
 
3
3
  from argparse import Namespace
4
4
  from pathlib import Path
5
+ import json
6
+ import os
5
7
 
6
8
  from generate_bugfix_prompt import (
7
9
  build_replacements,
@@ -303,3 +305,123 @@ def test_template_source_is_clean_for_headless_prompt_contract():
303
305
  content = Path("dev-pipeline/templates/bugfix-bootstrap-prompt.md").read_text(encoding="utf-8")
304
306
  for forbidden in HEADLESS_FORBIDDEN_PROMPT_STRINGS:
305
307
  assert forbidden not in content
308
+
309
+
310
+ # ---------------------------------------------------------------------------
311
+ # Anti-Bash regression guardrails (F-039)
312
+ # Note: grep -n, sed -n, grep -q, wc -l excluded — referenced in bugfix context
313
+ # budget rules as AI tool hints, not as operational shell snippets.
314
+ # | sed (trailing space) only matches pipe-to-sed operational patterns.
315
+ # ---------------------------------------------------------------------------
316
+
317
+ RETIRED_EXTRACTED_SNIPPETS_BUGFIX = [
318
+ "find . -maxdepth",
319
+ "| sed ",
320
+ r"grep -c '^\- \[ \]'",
321
+ "2>/dev/null",
322
+ "/dev/null",
323
+ "$DEV_PORT",
324
+ "$!",
325
+ "$HOME",
326
+ "if [ ",
327
+ "which playwright-cli",
328
+ "which opencli",
329
+ "lsof -ti",
330
+ "xargs",
331
+ " kill -",
332
+ "kill -",
333
+ "|| echo",
334
+ "mkdir -p",
335
+ "touch .prizmkit",
336
+ ]
337
+
338
+
339
+ def _render_bugfix_prompt(tmp_path, platform="claude"):
340
+ """Render a complete bugfix prompt through the generator."""
341
+ from generate_bugfix_prompt import main as bugfix_main
342
+
343
+ project = tmp_path / "bugfix-anti-bash"
344
+ project.mkdir(parents=True)
345
+ (project / ".prizmkit").mkdir(exist_ok=True)
346
+ (project / ".prizmkit" / "config.json").write_text(
347
+ json.dumps({"platform": platform, "ai_cli": platform}), encoding="utf-8"
348
+ )
349
+ _ensure_agent_files(project)
350
+ bug_list = project / ".prizmkit" / "plans" / "bug-fix-list.json"
351
+ bug_list.parent.mkdir(parents=True)
352
+ bug_list.write_text(json.dumps({
353
+ "bugs": [{"id": "B-001", "title": "Broken", "description": "Fix it",
354
+ "acceptance_criteria": ["Works"]}],
355
+ "global_context": {"language": "Python"},
356
+ }), encoding="utf-8")
357
+ output = project / "bugfix-prompt.md"
358
+ old_argv = os.sys.argv
359
+ old_platform = os.environ.get("PRIZMKIT_PLATFORM")
360
+ try:
361
+ os.environ.pop("PRIZMKIT_PLATFORM", None)
362
+ os.sys.argv = [
363
+ "generate-bugfix-prompt.py",
364
+ "--bug-list", str(bug_list),
365
+ "--bug-id", "B-001",
366
+ "--session-id", "session-1",
367
+ "--run-id", "run-1",
368
+ "--retry-count", "0",
369
+ "--resume-phase", "null",
370
+ "--state-dir", str(project / ".prizmkit" / "state" / "bugfix"),
371
+ "--project-root", str(project),
372
+ "--output", str(output),
373
+ ]
374
+ try:
375
+ bugfix_main()
376
+ except SystemExit as exc:
377
+ if exc.code not in (0, None):
378
+ raise
379
+ finally:
380
+ os.sys.argv = old_argv
381
+ if old_platform is None:
382
+ os.environ.pop("PRIZMKIT_PLATFORM", None)
383
+ else:
384
+ os.environ["PRIZMKIT_PLATFORM"] = old_platform
385
+ return output.read_text(encoding="utf-8")
386
+
387
+
388
+ def _ensure_agent_files(project, platforms=None):
389
+ """Create minimal agent files so platform resolution succeeds."""
390
+ if platforms is None:
391
+ platforms = ("claude", "codex")
392
+ for plat in platforms:
393
+ platform_dir = ".codex" if plat == "codex" else ".claude"
394
+ suffix = ".toml" if plat == "codex" else ".md"
395
+ agents_dir = project / platform_dir / "agents"
396
+ agents_dir.mkdir(parents=True, exist_ok=True)
397
+ for name in ("dev", "reviewer", "critic"):
398
+ (agents_dir / f"prizm-dev-team-{name}{suffix}").write_text("agent\n", encoding="utf-8")
399
+
400
+
401
+ class TestBugfixBootstrapShellExtraction:
402
+ """Regression guardrails: bugfix rendered prompts and source use helper, not shell."""
403
+
404
+ def test_rendered_bugfix_prompt_uses_runtime_helper_not_extracted_bash(self, tmp_path):
405
+ prompt = _render_bugfix_prompt(tmp_path)
406
+ for snippet in RETIRED_EXTRACTED_SNIPPETS_BUGFIX:
407
+ assert snippet not in prompt, f"Retired shell snippet found in bugfix prompt: {snippet}"
408
+ assert "{{RUNTIME_HELPER_CMD}}" not in prompt
409
+ assert "python" in prompt.lower()
410
+ assert ".prizmkit/dev-pipeline/scripts/prizmkit-runtime-helper.py" in prompt
411
+ assert "artifact ensure-dir .prizmkit/bugfix/B-001" in prompt
412
+ assert "text contains .prizmkit/bugfix/B-001/review-report.md --pattern \"## Verdict\"" in prompt
413
+
414
+ def test_bugfix_template_source_is_clean_of_helper_covered_snippets(self):
415
+ content = Path("dev-pipeline/templates/bugfix-bootstrap-prompt.md").read_text(encoding="utf-8")
416
+ for snippet in RETIRED_EXTRACTED_SNIPPETS_BUGFIX:
417
+ assert snippet not in content, f"Shell snippet in bugfix template: {snippet}"
418
+
419
+ def test_continuation_source_is_clean_of_helper_covered_snippets(self):
420
+ content = Path("dev-pipeline/scripts/continuation.py").read_text(encoding="utf-8")
421
+ for snippet in RETIRED_EXTRACTED_SNIPPETS_BUGFIX:
422
+ assert snippet not in content, f"Shell snippet in continuation.py: {snippet}"
423
+
424
+ def test_bugfix_generator_source_is_clean_of_helper_covered_snippets(self):
425
+ content = Path("dev-pipeline/scripts/generate-bugfix-prompt.py").read_text(encoding="utf-8")
426
+ for snippet in RETIRED_EXTRACTED_SNIPPETS_BUGFIX:
427
+ assert snippet not in content, f"Shell snippet in generate-bugfix-prompt.py: {snippet}"
@@ -2,6 +2,7 @@
2
2
 
3
3
  from argparse import Namespace
4
4
  from pathlib import Path
5
+ import os
5
6
 
6
7
  from continuation import append_continuation_handoff
7
8
  from generate_refactor_prompt import (
@@ -194,3 +195,126 @@ def test_template_source_is_clean_for_headless_prompt_contract():
194
195
  content = Path("dev-pipeline/templates/refactor-bootstrap-prompt.md").read_text(encoding="utf-8")
195
196
  for forbidden in HEADLESS_FORBIDDEN_PROMPT_STRINGS:
196
197
  assert forbidden not in content
198
+
199
+
200
+ # ---------------------------------------------------------------------------
201
+ # Anti-Bash regression guardrails (F-039)
202
+ # Note: grep -n, sed -n, grep -q, wc -l excluded — may appear in cross-pipeline
203
+ # context budget rules as AI tool hints, not as operational shell snippets.
204
+ # | sed (trailing space) only matches pipe-to-sed operational patterns.
205
+ # ---------------------------------------------------------------------------
206
+
207
+ RETIRED_EXTRACTED_SNIPPETS_REFACTOR = [
208
+ "find . -maxdepth",
209
+ "| sed ",
210
+ r"grep -c '^\- \[ \]'",
211
+ "2>/dev/null",
212
+ "/dev/null",
213
+ "$DEV_PORT",
214
+ "$!",
215
+ "$HOME",
216
+ "if [ ",
217
+ "which playwright-cli",
218
+ "which opencli",
219
+ "lsof -ti",
220
+ "xargs",
221
+ " kill -",
222
+ "kill -",
223
+ "|| echo",
224
+ "mkdir -p",
225
+ "touch .prizmkit",
226
+ ]
227
+
228
+
229
+ def _render_refactor_prompt_full(tmp_path, platform="claude"):
230
+ """Render a complete refactor prompt through the generator."""
231
+ import json, os
232
+ from generate_refactor_prompt import main as refactor_main
233
+
234
+ project = tmp_path / "refactor-anti-bash"
235
+ project.mkdir(parents=True)
236
+ (project / ".prizmkit").mkdir(exist_ok=True)
237
+ (project / ".prizmkit" / "config.json").write_text(
238
+ json.dumps({"platform": platform, "ai_cli": platform}), encoding="utf-8"
239
+ )
240
+ # Ensure agent files
241
+ for plat in ("claude", "codex"):
242
+ platform_dir = ".codex" if plat == "codex" else ".claude"
243
+ suffix = ".toml" if plat == "codex" else ".md"
244
+ agents_dir = project / platform_dir / "agents"
245
+ agents_dir.mkdir(parents=True, exist_ok=True)
246
+ for name in ("dev", "reviewer", "critic"):
247
+ (agents_dir / f"prizm-dev-team-{name}{suffix}").write_text("agent\n", encoding="utf-8")
248
+
249
+ refactor_list = project / ".prizmkit" / "plans" / "refactor-list.json"
250
+ refactor_list.parent.mkdir(parents=True)
251
+ refactor_list.write_text(json.dumps({
252
+ "refactors": [{
253
+ "id": "R-001", "title": "Restructure", "description": "Preserve behavior",
254
+ "acceptance_criteria": ["Behavior preserved"],
255
+ "scope": {"files": ["src/a.py"], "modules": ["src"]},
256
+ }],
257
+ "global_context": {"language": "Python"},
258
+ }), encoding="utf-8")
259
+ output = project / "refactor-prompt.md"
260
+ old_argv = os.sys.argv
261
+ old_platform = os.environ.get("PRIZMKIT_PLATFORM")
262
+ try:
263
+ os.environ.pop("PRIZMKIT_PLATFORM", None)
264
+ os.sys.argv = [
265
+ "generate-refactor-prompt.py",
266
+ "--refactor-list", str(refactor_list),
267
+ "--refactor-id", "R-001",
268
+ "--session-id", "session-1",
269
+ "--run-id", "run-1",
270
+ "--retry-count", "0",
271
+ "--resume-phase", "null",
272
+ "--state-dir", str(project / ".prizmkit" / "state" / "refactor"),
273
+ "--output", str(output),
274
+ ]
275
+ old_cwd = os.getcwd()
276
+ os.chdir(project)
277
+ try:
278
+ try:
279
+ refactor_main()
280
+ except SystemExit as exc:
281
+ if exc.code not in (0, None):
282
+ raise
283
+ finally:
284
+ os.chdir(old_cwd)
285
+ finally:
286
+ os.sys.argv = old_argv
287
+ if old_platform is None:
288
+ os.environ.pop("PRIZMKIT_PLATFORM", None)
289
+ else:
290
+ os.environ["PRIZMKIT_PLATFORM"] = old_platform
291
+ return output.read_text(encoding="utf-8")
292
+
293
+
294
+ class TestRefactorBootstrapShellExtraction:
295
+ """Regression guardrails: refactor rendered prompts and source use helper, not shell."""
296
+
297
+ def test_rendered_refactor_prompt_uses_runtime_helper_not_extracted_bash(self, tmp_path):
298
+ prompt = _render_refactor_prompt_full(tmp_path)
299
+ for snippet in RETIRED_EXTRACTED_SNIPPETS_REFACTOR:
300
+ assert snippet not in prompt, f"Retired shell snippet found in refactor prompt: {snippet}"
301
+ assert "{{RUNTIME_HELPER_CMD}}" not in prompt
302
+ assert "python" in prompt.lower()
303
+ assert ".prizmkit/dev-pipeline/scripts/prizmkit-runtime-helper.py" in prompt
304
+ assert "artifact ensure-dir .prizmkit/refactor/R-001" in prompt
305
+ assert "text contains .prizmkit/refactor/R-001/review-report.md --pattern \"## Verdict\"" in prompt
306
+
307
+ def test_refactor_template_source_is_clean_of_helper_covered_snippets(self):
308
+ content = Path("dev-pipeline/templates/refactor-bootstrap-prompt.md").read_text(encoding="utf-8")
309
+ for snippet in RETIRED_EXTRACTED_SNIPPETS_REFACTOR:
310
+ assert snippet not in content, f"Shell snippet in refactor template: {snippet}"
311
+
312
+ def test_continuation_source_is_clean_of_helper_covered_snippets(self):
313
+ content = Path("dev-pipeline/scripts/continuation.py").read_text(encoding="utf-8")
314
+ for snippet in RETIRED_EXTRACTED_SNIPPETS_REFACTOR:
315
+ assert snippet not in content, f"Shell snippet in continuation.py: {snippet}"
316
+
317
+ def test_refactor_generator_source_is_clean_of_helper_covered_snippets(self):
318
+ content = Path("dev-pipeline/scripts/generate-refactor-prompt.py").read_text(encoding="utf-8")
319
+ for snippet in RETIRED_EXTRACTED_SNIPPETS_REFACTOR:
320
+ assert snippet not in content, f"Shell snippet in generate-refactor-prompt.py: {snippet}"
@@ -804,7 +804,7 @@ def test_headless_subagent_prompt_guidance_keeps_no_worktree_boundary_visible():
804
804
  for path in guidance_files:
805
805
  text = path.read_text(encoding="utf-8")
806
806
  assert "default/no worktree" in text, path
807
- assert "do not request worktree isolation" in text.lower(), path
807
+ assert "do not request or create" in text.lower() or "do not request worktree isolation" in text.lower(), path
808
808
  assert ".claude/worktrees/..." in text, path
809
809
  assert ".prizmkit/state/worktrees/..." in text, path
810
810
  assert "USE_WORKTREE" in text, path
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.1.110",
2
+ "version": "1.1.111",
3
3
  "skills": {
4
4
  "prizmkit": {
5
5
  "description": "Full-lifecycle dev toolkit. Covers spec-driven development, Prizm context docs, code quality, debugging, deployment, and knowledge management.",
@@ -67,15 +67,15 @@ All three follow the same per-task flow. Detailed documentation policies (when t
67
67
 
68
68
  | Skill | Purpose | Trigger Phrases |
69
69
  |-------|---------|-----------------|
70
- | `/prizmkit-plan` | Specify + plan: natural language spec.md plan.md + tasks | "specify", "plan", "new feature", "I want to add...", "architect", "break it down" |
71
- | `/prizmkit-implement` | Execute plan.md tasks, write code (TDD) | "implement", "build", "code it", "start coding" |
72
- | `/prizmkit-code-review` | Diagnose issues + produce Fix Instructions | "review", "check code", "is it ready to commit" |
73
- | `/prizmkit-retrospective` | Sync .prizmkit/prizm-docs/ with code changes | "retrospective", "retro", "sync docs", "wrap up" |
74
- | `/prizmkit-committer` | Safe git commit with Conventional Commits | "commit", "submit", "finish", "ship it" |
75
- | `/prizmkit-test` | Generate + run tests, analyze coverage gaps, unified quality report | "test", "run tests", "check quality", "verify code", "add tests" |
76
- | `/prizmkit-deploy` | Universal deployment gateway: SSH automation, cloud/Docker guided setup, status/logs/rollback | "deploy", "ship it", "go live", "rollback", "deploy status" |
77
- | `/prizmkit-init` | Project bootstrap + .prizmkit/prizm-docs/ setup | "init", "initialize", "take over this project" |
78
- | `/prizmkit-prizm-docs` | Doc management (init/status/rebuild/validate) | "check docs", "rebuild docs", "validate docs" |
70
+ | `/prizmkit-plan` | Specify and plan: turns natural language into spec.md + plan.md with architecture and executable tasks. Use as the **first implementation step** in the lifecycle, before /prizmkit-implement. | "specify", "plan", "new feature", "I want to add...", "architect", "break it down" |
71
+ | `/prizmkit-implement` | Execute plan.md tasks with **TDD approach**, respecting task ordering and dependencies. Use after /prizmkit-plan as the **implementation step** of the lifecycle. | "implement", "build", "code it", "start coding" |
72
+ | `/prizmkit-code-review` | **Iterative review-fix loop** against spec and plan (max 3 rounds until PASS). Spawns a reviewer agent; the main orchestrator applies accepted fixes directly. Use after /prizmkit-implement as the **quality gate**. | "review", "check code", "code review", "is it ready to commit" |
73
+ | `/prizmkit-retrospective` | Incremental .prizmkit/prizm-docs/ maintainer: (1) **structural sync** of KEY_FILES/INTERFACES/DEPENDENCIES, (2) inject **TRAPS/RULES/DECISIONS**. Run after code review and **before committing**. | "retrospective", "retro", "update docs", "sync docs", "wrap up" |
74
+ | `/prizmkit-committer` | **Pure git commit** with safety checks: stages files, generates Conventional Commits message, commits. Does NOT modify docs — run /prizmkit-retrospective first. Use as the **final lifecycle step**. | "commit", "submit", "finish", "ship it" |
75
+ | `/prizmkit-test` | **Full-stack test orchestration**: detects architecture, analyzes coverage gaps, generates missing unit/integration/E2E tests with a boundary coverage matrix, and outputs a unified report. Use after development and **before deploy** for quality verification. | "test", "run tests", "check quality", "verify code", "generate tests", "add tests" |
76
+ | `/prizmkit-deploy` | **Universal deployment gateway**: discovers project type/target, automates SSH Linux PM2+Nginx+blue-green deploys, and manages operations — status, logs, restart, rollback, health checks. | "deploy", "ship it", "go live", "rollback", "deploy status" |
77
+ | `/prizmkit-init` | **Project bootstrap**: scans codebase, generates .prizmkit/prizm-docs/ and project brief, detects tech stack. Use as the **first lifecycle step for new projects**, or after `npx prizmkit install`. | "init", "initialize", "take over this project", "bootstrap" |
78
+ | `/prizmkit-prizm-docs` | **Doc system spec and management**: defines the 3-level .prizmkit/prizm-docs/ hierarchy (L0/L1/L2), format rules, size limits, and loading protocol. Use for doc init/status/rebuild/validate/migrate. For **incremental code-driven updates**, use /prizmkit-retrospective instead. | "check docs", "rebuild docs", "validate docs", "prizm docs" |
79
79
 
80
80
  **Reading guide**:
81
81
  - Need code structure/modules/interfaces/traps/decisions? → `.prizmkit/prizm-docs/`
@@ -37,7 +37,12 @@ The loop repeats until the Reviewer finds no issues or the max round limit is re
37
37
  - If `context-snapshot.md` contains `## PrizmKit Test Gate`, read that summary too.
38
38
  - If this is a feature artifact under `.prizmkit/specs/` and no test report pointer or summary exists, pass `No scoped /prizmkit-test report found (expected for pre-test review)` into the Reviewer context as informational scope context only. Do not ask the Reviewer to report it as a missing gate unless the calling workflow explicitly requires a prior test report.
39
39
  4. **Read dev rules** (if configured): If `.prizmkit/prizm-docs/root.prizm` exists, read it and check for a `RULES:` line. If present, read all referenced `.prizmkit/rules/<layer>-rules.md` files. If `root.prizm` or a referenced rules file does not exist, continue with "No custom dev rules configured — use general best practices."
40
- 5. **Capture workspace diff**: run `git diff` (unstaged) + `git diff --cached` (staged) + `git status` to understand the full scope of changes. For new files in git status, note their paths for the Reviewer to read.
40
+ 5. **Capture workspace context from the active checkout** (do NOT open a separate worktree or clone):
41
+ - Run `git status` to list modified, staged, untracked, and deleted files.
42
+ - Run `git diff` for unstaged changes and `git diff --cached` for staged changes.
43
+ - For new (untracked) files and renamed files in `git status`, note their full paths so the Reviewer can request targeted reads.
44
+ - Capture the current project context: read `.prizmkit/prizm-docs/root.prizm` for relevant RULES, and the artifact's `plan.md` for architecture decisions — this is the reviewer's context baseline.
45
+ - The workspace context is the **primary review scope**. The Reviewer must review this context and request only targeted file reads for lines it needs — it must not rediscover or re-index the whole repository.
41
46
  - If no changes are detected, skip Phase 1 and proceed to Phase 2 with verdict PASS, rounds 0, and an empty findings list. Always write `review-report.md`; downstream pipeline steps use it as the review gate.
42
47
 
43
48
  ## Phase 1: Review-Fix Loop
@@ -115,7 +120,24 @@ When Python is unavailable, apply these rules exactly — they match the script'
115
120
 
116
121
  ### Step 1: Spawn Reviewer Agent
117
122
 
118
- Include the dev rules read in Phase 0 step 4 and the workspace diff/status captured in Phase 0 step 5 in the Reviewer prompt. In Claude Code, spawn the dedicated PrizmKit Reviewer with `subagent_type: prizm-dev-team-reviewer`, `run_in_background=false`, and default/no worktree isolation. Do not request worktree isolation; this prevents AI tool-created `.claude/worktrees/...` subagent worktrees. PrizmKit pipeline-managed linked git worktrees under `.prizmkit/state/worktrees/...` are separate and controlled by `USE_WORKTREE`. Do not route the PrizmKit reviewer flow through `Explore` or a generic code-explorer agent. If the current platform has no dedicated Reviewer agent, perform the review pass in the Main Agent without editing files during that pass. Read `${SKILL_DIR}/references/reviewer-agent-prompt.md` for the full prompt template — fill `{goals}`, `{plan decisions}`, `{scoped test report}`, `{workspace context}`, `{dev rules}`, `{round N}`, and `{round_context}` before spawning.
123
+ Include the dev rules read in Phase 0 step 4 and the workspace context captured in Phase 0 step 5 in the Reviewer prompt.
124
+
125
+ **Reviewer Agent Invocation Contract (MANDATORY):**
126
+
127
+ | Parameter | Value | Rationale |
128
+ |-----------|-------|-----------|
129
+ | `subagent_type` | `prizm-dev-team-reviewer` | Dedicated PrizmKit reviewer agent |
130
+ | `run_in_background` | `false` | Orchestrator waits for review results |
131
+ | `isolation` | **omit or `"none"`** — default/no worktree isolation | Reviewer must review the active checkout's changes, not a separate repository copy |
132
+ | worktree tool access | **Forbidden** — do NOT pass `EnterWorktree` tool; do NOT request or create `.claude/worktrees/...` subagent worktrees | Tool-created worktrees defeat checkout-scoped review |
133
+
134
+ PrizmKit pipeline-managed linked git worktrees under `.prizmkit/state/worktrees/...` are separate infrastructure controlled by `USE_WORKTREE` and are not relevant to the Reviewer invocation.
135
+
136
+ Do not route the PrizmKit reviewer flow through `Explore` or a generic code-explorer agent.
137
+
138
+ **Fallback clause**: If the current platform or Agent-tool invocation path cannot guarantee a no-worktree Reviewer Agent (e.g., the platform does not support `prizm-dev-team-reviewer` or always creates worktree isolation for subagents), perform the review pass **in the Main Agent as a read-only pass** in the active checkout — do NOT edit files during that pass. Use the same review dimensions and output format as the Reviewer prompt template, and write `review-report.md` directly.
139
+
140
+ Read `${SKILL_DIR}/references/reviewer-agent-prompt.md` for the full prompt template — fill `{goals}`, `{plan decisions}`, `{scoped test report}`, `{workspace context}`, `{dev rules}`, `{round N}`, and `{round_context}` before spawning.
119
141
 
120
142
  Round context varies by round:
121
143
  - Round 1: "This is the first review. Examine all changes comprehensively."
@@ -15,7 +15,7 @@ You are the dedicated PrizmKit Reviewer agent (`prizm-dev-team-reviewer`). Revie
15
15
  {summary from artifact_dir/test-report-path.txt and test-report.md, including Scope, Generated / Updated Tests, In-Scope Failures, Baseline Failures, Out of Scope Gaps, Boundary Matrix / Boundary Completion Gate if present, and Verdict. If no scoped report exists for a feature artifact, write "No scoped /prizmkit-test report found (expected for pre-test review)." Treat that absence as informational unless the calling workflow explicitly requires a prior test report.}
16
16
 
17
17
  ## Workspace Context
18
- {git status, unstaged diff summary, staged diff summary, and new-file paths captured by the Main Agent in Phase 0. Use this context as the review scope; request targeted file context only when needed instead of re-running broad discovery.}
18
+ {git status, unstaged diff summary, staged diff summary, and new-file paths captured by the Main Agent in Phase 0 from the active checkout. This is your primary review scope — use this context to understand what changed. Request targeted file reads only when you need specific line-level detail for a finding, not to re-discover or re-index the repository. Do not read files that are not in the workspace context unless you need them to verify impact (callers, dependents, shared patterns). Do not switch to a separate worktree or repository copy — the Main Agent has provided everything needed to understand the change set.}
19
19
 
20
20
  ## Dev Rules (per-layer conventions)
21
21
  {rules from .prizmkit/rules/<layer>-rules.md, or "No custom dev rules configured — use general best practices."}
@@ -28,6 +28,7 @@ Use the orchestrator-provided workspace context as the source of truth for chang
28
28
  - For new files listed in workspace status, read their full content.
29
29
  - For modified files, read enough surrounding context to understand the change.
30
30
  - Do not edit files, stage changes, run test suites, or spawn other agents.
31
+ - Do not request or create worktrees — you are reviewing the active checkout's changes. The orchestrator has already captured the full diff and status context; re-discovering the whole repository is wasteful and unnecessary.
31
32
 
32
33
  ## Review Dimensions
33
34
  Evaluate the changes across these dimensions (focus on what's relevant):
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prizmkit",
3
- "version": "1.1.110",
3
+ "version": "1.1.111",
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": {