prizmkit 1.1.111 → 1.1.112
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/agents/prizm-dev-team-reviewer.md +12 -1
- package/bundled/dev-pipeline/prizmkit_runtime/interoperability.py +1 -0
- package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +19 -73
- package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +283 -421
- package/bundled/dev-pipeline/scripts/generate-recovery-prompt.py +1 -1
- package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +259 -481
- package/bundled/dev-pipeline/scripts/init-bugfix-pipeline.py +34 -0
- package/bundled/dev-pipeline/scripts/init-pipeline.py +12 -0
- package/bundled/dev-pipeline/scripts/init-refactor-pipeline.py +24 -1
- package/bundled/dev-pipeline/scripts/prompt_framework.py +312 -0
- package/bundled/dev-pipeline/scripts/update-bug-status.py +87 -20
- package/bundled/dev-pipeline/scripts/update-feature-status.py +82 -16
- package/bundled/dev-pipeline/scripts/update-refactor-status.py +68 -28
- package/bundled/dev-pipeline/scripts/utils.py +171 -0
- package/bundled/dev-pipeline/templates/bootstrap-prompt.md +1 -1
- package/bundled/dev-pipeline/templates/bootstrap-tier2.md +10 -18
- package/bundled/dev-pipeline/templates/bootstrap-tier3.md +14 -25
- package/bundled/dev-pipeline/templates/bug-fix-list-schema.json +9 -1
- package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +11 -8
- package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +122 -182
- package/bundled/dev-pipeline/templates/sections/bugfix-artifacts.md +16 -0
- package/bundled/dev-pipeline/templates/sections/bugfix-critical-paths.md +12 -0
- package/bundled/dev-pipeline/templates/sections/bugfix-mission.md +9 -0
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-commit-report.md +31 -0
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-critic.md +22 -0
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-diagnose-plan.md +23 -0
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-implement.md +20 -0
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-init.md +9 -0
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-manual-verification.md +15 -0
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-review.md +29 -0
- package/bundled/dev-pipeline/templates/sections/bugfix-reminders.md +11 -0
- package/bundled/dev-pipeline/templates/sections/bugfix-session-context.md +7 -0
- package/bundled/dev-pipeline/templates/sections/bugfix-session-status.md +31 -0
- package/bundled/dev-pipeline/templates/sections/bugfix-task-contract.md +40 -0
- package/bundled/dev-pipeline/templates/sections/failure-capture.md +2 -2
- package/bundled/dev-pipeline/templates/sections/phase-browser-verification-auto.md +1 -1
- package/bundled/dev-pipeline/templates/sections/refactor-artifacts.md +17 -0
- package/bundled/dev-pipeline/templates/sections/refactor-critical-paths.md +13 -0
- package/bundled/dev-pipeline/templates/sections/refactor-mission.md +9 -0
- package/bundled/dev-pipeline/templates/sections/refactor-phase-commit-report.md +37 -0
- package/bundled/dev-pipeline/templates/sections/refactor-phase-critic.md +22 -0
- package/bundled/dev-pipeline/templates/sections/refactor-phase-implement.md +16 -0
- package/bundled/dev-pipeline/templates/sections/refactor-phase-init.md +9 -0
- package/bundled/dev-pipeline/templates/sections/refactor-phase-plan.md +22 -0
- package/bundled/dev-pipeline/templates/sections/refactor-phase-review.md +19 -0
- package/bundled/dev-pipeline/templates/sections/refactor-reminders.md +11 -0
- package/bundled/dev-pipeline/templates/sections/refactor-session-context.md +7 -0
- package/bundled/dev-pipeline/templates/sections/refactor-session-status.md +28 -0
- package/bundled/dev-pipeline/templates/sections/refactor-task-contract.md +52 -0
- package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +17 -5
- package/bundled/dev-pipeline/tests/test_python_runner_parity.py +197 -0
- package/bundled/dev-pipeline/tests/test_unified_cli.py +6 -2
- package/bundled/dev-pipeline/tests/test_utils.py +66 -1
- package/bundled/skills/_metadata.json +1 -1
- package/bundled/skills/app-planner/SKILL.md +8 -7
- package/bundled/skills/app-planner/references/project-brief-guide.md +2 -2
- package/bundled/skills/app-planner/references/rules/backend/derivation-rules.md +1 -1
- package/bundled/skills/app-planner/references/rules/frontend/derivation-rules.md +1 -1
- package/bundled/skills/app-planner/references/rules-configuration.md +53 -26
- package/bundled/skills/bug-fix-workflow/SKILL.md +191 -336
- package/bundled/skills/bug-planner/SKILL.md +6 -5
- package/bundled/skills/bug-planner/references/critic-and-verification.md +3 -3
- package/bundled/skills/bug-planner/references/schema-validation.md +2 -1
- package/bundled/skills/bug-planner/scripts/validate-bug-list.py +30 -1
- package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +16 -16
- package/bundled/skills/bugfix-pipeline-launcher/references/configuration.md +2 -2
- package/bundled/skills/feature-pipeline-launcher/SKILL.md +19 -11
- package/bundled/skills/feature-planner/SKILL.md +9 -11
- package/bundled/skills/feature-planner/assets/planning-guide.md +1 -1
- package/bundled/skills/feature-planner/references/decomposition-patterns.md +1 -1
- package/bundled/skills/feature-planner/references/error-recovery.md +2 -1
- package/bundled/skills/feature-planner/references/incremental-feature-planning.md +2 -2
- package/bundled/skills/feature-planner/references/new-project-planning.md +35 -39
- package/bundled/skills/feature-planner/scripts/validate-and-generate.py +1 -1
- package/bundled/skills/feature-workflow/SKILL.md +169 -298
- package/bundled/skills/prizmkit/SKILL.md +103 -57
- package/bundled/skills/prizmkit-code-review/SKILL.md +97 -116
- package/bundled/skills/prizmkit-code-review/references/review-report-template.md +1 -0
- package/bundled/skills/prizmkit-code-review/references/reviewer-agent-prompt.md +12 -1
- package/bundled/skills/prizmkit-committer/SKILL.md +59 -47
- package/bundled/skills/prizmkit-deploy/SKILL.md +162 -381
- package/bundled/skills/prizmkit-deploy/references/ssh-adapter-flow.md +220 -0
- package/bundled/skills/prizmkit-implement/SKILL.md +51 -40
- package/bundled/skills/prizmkit-init/SKILL.md +4 -3
- package/bundled/skills/prizmkit-plan/SKILL.md +85 -70
- package/bundled/skills/prizmkit-prizm-docs/SKILL.md +94 -73
- package/bundled/skills/prizmkit-prizm-docs/assets/prizm-docs-format.md +20 -18
- package/bundled/skills/prizmkit-prizm-docs/references/op-update.md +18 -14
- package/bundled/skills/prizmkit-retrospective/SKILL.md +56 -48
- package/bundled/skills/prizmkit-retrospective/references/structural-sync-steps.md +79 -27
- package/bundled/skills/prizmkit-test/SKILL.md +138 -141
- package/bundled/skills/prizmkit-test/references/examples.md +10 -8
- package/bundled/skills/prizmkit-test/references/test-generation-steps.md +1 -1
- package/bundled/skills/prizmkit-test/references/test-report-template.md +2 -2
- package/bundled/skills/recovery-workflow/SKILL.md +195 -256
- package/bundled/skills/recovery-workflow/evals/evals.json +21 -13
- package/bundled/skills/recovery-workflow/references/detection.md +48 -39
- package/bundled/skills/recovery-workflow/scripts/detect-recovery-state.py +258 -322
- package/bundled/skills/refactor-pipeline-launcher/SKILL.md +29 -11
- package/bundled/skills/refactor-planner/SKILL.md +2 -2
- package/bundled/skills/refactor-planner/references/behavior-preservation.md +24 -19
- package/bundled/skills/refactor-planner/references/fast-path.md +2 -4
- package/bundled/skills/refactor-planner/references/planning-phases.md +2 -2
- package/bundled/skills/refactor-workflow/SKILL.md +173 -307
- package/package.json +1 -1
- package/src/scaffold.js +5 -0
- package/bundled/dev-pipeline/templates/agent-prompts/reviewer-review.md +0 -6
- package/bundled/skills/feature-workflow/references/brainstorm-guide.md +0 -137
- package/bundled/skills/refactor-workflow/references/brainstorm-guide.md +0 -116
|
@@ -61,6 +61,35 @@ def _write_feature_list(paths):
|
|
|
61
61
|
)
|
|
62
62
|
|
|
63
63
|
|
|
64
|
+
def _write_family_plan(paths, kind, items):
|
|
65
|
+
plan_config = {
|
|
66
|
+
"feature": (paths.feature_plan, "features", "dev-pipeline-feature-list-v1"),
|
|
67
|
+
"bugfix": (paths.bugfix_plan, "bugs", "dev-pipeline-bug-fix-list-v1"),
|
|
68
|
+
"refactor": (paths.refactor_plan, "refactors", "dev-pipeline-refactor-list-v1"),
|
|
69
|
+
}
|
|
70
|
+
plan_path, list_key, schema = plan_config[kind]
|
|
71
|
+
plan_path.write_text(
|
|
72
|
+
json.dumps({"$schema": schema, "project_name": "Fake", list_key: items}),
|
|
73
|
+
encoding="utf-8",
|
|
74
|
+
)
|
|
75
|
+
return plan_path
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _get_next_result(kind, paths):
|
|
79
|
+
from prizmkit_runtime.runner_models import family_for, parse_invocation
|
|
80
|
+
from prizmkit_runtime.runner_status import get_next
|
|
81
|
+
|
|
82
|
+
family = family_for(kind, paths)
|
|
83
|
+
invocation = parse_invocation(family, "run", ())
|
|
84
|
+
invocation = invocation.__class__(**{**invocation.__dict__, "list_path": family.plan_path})
|
|
85
|
+
return get_next(family, invocation, paths.project_root)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _item_id_from_next_result(kind, result):
|
|
89
|
+
key = {"feature": "feature_id", "bugfix": "bug_id", "refactor": "refactor_id"}[kind]
|
|
90
|
+
return result.data[key]
|
|
91
|
+
|
|
92
|
+
|
|
64
93
|
def test_retired_shell_runtime_files_are_absent_from_source():
|
|
65
94
|
retired = [
|
|
66
95
|
"run-feature.sh",
|
|
@@ -360,6 +389,174 @@ def test_status_bridge_preserves_feature_filter_selection(tmp_path):
|
|
|
360
389
|
assert update.data["context_overflow_count"] == 1
|
|
361
390
|
|
|
362
391
|
|
|
392
|
+
def test_status_bridge_uses_stable_dependency_order_across_pipeline_families(tmp_path):
|
|
393
|
+
cases = {
|
|
394
|
+
"feature": [
|
|
395
|
+
{"id": "F-001", "title": "Blocked", "status": "pending", "priority": "critical", "dependencies": ["F-002"]},
|
|
396
|
+
{"id": "F-002", "title": "First", "status": "pending", "priority": "low", "dependencies": []},
|
|
397
|
+
{"id": "F-003", "title": "Independent", "status": "pending", "priority": "high", "dependencies": []},
|
|
398
|
+
],
|
|
399
|
+
"bugfix": [
|
|
400
|
+
{"id": "B-001", "title": "Blocked", "status": "pending", "severity": "critical", "priority": "high", "error_source": {"type": "user_report"}, "verification_type": "manual", "acceptance_criteria": ["fixed"], "dependencies": ["B-002"]},
|
|
401
|
+
{"id": "B-002", "title": "First", "status": "pending", "severity": "low", "priority": "low", "error_source": {"type": "user_report"}, "verification_type": "manual", "acceptance_criteria": ["fixed"], "dependencies": []},
|
|
402
|
+
{"id": "B-003", "title": "Independent", "status": "pending", "severity": "high", "priority": "high", "error_source": {"type": "user_report"}, "verification_type": "manual", "acceptance_criteria": ["fixed"], "dependencies": []},
|
|
403
|
+
],
|
|
404
|
+
"refactor": [
|
|
405
|
+
{"id": "R-001", "title": "Blocked", "description": "blocked", "scope": {}, "type": "restructure", "priority": "critical", "complexity": "high", "behavior_preservation": {"strategy": "test-gate"}, "acceptance_criteria": ["done"], "dependencies": ["R-002"], "status": "pending"},
|
|
406
|
+
{"id": "R-002", "title": "First", "description": "first", "scope": {}, "type": "extract", "priority": "low", "complexity": "low", "behavior_preservation": {"strategy": "test-gate"}, "acceptance_criteria": ["done"], "dependencies": [], "status": "pending"},
|
|
407
|
+
{"id": "R-003", "title": "Independent", "description": "independent", "scope": {}, "type": "simplify", "priority": "high", "complexity": "medium", "behavior_preservation": {"strategy": "test-gate"}, "acceptance_criteria": ["done"], "dependencies": [], "status": "pending"},
|
|
408
|
+
],
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
for kind, items in cases.items():
|
|
412
|
+
paths = _make_paths(tmp_path / f"stable-{kind}")
|
|
413
|
+
_write_family_plan(paths, kind, items)
|
|
414
|
+
|
|
415
|
+
result = _get_next_result(kind, paths)
|
|
416
|
+
|
|
417
|
+
assert result.ok
|
|
418
|
+
assert _item_id_from_next_result(kind, result) == {"feature": "F-002", "bugfix": "B-002", "refactor": "R-002"}[kind]
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
def test_status_bridge_prefers_in_progress_items_with_satisfied_dependencies(tmp_path):
|
|
422
|
+
cases = {
|
|
423
|
+
"feature": [
|
|
424
|
+
{"id": "F-001", "title": "Done", "status": "completed", "priority": "low", "dependencies": []},
|
|
425
|
+
{"id": "F-002", "title": "Resume", "status": "in_progress", "priority": "low", "dependencies": ["F-001"]},
|
|
426
|
+
{"id": "F-003", "title": "Pending", "status": "pending", "priority": "critical", "dependencies": []},
|
|
427
|
+
],
|
|
428
|
+
"bugfix": [
|
|
429
|
+
{"id": "B-001", "title": "Done", "status": "completed", "severity": "low", "priority": "low", "error_source": {"type": "user_report"}, "verification_type": "manual", "acceptance_criteria": ["fixed"], "dependencies": []},
|
|
430
|
+
{"id": "B-002", "title": "Resume", "status": "in_progress", "severity": "low", "priority": "low", "error_source": {"type": "user_report"}, "verification_type": "manual", "acceptance_criteria": ["fixed"], "dependencies": ["B-001"]},
|
|
431
|
+
{"id": "B-003", "title": "Pending", "status": "pending", "severity": "critical", "priority": "high", "error_source": {"type": "user_report"}, "verification_type": "manual", "acceptance_criteria": ["fixed"], "dependencies": []},
|
|
432
|
+
],
|
|
433
|
+
"refactor": [
|
|
434
|
+
{"id": "R-001", "title": "Done", "description": "done", "scope": {}, "type": "extract", "priority": "low", "complexity": "low", "behavior_preservation": {"strategy": "test-gate"}, "acceptance_criteria": ["done"], "dependencies": [], "status": "completed"},
|
|
435
|
+
{"id": "R-002", "title": "Resume", "description": "resume", "scope": {}, "type": "extract", "priority": "low", "complexity": "low", "behavior_preservation": {"strategy": "test-gate"}, "acceptance_criteria": ["done"], "dependencies": ["R-001"], "status": "in_progress"},
|
|
436
|
+
{"id": "R-003", "title": "Pending", "description": "pending", "scope": {}, "type": "migrate", "priority": "critical", "complexity": "high", "behavior_preservation": {"strategy": "test-gate"}, "acceptance_criteria": ["done"], "dependencies": [], "status": "pending"},
|
|
437
|
+
],
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
for kind, items in cases.items():
|
|
441
|
+
paths = _make_paths(tmp_path / f"resume-{kind}")
|
|
442
|
+
_write_family_plan(paths, kind, items)
|
|
443
|
+
|
|
444
|
+
result = _get_next_result(kind, paths)
|
|
445
|
+
|
|
446
|
+
assert result.ok
|
|
447
|
+
assert _item_id_from_next_result(kind, result) == {"feature": "F-002", "bugfix": "B-002", "refactor": "R-002"}[kind]
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
def test_refactor_dependencies_require_completed_not_other_terminal_statuses(tmp_path):
|
|
451
|
+
paths = _make_paths(tmp_path)
|
|
452
|
+
_write_family_plan(
|
|
453
|
+
paths,
|
|
454
|
+
"refactor",
|
|
455
|
+
[
|
|
456
|
+
{"id": "R-001", "title": "Failed Dependency", "description": "failed", "scope": {}, "type": "extract", "priority": "high", "complexity": "low", "behavior_preservation": {"strategy": "test-gate"}, "acceptance_criteria": ["done"], "dependencies": [], "status": "failed"},
|
|
457
|
+
{"id": "R-002", "title": "Blocked", "description": "blocked", "scope": {}, "type": "restructure", "priority": "high", "complexity": "medium", "behavior_preservation": {"strategy": "test-gate"}, "acceptance_criteria": ["done"], "dependencies": ["R-001"], "status": "pending"},
|
|
458
|
+
],
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
result = _get_next_result("refactor", paths)
|
|
462
|
+
|
|
463
|
+
assert result.ok
|
|
464
|
+
assert result.text == "PIPELINE_BLOCKED"
|
|
465
|
+
assert result.data is None
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
def test_init_scripts_reject_missing_self_and_cycle_dependencies(tmp_path):
|
|
469
|
+
script_config = {
|
|
470
|
+
"feature": ("init-pipeline.py", "--feature-list", "F-", "missing dependency"),
|
|
471
|
+
"bugfix": ("init-bugfix-pipeline.py", "--bug-list", "B-", "missing dependency"),
|
|
472
|
+
"refactor": ("init-refactor-pipeline.py", "--refactor-list", "R-", "missing dependency"),
|
|
473
|
+
}
|
|
474
|
+
scenarios = {
|
|
475
|
+
"missing": lambda kind, prefix: _family_items(kind, [
|
|
476
|
+
(f"{prefix}001", [f"{prefix}999"], "pending"),
|
|
477
|
+
]),
|
|
478
|
+
"self": lambda kind, prefix: _family_items(kind, [
|
|
479
|
+
(f"{prefix}001", [f"{prefix}001"], "pending"),
|
|
480
|
+
]),
|
|
481
|
+
"cycle": lambda kind, prefix: _family_items(kind, [
|
|
482
|
+
(f"{prefix}001", [f"{prefix}002"], "pending"),
|
|
483
|
+
(f"{prefix}002", [f"{prefix}001"], "pending"),
|
|
484
|
+
]),
|
|
485
|
+
}
|
|
486
|
+
expected_fragments = {
|
|
487
|
+
"missing": "missing dependency",
|
|
488
|
+
"self": "cannot depend on itself",
|
|
489
|
+
"cycle": "Dependency cycle detected",
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
for kind, (script_name, list_arg, prefix, _missing_fragment) in script_config.items():
|
|
493
|
+
for scenario, build_items in scenarios.items():
|
|
494
|
+
paths = _make_paths(tmp_path / f"init-{kind}-{scenario}")
|
|
495
|
+
plan_path = _write_family_plan(paths, kind, build_items(kind, prefix))
|
|
496
|
+
result = subprocess.run(
|
|
497
|
+
[
|
|
498
|
+
PYTHON_310_PLUS,
|
|
499
|
+
str(PIPELINE_ROOT / "scripts" / script_name),
|
|
500
|
+
list_arg,
|
|
501
|
+
str(plan_path),
|
|
502
|
+
"--state-dir",
|
|
503
|
+
str(paths.project_root / ".prizmkit" / "state" / f"init-{kind}-{scenario}"),
|
|
504
|
+
],
|
|
505
|
+
cwd=REPO_ROOT,
|
|
506
|
+
stdout=subprocess.PIPE,
|
|
507
|
+
stderr=subprocess.PIPE,
|
|
508
|
+
text=True,
|
|
509
|
+
check=False,
|
|
510
|
+
)
|
|
511
|
+
|
|
512
|
+
assert result.returncode == 1, (kind, scenario, result.stdout, result.stderr)
|
|
513
|
+
payload = json.loads(result.stdout)
|
|
514
|
+
assert any(expected_fragments[scenario] in error for error in payload["errors"])
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
def _family_items(kind, entries):
|
|
518
|
+
items = []
|
|
519
|
+
for item_id, dependencies, status in entries:
|
|
520
|
+
if kind == "feature":
|
|
521
|
+
items.append({
|
|
522
|
+
"id": item_id,
|
|
523
|
+
"title": item_id,
|
|
524
|
+
"description": "Implement this feature with enough detail for initialization validation.",
|
|
525
|
+
"priority": "medium",
|
|
526
|
+
"dependencies": dependencies,
|
|
527
|
+
"acceptance_criteria": ["done"],
|
|
528
|
+
"status": status,
|
|
529
|
+
})
|
|
530
|
+
elif kind == "bugfix":
|
|
531
|
+
items.append({
|
|
532
|
+
"id": item_id,
|
|
533
|
+
"title": item_id,
|
|
534
|
+
"description": "Bug description",
|
|
535
|
+
"severity": "medium",
|
|
536
|
+
"priority": "medium",
|
|
537
|
+
"error_source": {"type": "user_report"},
|
|
538
|
+
"verification_type": "manual",
|
|
539
|
+
"acceptance_criteria": ["fixed"],
|
|
540
|
+
"dependencies": dependencies,
|
|
541
|
+
"status": status,
|
|
542
|
+
})
|
|
543
|
+
else:
|
|
544
|
+
items.append({
|
|
545
|
+
"id": item_id,
|
|
546
|
+
"title": item_id,
|
|
547
|
+
"description": "Refactor description",
|
|
548
|
+
"scope": {},
|
|
549
|
+
"type": "extract",
|
|
550
|
+
"priority": "medium",
|
|
551
|
+
"complexity": "low",
|
|
552
|
+
"behavior_preservation": {"strategy": "test-gate"},
|
|
553
|
+
"acceptance_criteria": ["done"],
|
|
554
|
+
"dependencies": dependencies,
|
|
555
|
+
"status": status,
|
|
556
|
+
})
|
|
557
|
+
return items
|
|
558
|
+
|
|
559
|
+
|
|
363
560
|
def test_status_bridge_passes_max_retries_from_invocation_or_environment(monkeypatch, tmp_path):
|
|
364
561
|
import subprocess
|
|
365
562
|
from prizmkit_runtime.runner_models import family_for, parse_invocation
|
|
@@ -458,7 +458,7 @@ def test_interoperability_preserves_existing_utility_mapping():
|
|
|
458
458
|
explicitly_library_only = set(UTILITY_LIBRARY_ONLY)
|
|
459
459
|
|
|
460
460
|
assert actual_scripts == mapped_scripts | explicitly_library_only
|
|
461
|
-
assert explicitly_library_only == {"continuation.py", "utils.py"}
|
|
461
|
+
assert explicitly_library_only == {"continuation.py", "prompt_framework.py", "utils.py"}
|
|
462
462
|
assert "parse-stream-progress.py" in mapped_scripts
|
|
463
463
|
assert "prizmkit-test-gate.py" not in mapped_scripts
|
|
464
464
|
|
|
@@ -803,7 +803,11 @@ def test_headless_subagent_prompt_guidance_keeps_no_worktree_boundary_visible():
|
|
|
803
803
|
|
|
804
804
|
for path in guidance_files:
|
|
805
805
|
text = path.read_text(encoding="utf-8")
|
|
806
|
-
assert
|
|
806
|
+
assert (
|
|
807
|
+
"default/no worktree" in text
|
|
808
|
+
or "current-workspace / active-checkout / no-worktree" in text
|
|
809
|
+
or "current workspace / active checkout / no worktree" in text
|
|
810
|
+
), path
|
|
807
811
|
assert "do not request or create" in text.lower() or "do not request worktree isolation" in text.lower(), path
|
|
808
812
|
assert ".claude/worktrees/..." in text, path
|
|
809
813
|
assert ".prizmkit/state/worktrees/..." in text, path
|
|
@@ -4,7 +4,13 @@ import json
|
|
|
4
4
|
import os
|
|
5
5
|
import tempfile
|
|
6
6
|
|
|
7
|
-
from utils import
|
|
7
|
+
from utils import (
|
|
8
|
+
load_json_file,
|
|
9
|
+
write_json_file,
|
|
10
|
+
pad_right,
|
|
11
|
+
_build_progress_bar,
|
|
12
|
+
stable_topological_order,
|
|
13
|
+
)
|
|
8
14
|
|
|
9
15
|
|
|
10
16
|
# ---------------------------------------------------------------------------
|
|
@@ -80,3 +86,62 @@ class TestBuildProgressBar:
|
|
|
80
86
|
def test_fifty_percent(self):
|
|
81
87
|
bar = _build_progress_bar(50, width=10)
|
|
82
88
|
assert "50%" in bar
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
# ---------------------------------------------------------------------------
|
|
92
|
+
# stable_topological_order
|
|
93
|
+
# ---------------------------------------------------------------------------
|
|
94
|
+
|
|
95
|
+
class TestStableTopologicalOrder:
|
|
96
|
+
def test_dependencies_precede_dependents_with_stable_tie_breaking(self):
|
|
97
|
+
items = [
|
|
98
|
+
{"id": "F-001", "dependencies": []},
|
|
99
|
+
{"id": "F-002", "dependencies": ["F-001"]},
|
|
100
|
+
{"id": "F-003", "dependencies": []},
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
ordered, errors = stable_topological_order(items, item_label="Feature")
|
|
104
|
+
|
|
105
|
+
assert errors == []
|
|
106
|
+
assert [item["id"] for item in ordered] == ["F-001", "F-002", "F-003"]
|
|
107
|
+
|
|
108
|
+
def test_unrelated_items_preserve_original_list_order(self):
|
|
109
|
+
items = [
|
|
110
|
+
{"id": "F-010", "dependencies": []},
|
|
111
|
+
{"id": "F-002", "dependencies": []},
|
|
112
|
+
{"id": "F-007", "dependencies": []},
|
|
113
|
+
]
|
|
114
|
+
|
|
115
|
+
ordered, errors = stable_topological_order(items, item_label="Feature")
|
|
116
|
+
|
|
117
|
+
assert errors == []
|
|
118
|
+
assert [item["id"] for item in ordered] == ["F-010", "F-002", "F-007"]
|
|
119
|
+
|
|
120
|
+
def test_missing_dependency_is_blocking_error(self):
|
|
121
|
+
items = [
|
|
122
|
+
{"id": "B-001", "dependencies": ["B-999"]},
|
|
123
|
+
]
|
|
124
|
+
|
|
125
|
+
_ordered, errors = stable_topological_order(items, item_label="Bug")
|
|
126
|
+
|
|
127
|
+
assert errors == ["Bug 'B-001' depends on missing dependency 'B-999'"]
|
|
128
|
+
|
|
129
|
+
def test_self_dependency_is_blocking_error(self):
|
|
130
|
+
items = [
|
|
131
|
+
{"id": "R-001", "dependencies": ["R-001"]},
|
|
132
|
+
]
|
|
133
|
+
|
|
134
|
+
_ordered, errors = stable_topological_order(items, item_label="Refactor")
|
|
135
|
+
|
|
136
|
+
assert errors == ["Refactor 'R-001' cannot depend on itself"]
|
|
137
|
+
|
|
138
|
+
def test_cycle_reports_chain(self):
|
|
139
|
+
items = [
|
|
140
|
+
{"id": "F-001", "dependencies": ["F-003"]},
|
|
141
|
+
{"id": "F-002", "dependencies": ["F-001"]},
|
|
142
|
+
{"id": "F-003", "dependencies": ["F-002"]},
|
|
143
|
+
]
|
|
144
|
+
|
|
145
|
+
_ordered, errors = stable_topological_order(items, item_label="Feature")
|
|
146
|
+
|
|
147
|
+
assert errors == ["Dependency cycle detected: F-001 -> F-003 -> F-002 -> F-001"]
|
|
@@ -35,10 +35,11 @@ If you believe the task is better suited for a different workflow, you MUST:
|
|
|
35
35
|
|
|
36
36
|
**Your ONLY writable outputs are:**
|
|
37
37
|
1. `.prizmkit/plans/project-brief.md` (`.prizmkit/plans/` — accumulated project context brief)
|
|
38
|
-
2.
|
|
39
|
-
3.
|
|
40
|
-
4.
|
|
41
|
-
5. `.prizmkit/
|
|
38
|
+
2. `.prizmkit/plans/project-brief.draft.md` — draft only when user exits before completion
|
|
39
|
+
3. Project conventions and architecture decisions appended to `AGENTS.md` / `CLAUDE.md` / `CODEBUDDY.md` (with user consent)
|
|
40
|
+
4. Infrastructure configuration (database conventions, deployment config, **cloud services**) appended to `AGENTS.md` / `CLAUDE.md` / `CODEBUDDY.md` `### Infrastructure` section
|
|
41
|
+
5. `.prizmkit/rules/<layer>-rules.md` — per-layer development rules generated by Rules Configuration
|
|
42
|
+
6. `.prizmkit/prizm-docs/root.prizm` — `RULES:` pointer line only (not other root.prizm content; created minimally if absent)
|
|
42
43
|
|
|
43
44
|
**Project instruction file selection**:
|
|
44
45
|
- If `.prizmkit/manifest.json` exists, use its `platform` field as the source of truth.
|
|
@@ -172,7 +173,7 @@ Route by answer:
|
|
|
172
173
|
- After brainstorm completes, use `AskUserQuestion`: "Ideas are taking shape. What's next?"
|
|
173
174
|
- **Continue to project planning (Recommended)** — capture tech stack, conventions, and architecture decisions
|
|
174
175
|
- **Continue refining** — keep brainstorming
|
|
175
|
-
- **Save draft & exit** — save progress to
|
|
176
|
+
- **Save draft & exit** — save progress to `.prizmkit/plans/project-brief.draft.md`
|
|
176
177
|
- **Checkpoints in explore mode**: CP-AP-0 (required), CP-AP-1 (required), CP-AP-2 (from brainstorm output), CP-AP-3 (only if user proceeds to Phase 2), CP-AP-4 and CP-AP-5 (only if user transitions to produce mode)
|
|
177
178
|
- **Generate project context only** → Enter **Quick Context Mode** (brownfield only):
|
|
178
179
|
- Run Project State Detection → if greenfield, redirect to produce mode
|
|
@@ -271,7 +272,7 @@ When the session appears to be ending:
|
|
|
271
272
|
1. **Remind**: "You set out to produce a project plan but `.prizmkit/plans/project-brief.md` isn't complete yet."
|
|
272
273
|
2. Use `AskUserQuestion`: "How would you like to proceed?"
|
|
273
274
|
- **Continue to completion (Recommended)** — finish the project brief
|
|
274
|
-
- **Save draft & exit** — write current progress as draft to `.prizmkit/
|
|
275
|
+
- **Save draft & exit** — write current progress as draft to `.prizmkit/plans/project-brief.draft.md`
|
|
275
276
|
- **Abandon** — exit without saving
|
|
276
277
|
|
|
277
278
|
## Completion Summary
|
|
@@ -281,7 +282,7 @@ After all checkpoints pass, present a summary and end the session:
|
|
|
281
282
|
1. **Summary** (as text): List all project-level artifacts produced:
|
|
282
283
|
- Project conventions → `AGENTS.md` / `CLAUDE.md` / `CODEBUDDY.md` `### Project Conventions`
|
|
283
284
|
- Infrastructure config → `AGENTS.md` / `CLAUDE.md` / `CODEBUDDY.md` `### Infrastructure` (database conventions + deployment config)
|
|
284
|
-
- Tech stack → `.prizmkit/config.json`
|
|
285
|
+
- Tech stack assumptions → captured in `.prizmkit/plans/project-brief.md` and reused from `.prizmkit/config.json` when it already exists
|
|
285
286
|
- Architecture decisions (if any) → `AGENTS.md` / `CLAUDE.md` / `CODEBUDDY.md` `### Architecture Decisions`
|
|
286
287
|
- Dev rules (if configured) → `.prizmkit/rules/<layer>-rules.md` (with `root.prizm` `RULES:` pointer)
|
|
287
288
|
- Project brief → `.prizmkit/plans/project-brief.md`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Project Brief Template
|
|
2
2
|
|
|
3
|
-
> Capture the user's key product ideas as a simple checklist. Each line is one idea.
|
|
3
|
+
> Capture the user's key product ideas as a simple checklist. Each line is one idea. Downstream planners can load this file as project-level context.
|
|
4
4
|
|
|
5
5
|
## File Location
|
|
6
6
|
|
|
@@ -70,7 +70,7 @@ Stop asking when ALL of these are clear:
|
|
|
70
70
|
2. **Users** — who will use it and in what context
|
|
71
71
|
3. **Core features** — at least 3 concrete capabilities (not vague aspirations)
|
|
72
72
|
4. **Boundaries** — what's in scope vs. out of scope for V1
|
|
73
|
-
5. **Technical direction** — enough to
|
|
73
|
+
5. **Technical direction** — enough to describe stack assumptions in the brief, or explicitly mark them deferred
|
|
74
74
|
|
|
75
75
|
If the user gives short/vague answers, don't accept them — rephrase and ask again. A weak brief leads to weak specs downstream.
|
|
76
76
|
|
|
@@ -614,6 +614,6 @@ This table verifies every template placeholder has at least one derivation rule
|
|
|
614
614
|
| `{{ ai_dependency_rule }}` | Yes | No | D-AI-DEP-STRICT-01, D-AI-DEP-ANNOUNCE-01, D-AI-DEP-LOOSE-01 |
|
|
615
615
|
| `{{ ai_breaking_change_rule }}` | Yes | No | D-AI-BREAK-STRICT-01, D-AI-BREAK-LOOSE-01 |
|
|
616
616
|
| `{{ ai_config_rule }}` | Yes | No | D-AI-CONFIG-STRICT-01, D-AI-CONFIG-SYNC-01 |
|
|
617
|
-
| `{{ FIXED_RULES_* }}` | No | Yes | `references/fixed-rules.md` |
|
|
617
|
+
| `{{ FIXED_RULES_* }}` | No | Yes | `references/rules/backend/fixed-rules.md` |
|
|
618
618
|
| `{{ deny_list_summary }}` | N/A | N/A | AI auto-generates in Phase 4 |
|
|
619
619
|
| `{{ recommended_libs }}` | N/A | N/A | AI auto-generates in Phase 4 |
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> This file defines the mapping "Phase 2 answers → rule blocks auto-injected into frontend-rules.md". The AI reads this file in Phase 3 — **do not ask the user again**.
|
|
4
4
|
>
|
|
5
|
-
> Usage: Each rule block has a unique ID (e.g., `D-TAILWIND-01`). After matching the user's answer, inject the "rule body" verbatim into the corresponding `{{ ... }}` placeholder in [
|
|
5
|
+
> Usage: Each rule block has a unique ID (e.g., `D-TAILWIND-01`). After matching the user's answer, inject the "rule body" verbatim into the corresponding `{{ ... }}` placeholder in [`template.md`](template.md).
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -1,6 +1,28 @@
|
|
|
1
|
-
# Rules Configuration —
|
|
1
|
+
# Rules Configuration — Full Procedure
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Procedure for configuring per-layer development rules during app planning.
|
|
4
|
+
|
|
5
|
+
## Step 1: Detect Layers
|
|
6
|
+
|
|
7
|
+
- Prefer `.prizmkit/config.json` `tech_stack` when present.
|
|
8
|
+
- Otherwise infer layers from project files:
|
|
9
|
+
- frontend: `package.json` plus React/Vue/Svelte/Next/Vite indicators
|
|
10
|
+
- backend: Express/FastAPI/Django/Go/Java service indicators
|
|
11
|
+
- database: migrations, ORM config, schema files, or DB dependencies
|
|
12
|
+
- mobile: Flutter/React Native/iOS/Android indicators
|
|
13
|
+
- Present detected layers with `AskUserQuestion`; include "Skip rules configuration" as a non-blocking option.
|
|
14
|
+
|
|
15
|
+
## Step 2: Choose Configuration Mode
|
|
16
|
+
|
|
17
|
+
- Quick mode asks only manifest groups marked `quick_mode: true`.
|
|
18
|
+
- Full mode asks every manifest group in order.
|
|
19
|
+
- Skip mode records no rules file for that layer and continues without reminders.
|
|
20
|
+
|
|
21
|
+
## Step 3: Ask Questions with AskUserQuestion
|
|
22
|
+
|
|
23
|
+
- Convert each question-bank option group into selectable `AskUserQuestion` options.
|
|
24
|
+
- Do not ask users to type letter shortcuts such as `A` or `1`; those shortcuts are legacy notation in question banks only.
|
|
25
|
+
- Every non-essential question includes a skip/defer option.
|
|
4
26
|
|
|
5
27
|
## Step 4: Configure Each Selected Layer — Full Q&A Workflow
|
|
6
28
|
|
|
@@ -10,37 +32,42 @@ For each selected layer, run the 4-phase rule generation pattern:
|
|
|
10
32
|
|
|
11
33
|
- Read `${SKILL_DIR}/references/rules/<layer>/fixed-rules.md` — industry-consensus rules injected without asking
|
|
12
34
|
- Read `${SKILL_DIR}/references/rules/<layer>/question-bank.md` — interactive questions organized in groups (G1->G10): authoritative source for question text, options, "Recommended" markers, and Notes
|
|
13
|
-
- Read `${SKILL_DIR}/references/rules/<layer>/question-manifest.json` — machine-readable structure for this layer.
|
|
35
|
+
- Read `${SKILL_DIR}/references/rules/<layer>/question-manifest.json` — machine-readable structure for this layer. This is the asking checklist. It lists, per question: `group`, `required`, `maps_to`, `required_if`, `auto_derived_when`, and `options_vary_by`. Its `groups[]` carries each group's `quick_mode` flag, and `template_placeholders` is the expected-set for the Phase D self-check. Use the manifest to track coverage; use question-bank.md for the actual wording you present to the user. If a question id in one file is absent from the other, trust question-bank.md for content and note the drift.
|
|
14
36
|
|
|
15
37
|
### Phase B — Interactive Q&A
|
|
16
38
|
|
|
17
|
-
- Ask questions one group at a time (max 3 questions per message), as defined in question-bank.md
|
|
18
|
-
- Each question shows a "Recommended" option to reduce decision cost
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
- `required_if: "<expr>"` — ask this question only when the expression over prior answers holds
|
|
23
|
-
- `auto_derived_when: "<expr>"` — when
|
|
24
|
-
- `options_vary_by: "<Qid>"` — the question is still asked; only its option list depends on that prior answer
|
|
25
|
-
-
|
|
26
|
-
|
|
27
|
-
- `all recommended` -> adopt all recommended for all remaining groups
|
|
28
|
-
- `skip` -> mark current group as "Not required at this stage"
|
|
39
|
+
- Ask questions one group at a time (max 3 questions per message), as defined in question-bank.md.
|
|
40
|
+
- Each question shows a "Recommended" option to reduce decision cost.
|
|
41
|
+
- Quick mode: ask only the groups whose `quick_mode` is `true` in `question-manifest.json`. All other groups adopt recommended defaults silently.
|
|
42
|
+
- Full mode: ask all groups in manifest order (`groups[]`).
|
|
43
|
+
- Conditional questions:
|
|
44
|
+
- `required_if: "<expr>"` — ask this question only when the expression over prior answers holds. If false, the question is legitimately skipped.
|
|
45
|
+
- `auto_derived_when: "<expr>"` — when true, fill the mapped placeholder(s) from the prior answer without asking. Count as satisfied, not skipped.
|
|
46
|
+
- `options_vary_by: "<Qid>"` — the question is still asked; only its option list depends on that prior answer.
|
|
47
|
+
- Legacy shortcut labels in question banks such as `recommended`, `default`, or `skip` are converted into selectable options in `AskUserQuestion`.
|
|
48
|
+
- Do not ask the user to type these commands manually; selectable options prevent merged or missed decisions.
|
|
29
49
|
- Record answers in memory after each group. Track which manifest questions are answered, auto-derived, or conditionally skipped so Phase D can verify coverage.
|
|
30
50
|
|
|
31
51
|
### Phase C — Auto-derivation
|
|
32
52
|
|
|
33
|
-
- Read `${SKILL_DIR}/references/rules/<layer>/derivation-rules.md
|
|
34
|
-
- Match user answers against the trigger map using keyword matching
|
|
35
|
-
- Derive platform-specific rules without asking the user
|
|
53
|
+
- Read `${SKILL_DIR}/references/rules/<layer>/derivation-rules.md`.
|
|
54
|
+
- Match user answers against the trigger map using keyword matching.
|
|
55
|
+
- Derive platform-specific rules without asking the user.
|
|
36
56
|
|
|
37
57
|
### Phase D — Render and Write
|
|
38
58
|
|
|
39
|
-
- Read `${SKILL_DIR}/references/rules/<layer>/template.md
|
|
40
|
-
- Fill all template placeholders with accumulated content from Phases A+B+C
|
|
41
|
-
-
|
|
42
|
-
1.
|
|
43
|
-
2.
|
|
44
|
-
- Generate Appendix A (Deny List) and Appendix B (Recommended Tools) per template instructions
|
|
45
|
-
- Create `.prizmkit/rules/` directory if it doesn't exist
|
|
46
|
-
- Write `.prizmkit/rules/<layer>-rules.md
|
|
59
|
+
- Read `${SKILL_DIR}/references/rules/<layer>/template.md`.
|
|
60
|
+
- Fill all template placeholders with accumulated content from Phases A+B+C.
|
|
61
|
+
- Post-render self-check driven by `question-manifest.json` -> `template_placeholders`:
|
|
62
|
+
1. Coverage pass — for every placeholder in `from_questions`, confirm it traces to an answered question OR an `auto_derived_when` path OR a conditionally-skipped question (`required_if` false). If a `from_questions` placeholder has no source, go back and ask it before writing.
|
|
63
|
+
2. Residual pass — scan the rendered document for any residual `{{ ` or ` }}`; count must be 0. Placeholders in `from_fixed_rules`, `auto_generated`, and `metadata` are filled from fixed rules, Phase D generation, and project metadata.
|
|
64
|
+
- Generate Appendix A (Deny List) and Appendix B (Recommended Tools) per template instructions.
|
|
65
|
+
- Create `.prizmkit/rules/` directory if it doesn't exist.
|
|
66
|
+
- Write `.prizmkit/rules/<layer>-rules.md`.
|
|
67
|
+
|
|
68
|
+
## Step 5: Update root.prizm RULES Pointer
|
|
69
|
+
|
|
70
|
+
- Ensure `.prizmkit/prizm-docs/root.prizm` has a concise `RULES:` pointer to configured rules.
|
|
71
|
+
- If root.prizm exists, update only the `RULES:` line.
|
|
72
|
+
- If root.prizm is absent, create a minimal valid root.prizm with required fields and a `RULES:` pointer.
|
|
73
|
+
- Do not write architecture decisions, project brief content, or changelog metadata into root.prizm.
|