prizmkit 1.1.129 → 1.1.130

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.129",
3
- "bundledAt": "2026-07-14T06:20:56.368Z",
4
- "bundledFrom": "60410c0"
2
+ "frameworkVersion": "1.1.130",
3
+ "bundledAt": "2026-07-15T08:17:59.816Z",
4
+ "bundledFrom": "81d54a2"
5
5
  }
@@ -1637,8 +1637,8 @@ def build_replacements(args, feature, features, global_context, script_dir):
1637
1637
  effective_resume = "6"
1638
1638
 
1639
1639
 
1640
- # Browser verification defaults to a mandatory attempt. It is disabled only
1641
- # by BROWSER_VERIFY=false or browser_interaction.enabled=false.
1640
+ # Browser verification is opt-in through a browser_interaction object and
1641
+ # can always be disabled globally with BROWSER_VERIFY=false.
1642
1642
  browser_enabled, browser_tool, browser_verify_steps, browser_blocking = browser_enabled_from_item(feature)
1643
1643
 
1644
1644
  if browser_tool not in ("playwright-cli", "opencli", "auto"):
@@ -362,7 +362,7 @@ def _bugfix_header(pipeline_mode):
362
362
  return title + "\n" + desc + "\n"
363
363
 
364
364
 
365
- def assemble_bugfix_sections(pipeline_mode, sections_dir, browser_enabled, manual_gate):
365
+ def assemble_bugfix_sections(pipeline_mode, sections_dir, browser_enabled, manual_gate, browser_tool="auto"):
366
366
  sections = [
367
367
  ("header", _bugfix_header(pipeline_mode)),
368
368
  ("bugfix-session-context", load_section(sections_dir, "bugfix-session-context.md")),
@@ -380,7 +380,11 @@ def assemble_bugfix_sections(pipeline_mode, sections_dir, browser_enabled, manua
380
380
  ("bugfix-phase-review", load_section(sections_dir, "bugfix-phase-review.md")),
381
381
  ])
382
382
  if browser_enabled:
383
- sections.append(("phase-browser", load_section(sections_dir, "phase-browser-verification-auto.md")))
383
+ browser_section_file = {
384
+ "playwright-cli": "phase-browser-verification.md",
385
+ "opencli": "phase-browser-verification-opencli.md",
386
+ }.get(browser_tool, "phase-browser-verification-auto.md")
387
+ sections.append(("phase-browser", load_section(sections_dir, browser_section_file)))
384
388
  if manual_gate:
385
389
  sections.append(("bugfix-manual-gate", load_section(sections_dir, "bugfix-phase-manual-verification.md")))
