prizmkit 1.1.153 → 1.1.155
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/adapters/codex/skill-adapter.js +1 -1
- package/bundled/adapters/pi/skill-adapter.js +2 -1
- package/bundled/dev-pipeline/.env.example +2 -6
- package/bundled/dev-pipeline/README.md +120 -113
- package/bundled/dev-pipeline/assets/skill-subagent-integration.md +1 -1
- package/bundled/dev-pipeline/prizmkit_runtime/checkpoint_state.py +355 -15
- package/bundled/dev-pipeline/prizmkit_runtime/cli.py +189 -110
- package/bundled/dev-pipeline/prizmkit_runtime/commands.py +146 -111
- package/bundled/dev-pipeline/prizmkit_runtime/daemon.py +4 -20
- package/bundled/dev-pipeline/prizmkit_runtime/gitops.py +48 -14
- package/bundled/dev-pipeline/prizmkit_runtime/interoperability.py +0 -1
- package/bundled/dev-pipeline/prizmkit_runtime/paths.py +0 -3
- package/bundled/dev-pipeline/prizmkit_runtime/reset.py +122 -15
- package/bundled/dev-pipeline/prizmkit_runtime/reset_preserve.py +1 -1
- package/bundled/dev-pipeline/prizmkit_runtime/runner_classification.py +50 -2
- package/bundled/dev-pipeline/prizmkit_runtime/runner_models.py +1 -10
- package/bundled/dev-pipeline/prizmkit_runtime/runner_prompts.py +3 -3
- package/bundled/dev-pipeline/prizmkit_runtime/runners.py +139 -74
- package/bundled/dev-pipeline/prizmkit_runtime/runtime_commit.py +482 -0
- package/bundled/dev-pipeline/prizmkit_runtime/status.py +106 -1
- package/bundled/dev-pipeline/prizmkit_runtime/test_result.py +64 -11
- package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +145 -367
- package/bundled/dev-pipeline/scripts/generate-bugfix-prompt.py +91 -62
- package/bundled/dev-pipeline/scripts/generate-refactor-prompt.py +105 -69
- package/bundled/dev-pipeline/scripts/init-bugfix-pipeline.py +2 -2
- package/bundled/dev-pipeline/scripts/prompt_framework.py +36 -41
- package/bundled/dev-pipeline/scripts/update-bug-status.py +197 -71
- package/bundled/dev-pipeline/scripts/update-checkpoint.py +77 -16
- package/bundled/dev-pipeline/scripts/update-feature-status.py +46 -121
- package/bundled/dev-pipeline/scripts/update-refactor-status.py +46 -123
- package/bundled/dev-pipeline/scripts/utils.py +119 -0
- package/bundled/dev-pipeline/templates/bootstrap-prompt.md +22 -27
- package/bundled/dev-pipeline/templates/bootstrap-tier3.md +71 -104
- package/bundled/dev-pipeline/templates/bug-fix-list-schema.json +3 -2
- package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +59 -111
- package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +37 -72
- package/bundled/dev-pipeline/templates/sections/artifact-manifest.md +39 -0
- package/bundled/dev-pipeline/templates/sections/bugfix-critical-paths.md +3 -1
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-commit-report.md +8 -25
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-diagnose-plan.md +3 -3
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-implement.md +2 -2
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-review.md +3 -3
- package/bundled/dev-pipeline/templates/sections/bugfix-phase-test.md +2 -2
- package/bundled/dev-pipeline/templates/sections/bugfix-reminders.md +4 -4
- package/bundled/dev-pipeline/templates/sections/bugfix-session-context.md +0 -1
- package/bundled/dev-pipeline/templates/sections/checkpoint-system.md +7 -4
- package/bundled/dev-pipeline/templates/sections/context-budget-rules.md +14 -27
- package/bundled/dev-pipeline/templates/sections/feature-completion-summary.md +20 -0
- package/bundled/dev-pipeline/templates/sections/phase-commit-full.md +8 -71
- package/bundled/dev-pipeline/templates/sections/phase-implement-full.md +7 -7
- package/bundled/dev-pipeline/templates/sections/phase-prizmkit-test.md +3 -3
- package/bundled/dev-pipeline/templates/sections/phase-review-full.md +4 -4
- package/bundled/dev-pipeline/templates/sections/phase-specify-plan-full.md +5 -17
- package/bundled/dev-pipeline/templates/sections/refactor-critical-paths.md +3 -1
- package/bundled/dev-pipeline/templates/sections/refactor-phase-commit-report.md +10 -23
- package/bundled/dev-pipeline/templates/sections/refactor-phase-implement.md +2 -2
- package/bundled/dev-pipeline/templates/sections/refactor-phase-plan.md +2 -2
- package/bundled/dev-pipeline/templates/sections/refactor-phase-review.md +3 -3
- package/bundled/dev-pipeline/templates/sections/refactor-phase-test.md +2 -2
- package/bundled/dev-pipeline/templates/sections/refactor-reminders.md +7 -7
- package/bundled/dev-pipeline/templates/sections/refactor-session-context.md +0 -1
- package/bundled/dev-pipeline/templates/sections/retrospective.md +12 -0
- package/bundled/dev-pipeline/templates/sections/runtime-commit-handoff.md +35 -0
- package/bundled/dev-pipeline/templates/session-status-schema.json +45 -62
- package/bundled/dev-pipeline/tests/test_auto_skip.py +111 -32
- package/bundled/dev-pipeline/tests/test_checkpoint_state.py +617 -30
- package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +262 -194
- package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +62 -23
- package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +76 -28
- package/bundled/dev-pipeline/tests/test_python_runner_parity.py +281 -206
- package/bundled/dev-pipeline/tests/test_recovery_workflow.py +322 -0
- package/bundled/dev-pipeline/tests/test_reset_modes.py +935 -0
- package/bundled/dev-pipeline/tests/test_reset_preserve.py +5 -7
- package/bundled/dev-pipeline/tests/test_runtime_commit.py +608 -0
- package/bundled/dev-pipeline/tests/test_unified_cli.py +617 -188
- package/bundled/skills/_metadata.json +9 -9
- package/bundled/skills/app-planner/SKILL.md +3 -3
- package/bundled/skills/app-planner/references/generated-plan-review.md +2 -1
- package/bundled/skills/app-planner/references/rules/backend/derivation-rules.md +56 -64
- package/bundled/skills/app-planner/references/rules/backend/fixed-rules.md +85 -240
- package/bundled/skills/app-planner/references/rules/backend/question-bank.md +25 -25
- package/bundled/skills/app-planner/references/rules/backend/question-manifest.json +16 -7
- package/bundled/skills/app-planner/references/rules/backend/template.md +74 -92
- package/bundled/skills/app-planner/references/rules/database/derivation-rules.md +93 -93
- package/bundled/skills/app-planner/references/rules/database/fixed-rules.md +66 -173
- package/bundled/skills/app-planner/references/rules/database/question-bank.md +31 -26
- package/bundled/skills/app-planner/references/rules/database/question-manifest.json +13 -4
- package/bundled/skills/app-planner/references/rules/database/template.md +76 -98
- package/bundled/skills/app-planner/references/rules/frontend/derivation-rules.md +68 -95
- package/bundled/skills/app-planner/references/rules/frontend/fixed-rules.md +93 -166
- package/bundled/skills/app-planner/references/rules/frontend/question-bank.md +46 -36
- package/bundled/skills/app-planner/references/rules/frontend/question-manifest.json +11 -2
- package/bundled/skills/app-planner/references/rules/frontend/template.md +79 -257
- package/bundled/skills/app-planner/references/rules/mobile/derivation-rules.md +91 -99
- package/bundled/skills/app-planner/references/rules/mobile/fixed-rules.md +73 -246
- package/bundled/skills/app-planner/references/rules/mobile/question-bank.md +17 -17
- package/bundled/skills/app-planner/references/rules/mobile/question-manifest.json +17 -2
- package/bundled/skills/app-planner/references/rules/mobile/template.md +73 -85
- package/bundled/skills/app-planner/references/rules-configuration.md +133 -65
- package/bundled/skills/app-planner/scripts/validate-rules-configuration.py +291 -0
- package/bundled/skills/bug-planner/references/schema-validation.md +1 -1
- package/bundled/skills/bug-planner/scripts/validate-bug-list.py +1 -1
- package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +21 -13
- package/bundled/skills/feature-pipeline-launcher/SKILL.md +22 -14
- package/bundled/skills/prizmkit/SKILL.md +17 -18
- package/bundled/skills/prizmkit/references/workflow-state-protocol.md +22 -18
- package/bundled/skills/prizmkit-code-review/SKILL.md +22 -58
- package/bundled/skills/prizmkit-code-review/references/independent-code-review.md +6 -6
- package/bundled/skills/prizmkit-code-review/references/review-report-template.md +2 -2
- package/bundled/skills/prizmkit-committer/SKILL.md +82 -132
- package/bundled/skills/prizmkit-deploy/SKILL.md +27 -11
- package/bundled/skills/prizmkit-deploy/assets/deploy-document-template.md +89 -0
- package/bundled/skills/prizmkit-deploy/references/ci-cd-workflows.md +44 -14
- package/bundled/skills/prizmkit-deploy/references/cloud-platform-deploy.md +19 -3
- package/bundled/skills/prizmkit-deploy/references/database-setup.md +22 -12
- package/bundled/skills/prizmkit-deploy/references/deploy-config-schema.md +3 -1
- package/bundled/skills/prizmkit-deploy/references/deploy-history-schema.md +22 -6
- package/bundled/skills/prizmkit-deploy/references/deploy-metadata-schema.json +49 -0
- package/bundled/skills/prizmkit-deploy/references/live-validation-notes.md +1 -1
- package/bundled/skills/prizmkit-deploy/references/pending-input-schema.json +164 -0
- package/bundled/skills/prizmkit-deploy/references/ssh-adapter-flow.md +9 -8
- package/bundled/skills/prizmkit-deploy/references/ssh-execution-flow.md +6 -6
- package/bundled/skills/prizmkit-implement/SKILL.md +30 -66
- package/bundled/skills/prizmkit-implement/references/implementation-subagent-procedure.md +4 -4
- package/bundled/skills/prizmkit-init/SKILL.md +4 -0
- package/bundled/skills/prizmkit-plan/SKILL.md +21 -64
- package/bundled/skills/prizmkit-plan/references/artifact-identity.md +38 -0
- package/bundled/skills/prizmkit-plan/references/independent-plan-review.md +4 -4
- package/bundled/skills/prizmkit-plan/references/review-plan-spec-loop.md +5 -5
- package/bundled/skills/prizmkit-prizm-docs/SKILL.md +11 -1
- package/bundled/skills/prizmkit-retrospective/SKILL.md +63 -77
- package/bundled/skills/prizmkit-retrospective/references/knowledge-injection-steps.md +3 -3
- package/bundled/skills/prizmkit-retrospective/references/retrospective-result-schema.json +138 -0
- package/bundled/skills/prizmkit-retrospective/references/structural-sync-steps.md +17 -23
- package/bundled/skills/prizmkit-test/SKILL.md +64 -51
- package/bundled/skills/prizmkit-test/references/boundary-contract-and-test-double-guidance.md +206 -0
- package/bundled/skills/prizmkit-test/references/independent-test-review.md +17 -9
- package/bundled/skills/prizmkit-test/references/test-coverage-model.md +45 -21
- package/bundled/skills/prizmkit-test/references/test-report-template.md +20 -13
- package/bundled/skills/prizmkit-workflow/SKILL.md +35 -37
- package/bundled/skills/prizmkit-workflow/references/artifact-identity.md +34 -0
- package/bundled/skills/prizmkit-workflow/references/workflow-state-protocol.md +22 -18
- package/bundled/skills/recovery-workflow/SKILL.md +9 -6
- package/bundled/skills/recovery-workflow/evals/evals.json +2 -2
- package/bundled/skills/recovery-workflow/references/detection.md +7 -6
- package/bundled/skills/recovery-workflow/scripts/detect-recovery-state.py +16 -6
- package/bundled/skills/refactor-pipeline-launcher/SKILL.md +21 -13
- package/bundled/templates/project-memory-template.md +19 -11
- package/package.json +1 -1
- package/src/scaffold.js +33 -22
- package/bundled/dev-pipeline/prizmkit_runtime/runner_recovery.py +0 -228
- package/bundled/dev-pipeline/scripts/generate-recovery-prompt.py +0 -767
- package/bundled/dev-pipeline/templates/sections/bugfix-artifacts.md +0 -16
- package/bundled/dev-pipeline/templates/sections/bugfix-session-status.md +0 -31
- package/bundled/dev-pipeline/templates/sections/critical-paths-agent.md +0 -7
- package/bundled/dev-pipeline/templates/sections/critical-paths-lite.md +0 -7
- package/bundled/dev-pipeline/templates/sections/directory-convention-agent.md +0 -9
- package/bundled/dev-pipeline/templates/sections/directory-convention-lite.md +0 -6
- package/bundled/dev-pipeline/templates/sections/headless-commit-authorization.md +0 -15
- package/bundled/dev-pipeline/templates/sections/phase-commit.md +0 -61
- package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-agent-suffix.md +0 -24
- package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-lite-suffix.md +0 -12
- package/bundled/dev-pipeline/templates/sections/phase-implement-agent.md +0 -44
- package/bundled/dev-pipeline/templates/sections/phase-implement-lite.md +0 -25
- package/bundled/dev-pipeline/templates/sections/phase-plan-agent.md +0 -20
- package/bundled/dev-pipeline/templates/sections/phase-plan-lite.md +0 -20
- package/bundled/dev-pipeline/templates/sections/phase-review-agent.md +0 -20
- package/bundled/dev-pipeline/templates/sections/refactor-artifacts.md +0 -17
- package/bundled/dev-pipeline/templates/sections/refactor-session-status.md +0 -28
- package/bundled/dev-pipeline/templates/sections/test-failure-recovery-agent.md +0 -52
- package/bundled/dev-pipeline/templates/sections/test-failure-recovery-lite.md +0 -52
- package/bundled/skills/prizmkit-code-review/references/workflow-state-protocol.md +0 -174
- package/bundled/skills/prizmkit-committer/references/workflow-state-protocol.md +0 -174
- package/bundled/skills/prizmkit-implement/references/workflow-state-protocol.md +0 -174
- package/bundled/skills/prizmkit-plan/references/workflow-state-protocol.md +0 -174
- package/bundled/skills/prizmkit-retrospective/references/workflow-state-protocol.md +0 -174
- package/bundled/skills/prizmkit-test/references/external-contract-mock-guidance.md +0 -119
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"""Tests for generate-bootstrap-prompt.py core functions."""
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
-
import importlib.util
|
|
5
4
|
import os
|
|
6
5
|
import re
|
|
7
6
|
from argparse import Namespace
|
|
@@ -16,10 +15,8 @@ from generate_bootstrap_prompt import (
|
|
|
16
15
|
format_ac_checklist,
|
|
17
16
|
format_global_context,
|
|
18
17
|
get_completed_dependencies,
|
|
19
|
-
determine_pipeline_mode,
|
|
20
|
-
determine_legacy_template_mode,
|
|
21
18
|
process_conditional_blocks,
|
|
22
|
-
|
|
19
|
+
process_template_blocks,
|
|
23
20
|
assemble_sections,
|
|
24
21
|
build_replacements,
|
|
25
22
|
generate_checkpoint_definition,
|
|
@@ -87,14 +84,6 @@ def assert_complete_prompt_rendering(prompt):
|
|
|
87
84
|
assert re.findall(r"\{\{[A-Z][A-Z_0-9]+\}\}", prompt) == []
|
|
88
85
|
|
|
89
86
|
|
|
90
|
-
def _load_recovery_prompt_module():
|
|
91
|
-
path = Path("dev-pipeline/scripts/generate-recovery-prompt.py")
|
|
92
|
-
spec = importlib.util.spec_from_file_location("generate_recovery_prompt", path)
|
|
93
|
-
module = importlib.util.module_from_spec(spec)
|
|
94
|
-
spec.loader.exec_module(module)
|
|
95
|
-
return module
|
|
96
|
-
|
|
97
|
-
|
|
98
87
|
def test_rendered_prompt_validation_rejects_unresolved_control_placeholders():
|
|
99
88
|
content = (
|
|
100
89
|
"## Your Mission\n"
|
|
@@ -218,7 +207,6 @@ class TestExplicitProjectRoot:
|
|
|
218
207
|
state_dir=str(main_checkout / ".prizmkit" / "state" / "features"),
|
|
219
208
|
output=str(main_checkout / ".prizmkit" / "state" / "features" / "F-001" / "sessions" / "session-1" / "bootstrap-prompt.md"),
|
|
220
209
|
template=None,
|
|
221
|
-
mode=None,
|
|
222
210
|
extract_baselines=False,
|
|
223
211
|
project_root=str(execution_root),
|
|
224
212
|
)
|
|
@@ -328,23 +316,6 @@ class TestGetCompletedDependencies:
|
|
|
328
316
|
assert "no completed dependencies" in result
|
|
329
317
|
|
|
330
318
|
|
|
331
|
-
# ---------------------------------------------------------------------------
|
|
332
|
-
# determine_pipeline_mode
|
|
333
|
-
# ---------------------------------------------------------------------------
|
|
334
|
-
|
|
335
|
-
class TestDeterminePipelineMode:
|
|
336
|
-
def test_all_complexities_use_unified_full_guidance(self):
|
|
337
|
-
for complexity in ("low", "medium", "high", "critical", "banana", None):
|
|
338
|
-
assert determine_pipeline_mode(complexity) == "full"
|
|
339
|
-
|
|
340
|
-
def test_legacy_template_mode_mapping_remains_available_for_custom_templates(self):
|
|
341
|
-
assert determine_legacy_template_mode("low") == "lite"
|
|
342
|
-
assert determine_legacy_template_mode("medium") == "lite"
|
|
343
|
-
assert determine_legacy_template_mode("high") == "standard"
|
|
344
|
-
assert determine_legacy_template_mode("critical") == "full"
|
|
345
|
-
assert determine_legacy_template_mode("banana") == "lite"
|
|
346
|
-
|
|
347
|
-
|
|
348
319
|
# ---------------------------------------------------------------------------
|
|
349
320
|
# process_conditional_blocks
|
|
350
321
|
# ---------------------------------------------------------------------------
|
|
@@ -374,29 +345,18 @@ class TestProcessConditionalBlocks:
|
|
|
374
345
|
|
|
375
346
|
|
|
376
347
|
# ---------------------------------------------------------------------------
|
|
377
|
-
#
|
|
348
|
+
# explicit custom-template conditionals
|
|
378
349
|
# ---------------------------------------------------------------------------
|
|
379
350
|
|
|
380
|
-
class
|
|
381
|
-
def test_lite_mode_keeps_lite(self):
|
|
382
|
-
tpl = "{{IF_MODE_LITE}}lite content{{END_IF_MODE_LITE}}"
|
|
383
|
-
result = process_mode_blocks(tpl, "lite", init_done=True)
|
|
384
|
-
assert "lite content" in result
|
|
385
|
-
assert "IF_MODE" not in result
|
|
386
|
-
|
|
387
|
-
def test_lite_mode_removes_full(self):
|
|
388
|
-
tpl = "{{IF_MODE_FULL}}full content{{END_IF_MODE_FULL}}"
|
|
389
|
-
result = process_mode_blocks(tpl, "lite", init_done=True)
|
|
390
|
-
assert "full content" not in result
|
|
391
|
-
|
|
351
|
+
class TestProcessTemplateBlocks:
|
|
392
352
|
def test_init_done_keeps_init_done_block(self):
|
|
393
353
|
tpl = "{{IF_INIT_DONE}}\ninit done\n{{END_IF_INIT_DONE}}"
|
|
394
|
-
result =
|
|
354
|
+
result = process_template_blocks(tpl, init_done=True)
|
|
395
355
|
assert "init done" in result
|
|
396
356
|
|
|
397
357
|
def test_init_needed_when_not_done(self):
|
|
398
358
|
tpl = "{{IF_INIT_NEEDED}}\nneed init\n{{END_IF_INIT_NEEDED}}"
|
|
399
|
-
result =
|
|
359
|
+
result = process_template_blocks(tpl, init_done=False)
|
|
400
360
|
assert "need init" in result
|
|
401
361
|
|
|
402
362
|
|
|
@@ -415,8 +375,7 @@ class TestScopedFeatureTestGate:
|
|
|
415
375
|
]
|
|
416
376
|
|
|
417
377
|
@staticmethod
|
|
418
|
-
def _write_result(root, result="TEST_PASS", *, unresolved=None
|
|
419
|
-
review_required=False, review_scope=None):
|
|
378
|
+
def _write_result(root, result="TEST_PASS", *, unresolved=None):
|
|
420
379
|
artifact = root / ".prizmkit" / "specs" / "123-scope-test"
|
|
421
380
|
artifact.mkdir(parents=True, exist_ok=True)
|
|
422
381
|
(artifact / "test-report.md").write_text(
|
|
@@ -436,19 +395,16 @@ class TestScopedFeatureTestGate:
|
|
|
436
395
|
},
|
|
437
396
|
"repair_rounds": 0,
|
|
438
397
|
"production_changed": False,
|
|
439
|
-
"review_required": review_required,
|
|
440
|
-
"review_scope": review_scope,
|
|
441
398
|
"unresolved_items": unresolved or [],
|
|
442
399
|
}
|
|
443
400
|
(artifact / "test-result.json").write_text(
|
|
444
401
|
json.dumps(payload), encoding="utf-8",
|
|
445
402
|
)
|
|
446
403
|
|
|
447
|
-
def
|
|
404
|
+
def test_canonical_sections_insert_prizmkit_test_after_review(self):
|
|
448
405
|
sections_dir = Path(__file__).resolve().parents[1] / "templates" / "sections"
|
|
449
406
|
sections = assemble_sections(
|
|
450
|
-
|
|
451
|
-
is_resume=False, browser_enabled=False,
|
|
407
|
+
str(sections_dir), init_done=True, browser_enabled=False,
|
|
452
408
|
)
|
|
453
409
|
names = [name for name, _ in sections]
|
|
454
410
|
rendered = "\n".join(content for _, content in sections)
|
|
@@ -461,11 +417,10 @@ class TestScopedFeatureTestGate:
|
|
|
461
417
|
assert ".prizmkit/test/evidence" not in rendered
|
|
462
418
|
assert "TEST_FAIL" not in rendered
|
|
463
419
|
|
|
464
|
-
def
|
|
420
|
+
def test_canonical_sections_include_central_checkpoint_helper(self):
|
|
465
421
|
sections_dir = Path(__file__).resolve().parents[1] / "templates" / "sections"
|
|
466
422
|
sections = assemble_sections(
|
|
467
|
-
|
|
468
|
-
is_resume=False, browser_enabled=False,
|
|
423
|
+
str(sections_dir), init_done=True, browser_enabled=False,
|
|
469
424
|
)
|
|
470
425
|
rendered = "\n".join(content for _, content in sections)
|
|
471
426
|
|
|
@@ -479,27 +434,82 @@ class TestScopedFeatureTestGate:
|
|
|
479
434
|
assert "--status completed --stage-result TEST_PASS" in rendered
|
|
480
435
|
assert "--status completed --stage-result RETRO_COMPLETE" in rendered
|
|
481
436
|
assert "--status in_progress --stage-result COMMIT_PENDING" in rendered
|
|
482
|
-
assert "
|
|
437
|
+
assert "Python runtime" in rendered
|
|
438
|
+
assert "alone advances this step to `completed/COMMITTED`" in rendered
|
|
483
439
|
assert "Do not hand-edit `workflow-checkpoint.json` directly." in rendered
|
|
484
440
|
assert "authoritative_evidence_paths" not in rendered
|
|
485
441
|
|
|
486
442
|
def test_checkpoint_orders_test_after_review_with_terminal_artifacts(self):
|
|
487
443
|
sections_dir = Path(__file__).resolve().parents[1] / "templates" / "sections"
|
|
488
444
|
sections = assemble_sections(
|
|
489
|
-
|
|
490
|
-
is_resume=False, browser_enabled=False,
|
|
445
|
+
str(sections_dir), init_done=True, browser_enabled=False,
|
|
491
446
|
)
|
|
492
447
|
checkpoint = generate_checkpoint_definition(
|
|
493
|
-
sections,
|
|
448
|
+
sections, workflow_type="feature-pipeline",
|
|
494
449
|
item_id="F-123", item_slug="123-scope-test",
|
|
495
450
|
session_id="F-123-session", init_done=True,
|
|
496
451
|
)
|
|
497
452
|
skills = [step["skill"] for step in checkpoint["steps"]]
|
|
453
|
+
plan_step = next(
|
|
454
|
+
step for step in checkpoint["steps"]
|
|
455
|
+
if step["skill"] == "context-snapshot-and-plan"
|
|
456
|
+
)
|
|
457
|
+
review_step = next(
|
|
458
|
+
step for step in checkpoint["steps"]
|
|
459
|
+
if step["skill"] == "prizmkit-code-review"
|
|
460
|
+
)
|
|
498
461
|
test_step = next(step for step in checkpoint["steps"] if step["skill"] == "prizmkit-test")
|
|
499
462
|
|
|
500
463
|
assert skills.index("prizmkit-code-review") < skills.index("prizmkit-test")
|
|
464
|
+
assert plan_step["required_artifacts"] == [
|
|
465
|
+
".prizmkit/specs/123-scope-test/context-snapshot.md",
|
|
466
|
+
".prizmkit/specs/123-scope-test/spec.md",
|
|
467
|
+
".prizmkit/specs/123-scope-test/plan.md",
|
|
468
|
+
]
|
|
469
|
+
assert review_step["required_artifacts"] == [
|
|
470
|
+
".prizmkit/specs/123-scope-test/review-report.md"
|
|
471
|
+
]
|
|
501
472
|
assert test_step["required_artifacts"] == self._artifacts()
|
|
502
473
|
|
|
474
|
+
def test_merge_rewinds_completed_atomic_stage_without_domain_result(self, tmp_path):
|
|
475
|
+
existing = {
|
|
476
|
+
"steps": [
|
|
477
|
+
{
|
|
478
|
+
"id": "S01",
|
|
479
|
+
"skill": "context-snapshot-and-plan",
|
|
480
|
+
"status": "completed",
|
|
481
|
+
"required_artifacts": [],
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
"id": "S02",
|
|
485
|
+
"skill": "prizmkit-implement",
|
|
486
|
+
"status": "completed",
|
|
487
|
+
"stage_result": "IMPLEMENTED",
|
|
488
|
+
"required_artifacts": [],
|
|
489
|
+
},
|
|
490
|
+
]
|
|
491
|
+
}
|
|
492
|
+
fresh = {
|
|
493
|
+
"steps": [
|
|
494
|
+
{
|
|
495
|
+
"id": "S01",
|
|
496
|
+
"skill": "context-snapshot-and-plan",
|
|
497
|
+
"status": "pending",
|
|
498
|
+
"required_artifacts": [],
|
|
499
|
+
},
|
|
500
|
+
{
|
|
501
|
+
"id": "S02",
|
|
502
|
+
"skill": "prizmkit-implement",
|
|
503
|
+
"status": "pending",
|
|
504
|
+
"required_artifacts": [],
|
|
505
|
+
},
|
|
506
|
+
]
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
merged = merge_checkpoint_state(existing, fresh, str(tmp_path))
|
|
510
|
+
|
|
511
|
+
assert [step["status"] for step in merged["steps"]] == ["pending", "pending"]
|
|
512
|
+
|
|
503
513
|
def test_merge_keeps_only_consistent_test_pass_artifacts(self, tmp_path):
|
|
504
514
|
self._write_result(tmp_path)
|
|
505
515
|
existing = {"steps": [{
|
|
@@ -536,8 +546,7 @@ class TestScopedFeatureTestGate:
|
|
|
536
546
|
self._write_result(tmp_path)
|
|
537
547
|
result_path = tmp_path / ".prizmkit/specs/123-scope-test/test-result.json"
|
|
538
548
|
payload = json.loads(result_path.read_text(encoding="utf-8"))
|
|
539
|
-
payload["
|
|
540
|
-
payload["review_scope"] = None
|
|
549
|
+
payload["caller_route"] = "prizmkit-code-review"
|
|
541
550
|
result_path.write_text(json.dumps(payload), encoding="utf-8")
|
|
542
551
|
existing = {"steps": [{
|
|
543
552
|
"id": "S01", "skill": "prizmkit-test", "status": "completed",
|
|
@@ -552,21 +561,32 @@ class TestScopedFeatureTestGate:
|
|
|
552
561
|
|
|
553
562
|
assert merged["steps"][0]["status"] == "pending"
|
|
554
563
|
|
|
555
|
-
def
|
|
556
|
-
review = route_feature_repair(
|
|
557
|
-
|
|
564
|
+
def test_feature_result_routing_owns_bounded_outer_repair(self):
|
|
565
|
+
review = route_feature_repair(
|
|
566
|
+
"REVIEW_NEEDS_FIXES", repair_scope="production", repair_round=0,
|
|
567
|
+
)
|
|
568
|
+
needs_fixes = route_feature_repair(
|
|
569
|
+
"TEST_NEEDS_FIXES", repair_scope="production", repair_round=1,
|
|
570
|
+
)
|
|
558
571
|
blocked = route_feature_repair("TEST_BLOCKED", repair_round=1)
|
|
572
|
+
passed = route_feature_repair("TEST_PASS", repair_round=1)
|
|
559
573
|
|
|
560
574
|
assert review["route"] == [
|
|
561
575
|
"prizmkit-implement", "prizmkit-code-review", "prizmkit-test",
|
|
562
576
|
]
|
|
563
577
|
assert review["repair_round"] == 1
|
|
564
|
-
assert needs_fixes["blocked"] is
|
|
565
|
-
assert needs_fixes["route"] == [
|
|
566
|
-
|
|
578
|
+
assert needs_fixes["blocked"] is False
|
|
579
|
+
assert needs_fixes["route"] == [
|
|
580
|
+
"prizmkit-implement", "prizmkit-code-review", "prizmkit-test",
|
|
581
|
+
]
|
|
582
|
+
assert needs_fixes["repair_round"] == 2
|
|
567
583
|
assert blocked["blocked"] is True
|
|
568
584
|
assert blocked["blocked_reason"] == "test_blocked"
|
|
569
585
|
assert blocked["terminal_status"] == "WORKFLOW_BLOCKED"
|
|
586
|
+
assert passed["route"] == ["family-report", "prizmkit-retrospective"]
|
|
587
|
+
assert passed["next_stage"] == "family-report"
|
|
588
|
+
unsafe = route_feature_repair("TEST_NEEDS_FIXES", repair_round=0)
|
|
589
|
+
assert unsafe["blocked_reason"] == "unsafe_repair_scope"
|
|
570
590
|
|
|
571
591
|
def test_pipeline_checkpoint_metadata_rejects_legacy_or_mismatched_status(self):
|
|
572
592
|
invalid = [
|
|
@@ -627,26 +647,24 @@ class TestHeadlessRecoveryReframing:
|
|
|
627
647
|
deleted_title = "Session " + "Checkpoint Awareness"
|
|
628
648
|
placeholder = "{{LOG_" + "SIZE_AWARENESS}}"
|
|
629
649
|
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
mode, str(sections_dir), init_done=True, is_resume=False,
|
|
634
|
-
browser_enabled=False,
|
|
635
|
-
)
|
|
650
|
+
rendered = "\n".join(
|
|
651
|
+
section for _name, section in assemble_sections(
|
|
652
|
+
str(sections_dir), init_done=True, browser_enabled=False,
|
|
636
653
|
)
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
654
|
+
)
|
|
655
|
+
assert deleted_title not in rendered
|
|
656
|
+
assert placeholder not in rendered
|
|
657
|
+
assert REMOVED_MAX_LOG_ENV not in rendered
|
|
658
|
+
assert REMOVED_MAX_LOG_HUMAN not in rendered
|
|
659
|
+
assert ("209" + "7152") not in rendered
|
|
660
|
+
assert ("2" + "MB") not in rendered
|
|
661
|
+
assert "monitor-log" not in rendered
|
|
662
|
+
assert "COMPACT_NEEDED" not in rendered
|
|
663
|
+
assert "log-size monitor" not in rendered
|
|
664
|
+
assert "The log-size monitor is the only allowed" not in rendered
|
|
665
|
+
assert "Run this command and keep it running" not in rendered
|
|
666
|
+
assert "Execute `/compact`" not in rendered
|
|
667
|
+
assert "run `/compact` after" not in rendered
|
|
650
668
|
|
|
651
669
|
|
|
652
670
|
class TestDirectOrchestratorImplementationPrompts:
|
|
@@ -668,57 +686,44 @@ class TestDirectOrchestratorImplementationPrompts:
|
|
|
668
686
|
assert (templates_dir / "agent-prompts" / FORBIDDEN_DEV_FIX_PROMPT).exists() is False
|
|
669
687
|
assert (templates_dir / "agent-prompts" / FORBIDDEN_DEV_RESUME_PROMPT).exists() is False
|
|
670
688
|
|
|
671
|
-
def
|
|
689
|
+
def test_canonical_sections_use_direct_orchestrator_implementation(self):
|
|
672
690
|
sections_dir = Path("dev-pipeline/templates/sections").resolve()
|
|
673
691
|
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
mode, str(sections_dir), init_done=True, is_resume=False,
|
|
678
|
-
browser_enabled=False,
|
|
679
|
-
)
|
|
692
|
+
rendered = "\n".join(
|
|
693
|
+
section for _name, section in assemble_sections(
|
|
694
|
+
str(sections_dir), init_done=True, browser_enabled=False,
|
|
680
695
|
)
|
|
696
|
+
)
|
|
681
697
|
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
698
|
+
assert "/prizmkit-implement" in rendered
|
|
699
|
+
assert "orchestrator) execute" in rendered or "Run `/prizmkit-implement`" in rendered
|
|
700
|
+
assert "Spawn Dev subagent" not in rendered
|
|
701
|
+
assert "Spawn Dev agent" not in rendered
|
|
702
|
+
assert "Implement — Dev" not in rendered
|
|
703
|
+
assert "{{AGENT_PROMPT_DEV_IMPLEMENT}}" not in rendered
|
|
704
|
+
assert "dev-implement.md" not in rendered
|
|
705
|
+
assert FORBIDDEN_REVIEW_DEV_FIX not in rendered
|
|
706
|
+
assert FORBIDDEN_REVIEWER_DEV_SPAWN not in rendered
|
|
707
|
+
assert FORBIDDEN_EXPLORE_SUBAGENT not in rendered
|
|
708
|
+
assert FORBIDDEN_TEST_CMD_PLACEHOLDER not in rendered
|
|
709
|
+
assert FORBIDDEN_THREE_STRIKE not in rendered
|
|
710
|
+
assert FORBIDDEN_TWENTY_STEP not in rendered
|
|
711
|
+
assert "review-report.md" in rendered
|
|
712
|
+
assert "Final Result" in rendered
|
|
713
|
+
assert "low=0" not in rendered
|
|
714
|
+
assert "Reviewer 3" not in rendered
|
|
699
715
|
|
|
700
716
|
def test_all_rendered_pipeline_prompts_have_one_main_agent_review_model(self, tmp_path):
|
|
701
|
-
recovery = _load_recovery_prompt_module()
|
|
702
717
|
prompts = [
|
|
703
|
-
_render_feature_prompt(tmp_path / "feature-
|
|
704
|
-
_render_feature_prompt(tmp_path / "feature-
|
|
705
|
-
_render_feature_prompt(tmp_path / "feature-
|
|
718
|
+
_render_feature_prompt(tmp_path / "feature-low", complexity="low"),
|
|
719
|
+
_render_feature_prompt(tmp_path / "feature-medium", complexity="medium"),
|
|
720
|
+
_render_feature_prompt(tmp_path / "feature-high", complexity="high"),
|
|
706
721
|
_render_feature_prompt(
|
|
707
|
-
tmp_path / "feature-
|
|
708
|
-
mode="full",
|
|
722
|
+
tmp_path / "feature-compatibility-template",
|
|
709
723
|
template="bootstrap-tier3.md",
|
|
710
724
|
),
|
|
711
725
|
_render_bugfix_prompt(tmp_path / "bugfix"),
|
|
712
726
|
_render_refactor_prompt(tmp_path / "refactor"),
|
|
713
|
-
recovery.build_bugfix_prompt(
|
|
714
|
-
{
|
|
715
|
-
"workflow_type": "bug-fix-workflow",
|
|
716
|
-
"phase": 5,
|
|
717
|
-
"phase_name": "Review",
|
|
718
|
-
"workflow_data": {"bug_id": "B-001"},
|
|
719
|
-
},
|
|
720
|
-
str(tmp_path),
|
|
721
|
-
),
|
|
722
727
|
]
|
|
723
728
|
|
|
724
729
|
for prompt in prompts:
|
|
@@ -824,7 +829,6 @@ class TestContinuationHandoff:
|
|
|
824
829
|
def test_modular_feature_prompt_renders_user_context(tmp_path):
|
|
825
830
|
prompt = _render_feature_prompt(
|
|
826
831
|
tmp_path / "user-context",
|
|
827
|
-
mode="standard",
|
|
828
832
|
user_context=["Use the existing event envelope", "Keep the public API backward compatible"],
|
|
829
833
|
)
|
|
830
834
|
|
|
@@ -854,7 +858,7 @@ def _write_feature_list(path, feature):
|
|
|
854
858
|
path.write_text(json.dumps({"features": [feature], "global_context": {"language": "Python"}}), encoding="utf-8")
|
|
855
859
|
|
|
856
860
|
|
|
857
|
-
def _render_feature_prompt(tmp_path,
|
|
861
|
+
def _render_feature_prompt(tmp_path, template=None, platform="claude", browser_interaction=None, complexity=None, user_context=None, continuation_mode=None):
|
|
858
862
|
project = tmp_path / "project"
|
|
859
863
|
project.mkdir(parents=True)
|
|
860
864
|
(project / ".prizmkit" / "plans").mkdir(parents=True)
|
|
@@ -865,7 +869,7 @@ def _render_feature_prompt(tmp_path, mode="standard", template=None, platform="c
|
|
|
865
869
|
"id": "F-123",
|
|
866
870
|
"title": "Prompt Cleanup",
|
|
867
871
|
"description": "Clean prompt text",
|
|
868
|
-
"estimated_complexity": complexity or
|
|
872
|
+
"estimated_complexity": complexity or "medium",
|
|
869
873
|
"acceptance_criteria": ["Prompts are clean"],
|
|
870
874
|
}
|
|
871
875
|
if user_context is not None:
|
|
@@ -886,8 +890,9 @@ def _render_feature_prompt(tmp_path, mode="standard", template=None, platform="c
|
|
|
886
890
|
"--state-dir", str(project / ".prizmkit" / "state" / "features"),
|
|
887
891
|
"--project-root", str(project),
|
|
888
892
|
"--output", str(output),
|
|
889
|
-
"--mode", mode,
|
|
890
893
|
]
|
|
894
|
+
if continuation_mode:
|
|
895
|
+
args.extend(["--continuation-mode", continuation_mode])
|
|
891
896
|
if template:
|
|
892
897
|
template_path = Path(template)
|
|
893
898
|
if not template_path.is_absolute():
|
|
@@ -1045,12 +1050,11 @@ def _render_refactor_prompt(tmp_path, platform="claude", explicit_platform=None,
|
|
|
1045
1050
|
return output.read_text(encoding="utf-8")
|
|
1046
1051
|
|
|
1047
1052
|
|
|
1048
|
-
class
|
|
1049
|
-
def
|
|
1053
|
+
class TestFeaturePromptCanonicalGuidance:
|
|
1054
|
+
def test_default_sections_prompt_uses_same_guidance_for_all_complexities(self, tmp_path):
|
|
1050
1055
|
prompts = {
|
|
1051
1056
|
complexity: _render_feature_prompt(
|
|
1052
1057
|
tmp_path / f"complexity-{complexity}",
|
|
1053
|
-
mode="lite",
|
|
1054
1058
|
complexity=complexity,
|
|
1055
1059
|
)
|
|
1056
1060
|
for complexity in ("low", "medium", "high", "critical")
|
|
@@ -1061,83 +1065,96 @@ class TestFeaturePromptUnifiedFullGuidance:
|
|
|
1061
1065
|
assert "## Task Contract" in prompt
|
|
1062
1066
|
assert "## Workflow Checkpoint System" in prompt
|
|
1063
1067
|
assert "### Specify + Plan (Full Workflow)" in prompt
|
|
1064
|
-
assert "
|
|
1068
|
+
assert "The injected session validates artifact presence but does not perform a second planning review" in prompt
|
|
1065
1069
|
assert "### Scoped Feature Test Gate — PrizmKit Test" in prompt
|
|
1066
|
-
assert "## Test Failure Recovery Protocol" in prompt
|
|
1070
|
+
assert "## Test Failure Recovery Protocol" not in prompt
|
|
1067
1071
|
assert "test-report.md" in prompt
|
|
1068
1072
|
assert "test-result.json" in prompt
|
|
1069
1073
|
assert "### Browser Verification — MANDATORY" not in prompt
|
|
1070
|
-
assert "###
|
|
1074
|
+
assert "### Feature Completion Summary" in prompt
|
|
1075
|
+
assert "### Runtime Commit Handoff" in prompt
|
|
1071
1076
|
assert "# Dev-Pipeline Session Bootstrap — Tier 1" not in prompt
|
|
1072
1077
|
assert "# Dev-Pipeline Session Bootstrap — Tier 2" not in prompt
|
|
1073
1078
|
assert "### Phase 0: SKIP (already initialized)" not in prompt
|
|
1074
1079
|
|
|
1075
|
-
#
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
prompt = _render_feature_prompt(tmp_path / "mode-lite", mode="lite", complexity="low")
|
|
1080
|
+
# Complexity remains planning metadata; every task gets the same guidance.
|
|
1081
|
+
def test_canonical_guidance_has_no_pipeline_mode_context(self, tmp_path):
|
|
1082
|
+
prompt = _render_feature_prompt(tmp_path / "canonical", complexity="low")
|
|
1079
1083
|
|
|
1080
1084
|
assert "{{PIPELINE_MODE}}" not in prompt
|
|
1085
|
+
assert "compatibility mode" not in prompt
|
|
1081
1086
|
assert "### Specify + Plan (Full Workflow)" in prompt
|
|
1082
|
-
assert "### Plan Self-Check — Orchestrator Direct" in prompt
|
|
1087
|
+
assert "### Plan Self-Check — Orchestrator Direct" not in prompt
|
|
1088
|
+
assert "does not perform a second planning review" in prompt
|
|
1083
1089
|
assert "### Phase 0: SKIP (already initialized)" not in prompt
|
|
1084
1090
|
|
|
1085
1091
|
def test_explicit_custom_template_override_still_renders(self, tmp_path):
|
|
1086
1092
|
custom = tmp_path / "custom-template.md"
|
|
1087
1093
|
custom.write_text(
|
|
1088
|
-
"
|
|
1094
|
+
"## Your Mission\nCUSTOM {{FEATURE_ID}} {{FEATURE_TITLE}} {{COMPLEXITY}} "
|
|
1095
|
+
"{{IF_FRESH_START}}fresh{{END_IF_FRESH_START}}\n\n## Execution\ncustom\n\n"
|
|
1096
|
+
"## Failure Capture\ncustom",
|
|
1089
1097
|
encoding="utf-8",
|
|
1090
1098
|
)
|
|
1091
1099
|
|
|
1092
1100
|
prompt = _render_feature_prompt(
|
|
1093
1101
|
tmp_path / "custom-template-project",
|
|
1094
|
-
mode="lite",
|
|
1095
1102
|
complexity="low",
|
|
1096
1103
|
template=custom,
|
|
1097
1104
|
)
|
|
1098
1105
|
|
|
1099
|
-
assert
|
|
1106
|
+
assert "CUSTOM F-123 Prompt Cleanup low fresh" in prompt
|
|
1107
|
+
checkpoint_path = (
|
|
1108
|
+
tmp_path / "custom-template-project" / "project" / ".prizmkit"
|
|
1109
|
+
/ "specs" / "123-prompt-cleanup" / "workflow-checkpoint.json"
|
|
1110
|
+
)
|
|
1111
|
+
checkpoint = json.loads(checkpoint_path.read_text(encoding="utf-8"))
|
|
1112
|
+
assert [step["skill"] for step in checkpoint["steps"]] == [
|
|
1113
|
+
"prizmkit-init",
|
|
1114
|
+
"context-snapshot-and-plan",
|
|
1115
|
+
"prizmkit-implement",
|
|
1116
|
+
"prizmkit-code-review",
|
|
1117
|
+
"prizmkit-test",
|
|
1118
|
+
"completion-summary",
|
|
1119
|
+
"prizmkit-retrospective",
|
|
1120
|
+
"prizmkit-committer",
|
|
1121
|
+
]
|
|
1100
1122
|
|
|
1101
|
-
def
|
|
1123
|
+
def test_malformed_continuation_addendum_fails_closed(self, tmp_path, monkeypatch):
|
|
1124
|
+
import generate_bootstrap_prompt as gbp
|
|
1125
|
+
|
|
1126
|
+
monkeypatch.setattr(
|
|
1127
|
+
gbp,
|
|
1128
|
+
"append_continuation_handoff",
|
|
1129
|
+
lambda rendered, *_args, **_kwargs: rendered + "\n{{BROKEN_CONTINUATION}}",
|
|
1130
|
+
)
|
|
1131
|
+
try:
|
|
1132
|
+
_render_feature_prompt(
|
|
1133
|
+
tmp_path / "malformed-continuation",
|
|
1134
|
+
continuation_mode="ai_error",
|
|
1135
|
+
)
|
|
1136
|
+
except SystemExit as exc:
|
|
1137
|
+
assert exc.code == 1
|
|
1138
|
+
else:
|
|
1139
|
+
raise AssertionError("malformed continuation Prompt did not fail closed")
|
|
1140
|
+
|
|
1141
|
+
def test_missing_canonical_sections_fails_closed(self, tmp_path, monkeypatch):
|
|
1102
1142
|
import generate_bootstrap_prompt as gbp
|
|
1103
1143
|
|
|
1104
1144
|
original_isdir = os.path.isdir
|
|
1105
|
-
original_isfile = os.path.isfile
|
|
1106
|
-
requested = []
|
|
1107
1145
|
|
|
1108
1146
|
def fake_isdir(path):
|
|
1109
1147
|
if str(path).endswith(os.path.join("templates", "sections")):
|
|
1110
1148
|
return False
|
|
1111
1149
|
return original_isdir(path)
|
|
1112
1150
|
|
|
1113
|
-
def fake_isfile(path):
|
|
1114
|
-
requested.append(Path(path).name)
|
|
1115
|
-
return original_isfile(path)
|
|
1116
|
-
|
|
1117
1151
|
monkeypatch.setattr(gbp.os.path, "isdir", fake_isdir)
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
assert "bootstrap-tier2.md" not in requested
|
|
1125
|
-
|
|
1126
|
-
requested.clear()
|
|
1127
|
-
|
|
1128
|
-
def fake_isfile_without_tier3(path):
|
|
1129
|
-
requested.append(Path(path).name)
|
|
1130
|
-
if Path(path).name == "bootstrap-tier3.md":
|
|
1131
|
-
return False
|
|
1132
|
-
return original_isfile(path)
|
|
1133
|
-
|
|
1134
|
-
monkeypatch.setattr(gbp.os.path, "isfile", fake_isfile_without_tier3)
|
|
1135
|
-
prompt = _render_feature_prompt(tmp_path / "fallback-bootstrap-prompt", mode="lite", complexity="low")
|
|
1136
|
-
|
|
1137
|
-
assert "bootstrap-tier3.md" in requested
|
|
1138
|
-
assert "bootstrap-tier1.md" not in requested
|
|
1139
|
-
assert "bootstrap-tier2.md" not in requested
|
|
1140
|
-
assert "# Dev-Pipeline Fallback Bootstrap Prompt" in prompt
|
|
1152
|
+
try:
|
|
1153
|
+
_render_feature_prompt(tmp_path / "missing-sections", complexity="low")
|
|
1154
|
+
except SystemExit as exc:
|
|
1155
|
+
assert exc.code == 1
|
|
1156
|
+
else:
|
|
1157
|
+
raise AssertionError("missing canonical sections did not fail closed")
|
|
1141
1158
|
|
|
1142
1159
|
|
|
1143
1160
|
class TestBrowserBlockingPromptPolicy:
|
|
@@ -1153,10 +1170,16 @@ class TestBrowserBlockingPromptPolicy:
|
|
|
1153
1170
|
_render_refactor_prompt(tmp_path / "refactor-browser-blocking", refactor_overrides={"browser_interaction": browser_interaction}),
|
|
1154
1171
|
]
|
|
1155
1172
|
|
|
1156
|
-
|
|
1173
|
+
test_markers = (
|
|
1174
|
+
"### Scoped Feature Test Gate — PrizmKit Test",
|
|
1175
|
+
"### Bugfix Test Gate — PrizmKit Test",
|
|
1176
|
+
"### Refactor Test Gate — PrizmKit Test",
|
|
1177
|
+
)
|
|
1178
|
+
for prompt, test_marker in zip(prompts, test_markers, strict=True):
|
|
1157
1179
|
assert "Browser blocking policy: `true`" in prompt
|
|
1158
1180
|
assert "stop before commit" in prompt
|
|
1159
1181
|
assert "Failures do NOT block the commit" not in prompt
|
|
1182
|
+
assert prompt.index("### Browser Verification — MANDATORY") < prompt.index(test_marker)
|
|
1160
1183
|
|
|
1161
1184
|
def test_feature_bugfix_refactor_nonblocking_browser_policy_continues(self, tmp_path):
|
|
1162
1185
|
browser_interaction = {
|
|
@@ -1278,7 +1301,8 @@ class TestBrowserOptInPromptPolicy:
|
|
|
1278
1301
|
assert "Manual / Hybrid Verification Gate" in prompt
|
|
1279
1302
|
assert "must complete manual/hybrid verification automatically" in prompt
|
|
1280
1303
|
assert "Stop before `/prizmkit-retrospective` and `/prizmkit-committer`" not in prompt
|
|
1281
|
-
assert "###
|
|
1304
|
+
assert "### Runtime Commit Handoff" in prompt
|
|
1305
|
+
assert prompt.index("Manual / Hybrid Verification Gate") < prompt.index("### Bugfix Test Gate")
|
|
1282
1306
|
assert "status=\"partial\"" not in prompt
|
|
1283
1307
|
|
|
1284
1308
|
def test_recovery_manual_bugfix_uses_same_automatic_policy(self, tmp_path):
|
|
@@ -1291,14 +1315,16 @@ class TestBrowserOptInPromptPolicy:
|
|
|
1291
1315
|
assert "Manual / Hybrid Verification Gate" in prompt
|
|
1292
1316
|
assert "must complete manual/hybrid verification automatically" in prompt
|
|
1293
1317
|
assert "Recovery-class session: automated verification may substitute" not in prompt
|
|
1294
|
-
assert "###
|
|
1318
|
+
assert "### Runtime Commit Handoff" in prompt
|
|
1295
1319
|
|
|
1296
1320
|
|
|
1297
1321
|
class TestHeadlessPromptCleanupF033:
|
|
1298
1322
|
def test_rendered_feature_bugfix_refactor_prompts_are_clean(self, tmp_path):
|
|
1299
|
-
prompts = [
|
|
1300
|
-
|
|
1301
|
-
|
|
1323
|
+
prompts = [
|
|
1324
|
+
_render_feature_prompt(tmp_path / "low", complexity="low"),
|
|
1325
|
+
_render_feature_prompt(tmp_path / "medium", complexity="medium"),
|
|
1326
|
+
_render_feature_prompt(tmp_path / "high", complexity="high"),
|
|
1327
|
+
]
|
|
1302
1328
|
prompts.append(_render_bugfix_prompt(tmp_path))
|
|
1303
1329
|
prompts.append(_render_refactor_prompt(tmp_path))
|
|
1304
1330
|
|
|
@@ -1326,8 +1352,47 @@ class TestHeadlessPromptCleanupF033:
|
|
|
1326
1352
|
assert "which " not in prompt # no which-command shell glue
|
|
1327
1353
|
assert "python3 {{INIT_SCRIPT_PATH}}" not in prompt
|
|
1328
1354
|
|
|
1355
|
+
def test_all_families_render_shared_manifest_and_precommit_order(self, tmp_path):
|
|
1356
|
+
prompts = {
|
|
1357
|
+
"feature": _render_feature_prompt(tmp_path / "manifest-feature"),
|
|
1358
|
+
"bugfix": _render_bugfix_prompt(tmp_path / "manifest-bugfix"),
|
|
1359
|
+
"refactor": _render_refactor_prompt(tmp_path / "manifest-refactor"),
|
|
1360
|
+
}
|
|
1361
|
+
common_artifacts = (
|
|
1362
|
+
"spec.md",
|
|
1363
|
+
"plan.md",
|
|
1364
|
+
"context-snapshot.md",
|
|
1365
|
+
"review-report.md",
|
|
1366
|
+
"test-report.md",
|
|
1367
|
+
"test-result.json",
|
|
1368
|
+
"retrospective-result.json",
|
|
1369
|
+
"failure-log.md",
|
|
1370
|
+
"workflow-checkpoint.json",
|
|
1371
|
+
"runtime-commit-request.json",
|
|
1372
|
+
)
|
|
1373
|
+
test_markers = {
|
|
1374
|
+
"feature": "### Scoped Feature Test Gate — PrizmKit Test",
|
|
1375
|
+
"bugfix": "### Bugfix Test Gate — PrizmKit Test",
|
|
1376
|
+
"refactor": "### Refactor Test Gate — PrizmKit Test",
|
|
1377
|
+
}
|
|
1378
|
+
for family, prompt in prompts.items():
|
|
1379
|
+
assert "## Requirement Artifact Manifest" in prompt
|
|
1380
|
+
for artifact in common_artifacts:
|
|
1381
|
+
assert artifact in prompt
|
|
1382
|
+
assert "Atomic Skills write only their stage-owned artifacts and domain results" in prompt
|
|
1383
|
+
assert "COMMIT_REQUEST_READY" in prompt
|
|
1384
|
+
assert "workflow_state_path" not in prompt
|
|
1385
|
+
assert "outcome=RETRO_COMPLETE" in prompt
|
|
1386
|
+
assert "Never recreate the testing-local repair loop" in prompt
|
|
1387
|
+
assert prompt.index(test_markers[family]) < prompt.index("### Retrospective")
|
|
1388
|
+
assert prompt.index("### Retrospective") < prompt.index("### Runtime Commit Handoff")
|
|
1389
|
+
|
|
1390
|
+
assert prompts["feature"].index("### Feature Completion Summary") < prompts["feature"].index("### Retrospective")
|
|
1391
|
+
assert prompts["bugfix"].index("### Bug Fix Report") < prompts["bugfix"].index("### Retrospective")
|
|
1392
|
+
assert prompts["refactor"].index("### Refactor Report") < prompts["refactor"].index("### Retrospective")
|
|
1393
|
+
|
|
1329
1394
|
def test_retained_tier3_template_is_clean_when_rendered(self, tmp_path):
|
|
1330
|
-
prompt = _render_feature_prompt(tmp_path / "bootstrap-tier3.md",
|
|
1395
|
+
prompt = _render_feature_prompt(tmp_path / "bootstrap-tier3.md", template="bootstrap-tier3.md")
|
|
1331
1396
|
for forbidden in HEADLESS_FORBIDDEN_PROMPT_STRINGS:
|
|
1332
1397
|
assert forbidden not in prompt
|
|
1333
1398
|
assert "prizmkit-test-gate.py" not in prompt
|
|
@@ -1357,7 +1422,6 @@ class TestHeadlessPromptCleanupF033:
|
|
|
1357
1422
|
]
|
|
1358
1423
|
prompt = _render_feature_prompt(
|
|
1359
1424
|
tmp_path / "browser-bootstrap-tier3.md",
|
|
1360
|
-
mode="full",
|
|
1361
1425
|
template="bootstrap-tier3.md",
|
|
1362
1426
|
browser_interaction={"tool": "auto", "verify_steps": ["Open the page"]},
|
|
1363
1427
|
)
|
|
@@ -1380,7 +1444,6 @@ class TestHeadlessPromptCleanupF033:
|
|
|
1380
1444
|
bug_id="B-001", session_id="session-1", run_id="run-1",
|
|
1381
1445
|
retry_count="0", resume_phase="null", state_dir=None,
|
|
1382
1446
|
output="out.md", project_root=str(bug_project), template=None,
|
|
1383
|
-
mode=None,
|
|
1384
1447
|
)
|
|
1385
1448
|
replacements = bugfix_build_replacements(
|
|
1386
1449
|
args, {"id": "B-001", "title": "Broken"}, {}, str(Path("dev-pipeline/scripts").resolve())
|
|
@@ -1403,7 +1466,6 @@ class TestHeadlessPromptCleanupF033:
|
|
|
1403
1466
|
bug_id="B-001", session_id="session-1", run_id="run-1",
|
|
1404
1467
|
retry_count="0", resume_phase="null", state_dir=None,
|
|
1405
1468
|
output="out.md", project_root=str(bug_project), template=None,
|
|
1406
|
-
mode=None,
|
|
1407
1469
|
)
|
|
1408
1470
|
replacements = bugfix_build_replacements(
|
|
1409
1471
|
args, {"id": "B-001", "title": "Broken"}, {}, str(Path("dev-pipeline/scripts").resolve())
|
|
@@ -1455,11 +1517,11 @@ class TestFeatureBootstrapShellExtraction:
|
|
|
1455
1517
|
assert "text final-verdict .prizmkit/specs/123-prompt-cleanup/review-report.md" in prompt
|
|
1456
1518
|
|
|
1457
1519
|
def test_rendered_section_feature_prompts_use_runtime_helper_not_extracted_bash(self, tmp_path):
|
|
1458
|
-
for
|
|
1520
|
+
for complexity in ("low", "medium", "high"):
|
|
1459
1521
|
prompt = _render_feature_prompt(
|
|
1460
|
-
tmp_path / f"section-{
|
|
1461
|
-
|
|
1462
|
-
|
|
1522
|
+
tmp_path / f"section-{complexity}",
|
|
1523
|
+
complexity=complexity,
|
|
1524
|
+
browser_interaction={"tool": "auto", "verify_steps": ["Open the page"]},
|
|
1463
1525
|
)
|
|
1464
1526
|
|
|
1465
1527
|
self.assert_shell_extracted_operations_removed(prompt)
|
|
@@ -1474,7 +1536,6 @@ class TestFeatureBootstrapShellExtraction:
|
|
|
1474
1536
|
def test_rendered_retained_tier3_feature_prompt_uses_runtime_helper_not_extracted_bash(self, tmp_path):
|
|
1475
1537
|
prompt = _render_feature_prompt(
|
|
1476
1538
|
tmp_path / "legacy-bootstrap-tier3.md",
|
|
1477
|
-
mode="full",
|
|
1478
1539
|
template="bootstrap-tier3.md",
|
|
1479
1540
|
browser_interaction={"tool": "playwright-cli", "verify_steps": ["Open the page"]},
|
|
1480
1541
|
)
|
|
@@ -1488,12 +1549,18 @@ class TestFeatureBootstrapShellExtraction:
|
|
|
1488
1549
|
def test_retained_tier3_specific_extracted_operations_map_to_helper_commands(self, tmp_path):
|
|
1489
1550
|
tier3 = _render_feature_prompt(
|
|
1490
1551
|
tmp_path / "legacy-tier3-helper-map",
|
|
1491
|
-
mode="full",
|
|
1492
1552
|
template="bootstrap-tier3.md",
|
|
1493
1553
|
)
|
|
1494
1554
|
assert "text search . --pattern \"123-prompt-cleanup\" --json" in tier3
|
|
1495
1555
|
assert "text count .prizmkit/specs/123-prompt-cleanup/plan.md --pattern \"- [ ]\"" in tier3
|
|
1496
|
-
assert "
|
|
1556
|
+
assert "/prizmkit-committer operation=prepare-runtime-commit" in tier3
|
|
1557
|
+
assert "Require `COMMIT_REQUEST_READY`; this session records `COMMIT_PENDING`" in tier3
|
|
1558
|
+
assert "The Python runtime validates the request" in tier3
|
|
1559
|
+
assert "all `.prizmkit/**` paths are PrizmKit-internal" in tier3
|
|
1560
|
+
assert "project-specific transient data must already be covered by git ignore rules" in tier3.lower()
|
|
1561
|
+
assert "Unknown Git-visible paths are not silently ignored" in tier3
|
|
1562
|
+
assert "git add" not in tier3
|
|
1563
|
+
assert "git commit" not in tier3
|
|
1497
1564
|
|
|
1498
1565
|
|
|
1499
1566
|
# ---------------------------------------------------------------------------
|
|
@@ -1587,16 +1654,17 @@ class TestDetectTestCommands:
|
|
|
1587
1654
|
|
|
1588
1655
|
|
|
1589
1656
|
|
|
1590
|
-
def
|
|
1657
|
+
def test_feature_checkpoint_generation_skips_noop_and_preserves_finalization_order():
|
|
1591
1658
|
from generate_bootstrap_prompt import generate_checkpoint_definition
|
|
1592
1659
|
|
|
1593
1660
|
checkpoint = generate_checkpoint_definition(
|
|
1594
1661
|
[
|
|
1595
1662
|
("phase0-test-baseline", "noop"),
|
|
1596
1663
|
("phase-context-snapshot", "context"),
|
|
1597
|
-
("
|
|
1664
|
+
("feature-completion-summary", "summary"),
|
|
1665
|
+
("phase-commit", "retrospective"),
|
|
1666
|
+
("runtime-commit-handoff", "commit"),
|
|
1598
1667
|
],
|
|
1599
|
-
"standard",
|
|
1600
1668
|
"feature-pipeline",
|
|
1601
1669
|
"F-123",
|
|
1602
1670
|
"123-prompt-cleanup",
|
|
@@ -1605,9 +1673,9 @@ def test_feature_checkpoint_generation_skips_noop_and_expands_only_finalization_
|
|
|
1605
1673
|
|
|
1606
1674
|
assert [step["skill"] for step in checkpoint["steps"]] == [
|
|
1607
1675
|
"context-snapshot",
|
|
1676
|
+
"completion-summary",
|
|
1608
1677
|
"prizmkit-retrospective",
|
|
1609
1678
|
"prizmkit-committer",
|
|
1610
|
-
"completion-summary",
|
|
1611
1679
|
]
|
|
1612
1680
|
assert checkpoint["steps"][0]["depends_on"] is None
|
|
1613
1681
|
assert checkpoint["steps"][1]["depends_on"] == "S01"
|