386
390
  sections.extend([
@@ -463,6 +467,7 @@ def main():
463
467
  try:
464
468
  sections = assemble_bugfix_sections(
465
469
  pipeline_mode, sections_dir, browser_enabled, manual_gate,
470
+ browser_tool=replacements["{{BROWSER_TOOL}}"],
466
471
  )
467
472
  rendered = render_from_sections(sections, replacements)
468
473
  except FileNotFoundError as exc:
@@ -383,7 +383,7 @@ def _refactor_header(pipeline_mode):
383
383
  return title + "\n" + desc + "\n"
384
384
 
385
385
 
386
- def assemble_refactor_sections(pipeline_mode, sections_dir, browser_enabled):
386
+ def assemble_refactor_sections(pipeline_mode, sections_dir, browser_enabled, browser_tool="auto"):
387
387
  sections = [
388
388
  ("header", _refactor_header(pipeline_mode)),
389
389
  ("refactor-session-context", load_section(sections_dir, "refactor-session-context.md")),
@@ -401,7 +401,11 @@ def assemble_refactor_sections(pipeline_mode, sections_dir, browser_enabled):
401
401
  ("refactor-phase-review", load_section(sections_dir, "refactor-phase-review.md")),
402
402
  ])
403
403
  if browser_enabled:
404
- sections.append(("phase-browser", load_section(sections_dir, "phase-browser-verification-auto.md")))
404
+ browser_section_file = {
405
+ "playwright-cli": "phase-browser-verification.md",
406
+ "opencli": "phase-browser-verification-opencli.md",
407
+ }.get(browser_tool, "phase-browser-verification-auto.md")
408
+ sections.append(("phase-browser", load_section(sections_dir, browser_section_file)))
405
409
  sections.extend([
406
410
  ("refactor-phase-commit-report", load_section(sections_dir, "refactor-phase-commit-report.md")),
407
411
  ("refactor-session-status", load_section(sections_dir, "refactor-session-status.md")),
@@ -469,7 +473,10 @@ def main():
469
473
  sections = []
470
474
  if use_sections:
471
475
  try:
472
- sections = assemble_refactor_sections(pipeline_mode, sections_dir, browser_enabled)
476
+ sections = assemble_refactor_sections(
477
+ pipeline_mode, sections_dir, browser_enabled,
478
+ browser_tool=replacements["{{BROWSER_TOOL}}"],
479
+ )
473
480
  rendered = render_from_sections(sections, replacements)
474
481
  except FileNotFoundError as exc:
475
482
  LOGGER.warning("Refactor section assembly failed (%s), falling back to legacy template", exc)
@@ -327,21 +327,19 @@ def detect_dev_server_port(project_root):
327
327
 
328
328
 
329
329
  def browser_enabled_from_item(item):
330
- """Return browser enabled/tool/steps with mandatory-attempt defaults."""
330
+ """Return browser enabled/tool/steps with explicit opt-in semantics."""
331
331
  browser_interaction = item.get("browser_interaction") if isinstance(item, dict) else None
332
332
  env_value = os.environ.get("BROWSER_VERIFY", "").lower()
333
333
  if env_value == "false":
334
334
  return False, "auto", "", False
335
335
 
336
- enabled = True
336
+ enabled = False
337
337
  tool = "auto"
338
338
  blocking = False
339
339
  steps = []
340
340
 
341
- if isinstance(browser_interaction, bool):
342
- enabled = browser_interaction
343
- elif isinstance(browser_interaction, dict):
344
- enabled = browser_interaction.get("enabled", True)
341
+ if isinstance(browser_interaction, dict):
342
+ enabled = True
345
343
  tool = browser_interaction.get("tool", "auto")
346
344
  blocking = bool(browser_interaction.get("blocking", False))
347
345
  steps = browser_interaction.get("verify_steps", []) or []
@@ -1310,7 +1310,7 @@ class TestFeaturePromptUnifiedFullGuidance:
1310
1310
  assert "### Scoped Feature Test Gate — PrizmKit Test" in prompt
1311
1311
  assert "## Test Failure Recovery Protocol" in prompt
1312
1312
  assert "Boundary Matrix" in prompt
1313
- assert "### Browser Verification — MANDATORY" in prompt
1313
+ assert "### Browser Verification — MANDATORY" not in prompt
1314
1314
  assert "### TERMINAL STOP — Commit Completed" in prompt
1315
1315
  assert "# Dev-Pipeline Session Bootstrap — Tier 1" not in prompt
1316
1316
  assert "# Dev-Pipeline Session Bootstrap — Tier 2" not in prompt
@@ -1420,7 +1420,99 @@ class TestBrowserBlockingPromptPolicy:
1420
1420
  assert "Failures do NOT block the commit" not in prompt
1421
1421
 
1422
1422
 
1423
- class TestManualHybridBugfixPromptPolicy:
1423
+ class TestBrowserOptInPromptPolicy:
1424
+ @staticmethod
1425
+ def _rendered_outputs(tmp_path, browser_interaction=None, env_value=None):
1426
+ previous_env = os.environ.get("BROWSER_VERIFY")
1427
+ if env_value is None:
1428
+ os.environ.pop("BROWSER_VERIFY", None)
1429
+ else:
1430
+ os.environ["BROWSER_VERIFY"] = env_value
1431
+ try:
1432
+ feature_prompt = _render_feature_prompt(
1433
+ tmp_path / "feature", browser_interaction=browser_interaction,
1434
+ )
1435
+ bugfix_prompt = _render_bugfix_prompt(
1436
+ tmp_path / "bugfix",
1437
+ bug_overrides=(
1438
+ {"browser_interaction": browser_interaction}
1439
+ if browser_interaction is not None else None
1440
+ ),
1441
+ )
1442
+ refactor_prompt = _render_refactor_prompt(
1443
+ tmp_path / "refactor",
1444
+ refactor_overrides=(
1445
+ {"browser_interaction": browser_interaction}
1446
+ if browser_interaction is not None else None
1447
+ ),
1448
+ )
1449
+ return feature_prompt, bugfix_prompt, refactor_prompt
1450
+ finally:
1451
+ if previous_env is None:
1452
+ os.environ.pop("BROWSER_VERIFY", None)
1453
+ else:
1454
+ os.environ["BROWSER_VERIFY"] = previous_env
1455
+
1456
+ @staticmethod
1457
+ def _checkpoint_skills(project_root, family):
1458
+ if family == "feature":
1459
+ pattern = "**/workflow-checkpoint.json"
1460
+ elif family == "bugfix":
1461
+ pattern = "**/bugfix/B-001/workflow-checkpoint.json"
1462
+ else:
1463
+ pattern = "**/refactor/R-001/workflow-checkpoint.json"
1464
+ path = next(project_root.glob(pattern))
1465
+ return [step["skill"] for step in json.loads(path.read_text(encoding="utf-8"))["steps"]]
1466
+
1467
+ def test_omitted_browser_interaction_disables_browser_for_all_families(self, tmp_path):
1468
+ prompts = self._rendered_outputs(tmp_path / "omitted")
1469
+
1470
+ for prompt in prompts:
1471
+ assert "Browser Verification" not in prompt
1472
+ assert "playwright-cli" not in prompt
1473
+ assert "opencli" not in prompt
1474
+ assert "development-server" not in prompt
1475
+ assert "browser-dev-server.pid" not in prompt
1476
+
1477
+ assert "browser-verification" not in self._checkpoint_skills(tmp_path / "omitted" / "feature", "feature")
1478
+ assert "browser-verification" not in self._checkpoint_skills(tmp_path / "omitted" / "bugfix", "bugfix")
1479
+ assert "browser-verification" not in self._checkpoint_skills(tmp_path / "omitted" / "refactor", "refactor")
1480
+
1481
+ def test_explicit_browser_interaction_preserves_configuration_for_all_families(self, tmp_path):
1482
+ browser_interaction = {
1483
+ "tool": "playwright-cli",
1484
+ "verify_steps": ["Open the page", "Submit the form"],
1485
+ }
1486
+ prompts = self._rendered_outputs(tmp_path / "explicit", browser_interaction)
1487
+
1488
+ for prompt in prompts:
1489
+ assert "Browser Verification" in prompt
1490
+ assert "Browser Verification (playwright-cli)" in prompt
1491
+ assert "playwright-cli" in prompt
1492
+ assert "opencli" not in prompt
1493
+ assert "Goal 1: Open the page" in prompt
1494
+ assert "Goal 2: Submit the form" in prompt
1495
+
1496
+ assert "browser-verification" in self._checkpoint_skills(tmp_path / "explicit" / "feature", "feature")
1497
+ assert "browser-verification" in self._checkpoint_skills(tmp_path / "explicit" / "bugfix", "bugfix")
1498
+ assert "browser-verification" in self._checkpoint_skills(tmp_path / "explicit" / "refactor", "refactor")
1499
+
1500
+ def test_global_disable_suppresses_explicit_browser_for_all_families(self, tmp_path):
1501
+ prompts = self._rendered_outputs(
1502
+ tmp_path / "global-disable",
1503
+ {"tool": "opencli", "verify_steps": ["Open the page"]},
1504
+ env_value="false",
1505
+ )
1506
+
1507
+ for prompt in prompts:
1508
+ assert "Browser Verification" not in prompt
1509
+ assert "opencli" not in prompt
1510
+ assert "browser-dev-server.pid" not in prompt
1511
+
1512
+ assert "browser-verification" not in self._checkpoint_skills(tmp_path / "global-disable" / "feature", "feature")
1513
+ assert "browser-verification" not in self._checkpoint_skills(tmp_path / "global-disable" / "bugfix", "bugfix")
1514
+ assert "browser-verification" not in self._checkpoint_skills(tmp_path / "global-disable" / "refactor", "refactor")
1515
+
1424
1516
  def test_fresh_manual_bugfix_prompt_stops_before_commit_phase(self, tmp_path):
1425
1517
  prompt = _render_bugfix_prompt(
1426
1518
  tmp_path / "fresh-manual-bugfix",
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.1.129",
2
+ "version": "1.1.130",
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.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prizmkit",
3
- "version": "1.1.129",
3
+ "version": "1.1.130",
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": {