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
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Validate app-planner Rules Configuration resources as one coherent contract."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import json
|
|
8
|
+
import re
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
LAYERS = ("frontend", "backend", "database", "mobile")
|
|
13
|
+
SCHEMA_VERSION = "app-planner-question-manifest-v2"
|
|
14
|
+
REQUIRED_RENDER_CONTRACT = {
|
|
15
|
+
"fixed_rules_policy": "invariants_only",
|
|
16
|
+
"selected_decision_precedence": True,
|
|
17
|
+
"quick_mode_requires_explicit_authorization": True,
|
|
18
|
+
"unmapped_option_policy": "semantic_fallback",
|
|
19
|
+
"not_applicable_policy": "explicit_statement",
|
|
20
|
+
"conflict_policy": "fail_generation",
|
|
21
|
+
}
|
|
22
|
+
QUESTION_HEADING_RE = re.compile(r"^###\s+(Q[0-9]+[a-z]?)\.", re.MULTILINE)
|
|
23
|
+
PLACEHOLDER_RE = re.compile(r"\{\{\s*([A-Za-z0-9_]+)\s*\}\}")
|
|
24
|
+
CONDITION_RE = re.compile(
|
|
25
|
+
r"^(Q[0-9]+[a-z]?)\s*(?:==|!=)\s*[A-Z][A-Z0-9_-]*$"
|
|
26
|
+
r"|^(Q[0-9]+[a-z]?)\s+in\s+\[[A-Z][A-Z0-9_-]*(?:,\s*[A-Z][A-Z0-9_-]*)*\]$"
|
|
27
|
+
)
|
|
28
|
+
CONDITION_FIELDS = (
|
|
29
|
+
"required_if",
|
|
30
|
+
"auto_derived_when",
|
|
31
|
+
"dual_native_repeat_when",
|
|
32
|
+
"note_open_ended_when",
|
|
33
|
+
)
|
|
34
|
+
FIXED_RULE_FORBIDDEN_PATTERNS = (
|
|
35
|
+
"all user-facing text must use i18n keys",
|
|
36
|
+
"only add dependencies after explicit permission",
|
|
37
|
+
"all apis must follow rest conventions",
|
|
38
|
+
"jwt is the default authentication mechanism",
|
|
39
|
+
"every module must have unit tests",
|
|
40
|
+
"all deep links must use universal links",
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _load_json(path: Path) -> dict[str, Any]:
|
|
45
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
46
|
+
if not isinstance(data, dict):
|
|
47
|
+
raise ValueError("root must be a JSON object")
|
|
48
|
+
return data
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _as_string_list(value: object, label: str, errors: list[str]) -> list[str]:
|
|
52
|
+
if not isinstance(value, list) or not all(isinstance(item, str) for item in value):
|
|
53
|
+
errors.append(f"{label} must be an array of strings")
|
|
54
|
+
return []
|
|
55
|
+
return list(value)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _validate_layer(rules_root: Path, layer: str, errors: list[str]) -> None:
|
|
59
|
+
layer_root = rules_root / layer
|
|
60
|
+
required_files = {
|
|
61
|
+
"manifest": layer_root / "question-manifest.json",
|
|
62
|
+
"bank": layer_root / "question-bank.md",
|
|
63
|
+
"fixed": layer_root / "fixed-rules.md",
|
|
64
|
+
"derivation": layer_root / "derivation-rules.md",
|
|
65
|
+
"template": layer_root / "template.md",
|
|
66
|
+
}
|
|
67
|
+
missing = [name for name, path in required_files.items() if not path.is_file()]
|
|
68
|
+
if missing:
|
|
69
|
+
errors.append(f"{layer}: missing resources: {', '.join(missing)}")
|
|
70
|
+
return
|
|
71
|
+
|
|
72
|
+
try:
|
|
73
|
+
manifest = _load_json(required_files["manifest"])
|
|
74
|
+
except (OSError, json.JSONDecodeError, ValueError) as exc:
|
|
75
|
+
errors.append(f"{layer}: invalid question-manifest.json: {exc}")
|
|
76
|
+
return
|
|
77
|
+
|
|
78
|
+
if manifest.get("schema_version") != SCHEMA_VERSION:
|
|
79
|
+
errors.append(
|
|
80
|
+
f"{layer}: schema_version must be {SCHEMA_VERSION!r}, "
|
|
81
|
+
f"got {manifest.get('schema_version')!r}"
|
|
82
|
+
)
|
|
83
|
+
if manifest.get("layer") != layer:
|
|
84
|
+
errors.append(f"{layer}: manifest layer must equal directory name")
|
|
85
|
+
|
|
86
|
+
render_contract = manifest.get("render_contract")
|
|
87
|
+
if not isinstance(render_contract, dict):
|
|
88
|
+
errors.append(f"{layer}: render_contract must be an object")
|
|
89
|
+
render_contract = {}
|
|
90
|
+
for key, expected in REQUIRED_RENDER_CONTRACT.items():
|
|
91
|
+
if render_contract.get(key) != expected:
|
|
92
|
+
errors.append(
|
|
93
|
+
f"{layer}: render_contract.{key} must be {expected!r}, "
|
|
94
|
+
f"got {render_contract.get(key)!r}"
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
questions = manifest.get("questions")
|
|
98
|
+
if not isinstance(questions, list) or not all(isinstance(item, dict) for item in questions):
|
|
99
|
+
errors.append(f"{layer}: questions must be an array of objects")
|
|
100
|
+
questions = []
|
|
101
|
+
question_ids = [str(item.get("id") or "") for item in questions]
|
|
102
|
+
if any(not question_id for question_id in question_ids):
|
|
103
|
+
errors.append(f"{layer}: every question must have a non-empty id")
|
|
104
|
+
if len(question_ids) != len(set(question_ids)):
|
|
105
|
+
errors.append(f"{layer}: duplicate question ids in manifest")
|
|
106
|
+
if manifest.get("total_questions") != len(question_ids):
|
|
107
|
+
errors.append(
|
|
108
|
+
f"{layer}: total_questions={manifest.get('total_questions')!r} "
|
|
109
|
+
f"does not match {len(question_ids)} manifest questions"
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
groups = manifest.get("groups")
|
|
113
|
+
if not isinstance(groups, list) or not all(isinstance(item, dict) for item in groups):
|
|
114
|
+
errors.append(f"{layer}: groups must be an array of objects")
|
|
115
|
+
groups = []
|
|
116
|
+
group_ids = {str(item.get("id") or "") for item in groups}
|
|
117
|
+
question_positions = {question_id: index for index, question_id in enumerate(question_ids)}
|
|
118
|
+
for index, question in enumerate(questions):
|
|
119
|
+
question_id = str(question.get("id") or "")
|
|
120
|
+
if question.get("group") not in group_ids:
|
|
121
|
+
errors.append(
|
|
122
|
+
f"{layer}: question {question_id!r} references unknown group "
|
|
123
|
+
f"{question.get('group')!r}"
|
|
124
|
+
)
|
|
125
|
+
varying_parent = question.get("options_vary_by")
|
|
126
|
+
if varying_parent is not None:
|
|
127
|
+
if not isinstance(varying_parent, str) or varying_parent not in question_positions:
|
|
128
|
+
errors.append(
|
|
129
|
+
f"{layer}: question {question_id} options_vary_by references "
|
|
130
|
+
f"unknown question {varying_parent!r}"
|
|
131
|
+
)
|
|
132
|
+
elif question_positions[varying_parent] >= index:
|
|
133
|
+
errors.append(
|
|
134
|
+
f"{layer}: question {question_id} options_vary_by must reference "
|
|
135
|
+
"an earlier question"
|
|
136
|
+
)
|
|
137
|
+
for field in CONDITION_FIELDS:
|
|
138
|
+
condition = question.get(field)
|
|
139
|
+
if condition is None:
|
|
140
|
+
continue
|
|
141
|
+
if not isinstance(condition, str):
|
|
142
|
+
errors.append(f"{layer}: question {question_id} {field} must be a string")
|
|
143
|
+
continue
|
|
144
|
+
match = CONDITION_RE.fullmatch(condition)
|
|
145
|
+
if match is None:
|
|
146
|
+
errors.append(
|
|
147
|
+
f"{layer}: question {question_id} {field} has invalid option-ID "
|
|
148
|
+
f"condition syntax: {condition!r}"
|
|
149
|
+
)
|
|
150
|
+
continue
|
|
151
|
+
referenced_question = match.group(1) or match.group(2)
|
|
152
|
+
if referenced_question not in question_positions:
|
|
153
|
+
errors.append(
|
|
154
|
+
f"{layer}: question {question_id} {field} references unknown "
|
|
155
|
+
f"question {referenced_question}"
|
|
156
|
+
)
|
|
157
|
+
elif question_positions[referenced_question] >= index:
|
|
158
|
+
errors.append(
|
|
159
|
+
f"{layer}: question {question_id} {field} must reference an earlier question"
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
bank_text = required_files["bank"].read_text(encoding="utf-8")
|
|
163
|
+
bank_question_ids = QUESTION_HEADING_RE.findall(bank_text)
|
|
164
|
+
if question_ids != bank_question_ids:
|
|
165
|
+
errors.append(
|
|
166
|
+
f"{layer}: question bank/manifest drift; manifest={question_ids}, "
|
|
167
|
+
f"question_bank={bank_question_ids}"
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
fallback_questions = _as_string_list(
|
|
171
|
+
render_contract.get("semantic_fallback_questions"),
|
|
172
|
+
f"{layer}: render_contract.semantic_fallback_questions",
|
|
173
|
+
errors,
|
|
174
|
+
)
|
|
175
|
+
if fallback_questions != question_ids:
|
|
176
|
+
errors.append(
|
|
177
|
+
f"{layer}: semantic_fallback_questions must match manifest question order; "
|
|
178
|
+
f"got {fallback_questions}"
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
placeholders = manifest.get("template_placeholders")
|
|
182
|
+
if not isinstance(placeholders, dict):
|
|
183
|
+
errors.append(f"{layer}: template_placeholders must be an object")
|
|
184
|
+
placeholders = {}
|
|
185
|
+
declared: set[str] = set()
|
|
186
|
+
for category in ("from_questions", "from_fixed_rules", "auto_generated", "metadata"):
|
|
187
|
+
values = _as_string_list(
|
|
188
|
+
placeholders.get(category),
|
|
189
|
+
f"{layer}: template_placeholders.{category}",
|
|
190
|
+
errors,
|
|
191
|
+
)
|
|
192
|
+
overlap = declared.intersection(values)
|
|
193
|
+
if overlap:
|
|
194
|
+
errors.append(
|
|
195
|
+
f"{layer}: placeholders declared in multiple categories: {sorted(overlap)}"
|
|
196
|
+
)
|
|
197
|
+
declared.update(values)
|
|
198
|
+
|
|
199
|
+
template_text = required_files["template"].read_text(encoding="utf-8")
|
|
200
|
+
actual = set(PLACEHOLDER_RE.findall(template_text))
|
|
201
|
+
undeclared = sorted(actual - declared)
|
|
202
|
+
unused = sorted(declared - actual)
|
|
203
|
+
if undeclared:
|
|
204
|
+
errors.append(f"{layer}: template has undeclared placeholders: {undeclared}")
|
|
205
|
+
if unused:
|
|
206
|
+
errors.append(f"{layer}: manifest declares unused placeholders: {unused}")
|
|
207
|
+
if "Generated by app-planner Rules Configuration" not in template_text:
|
|
208
|
+
errors.append(f"{layer}: template must identify app-planner Rules Configuration as owner")
|
|
209
|
+
if ".claude/skills/" in template_text:
|
|
210
|
+
errors.append(f"{layer}: template contains a platform-specific source pointer")
|
|
211
|
+
if re.search(r"generated by the `(?:frontend|backend|database|mobile)-rules` skill", template_text, re.I):
|
|
212
|
+
errors.append(f"{layer}: template references a retired independent layer-rules skill")
|
|
213
|
+
|
|
214
|
+
fixed_text = required_files["fixed"].read_text(encoding="utf-8")
|
|
215
|
+
if "POLICY_CLASS: invariant" not in fixed_text:
|
|
216
|
+
errors.append(f"{layer}: fixed-rules.md must declare POLICY_CLASS: invariant")
|
|
217
|
+
if "Selected-decision boundary" not in fixed_text:
|
|
218
|
+
errors.append(f"{layer}: fixed-rules.md must state the selected-decision boundary")
|
|
219
|
+
normalized_fixed_text = " ".join(fixed_text.lower().split())
|
|
220
|
+
for forbidden_pattern in FIXED_RULE_FORBIDDEN_PATTERNS:
|
|
221
|
+
if forbidden_pattern in normalized_fixed_text:
|
|
222
|
+
errors.append(
|
|
223
|
+
f"{layer}: fixed-rules.md contains decision-owned policy: "
|
|
224
|
+
f"{forbidden_pattern!r}"
|
|
225
|
+
)
|
|
226
|
+
for placeholder in placeholders.get("from_fixed_rules", []):
|
|
227
|
+
if f"{{{{ {placeholder} }}}}" not in fixed_text:
|
|
228
|
+
errors.append(
|
|
229
|
+
f"{layer}: fixed-rules.md has no injection mapping for {{{{ {placeholder} }}}}"
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
derivation_text = required_files["derivation"].read_text(encoding="utf-8")
|
|
233
|
+
if "SEMANTIC_FALLBACK_POLICY" not in derivation_text:
|
|
234
|
+
errors.append(f"{layer}: derivation-rules.md must define SEMANTIC_FALLBACK_POLICY")
|
|
235
|
+
|
|
236
|
+
multi_profile = manifest.get("multi_profile_contract")
|
|
237
|
+
if layer == "mobile":
|
|
238
|
+
expected_multi_profile = {
|
|
239
|
+
"when": "Q1 == E",
|
|
240
|
+
"dimensions": ["Q4", "Q6", "Q7", "Q8", "Q13", "Q14"],
|
|
241
|
+
"profiles": ["ios", "android"],
|
|
242
|
+
"output_placeholder": "platform_profiles",
|
|
243
|
+
}
|
|
244
|
+
if multi_profile != expected_multi_profile:
|
|
245
|
+
errors.append("mobile: multi_profile_contract does not match the dual-native contract")
|
|
246
|
+
if "platform_profiles" not in placeholders.get("auto_generated", []):
|
|
247
|
+
errors.append("mobile: platform_profiles must be an auto-generated placeholder")
|
|
248
|
+
elif multi_profile is not None:
|
|
249
|
+
errors.append(f"{layer}: multi_profile_contract is only valid for mobile")
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def validate_rules_root(rules_root: Path) -> dict[str, object]:
|
|
253
|
+
errors: list[str] = []
|
|
254
|
+
if not rules_root.is_dir():
|
|
255
|
+
errors.append(f"rules root does not exist: {rules_root}")
|
|
256
|
+
else:
|
|
257
|
+
actual_layers = sorted(path.name for path in rules_root.iterdir() if path.is_dir())
|
|
258
|
+
unexpected = sorted(set(actual_layers) - set(LAYERS))
|
|
259
|
+
missing = sorted(set(LAYERS) - set(actual_layers))
|
|
260
|
+
if unexpected:
|
|
261
|
+
errors.append(f"unexpected rules layers: {unexpected}")
|
|
262
|
+
if missing:
|
|
263
|
+
errors.append(f"missing rules layers: {missing}")
|
|
264
|
+
for layer in LAYERS:
|
|
265
|
+
if (rules_root / layer).is_dir():
|
|
266
|
+
_validate_layer(rules_root, layer, errors)
|
|
267
|
+
return {
|
|
268
|
+
"valid": not errors,
|
|
269
|
+
"layers": list(LAYERS),
|
|
270
|
+
"errors": errors,
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def main() -> int:
|
|
275
|
+
parser = argparse.ArgumentParser(
|
|
276
|
+
description="Validate app-planner Rules Configuration manifests and rendering resources."
|
|
277
|
+
)
|
|
278
|
+
parser.add_argument(
|
|
279
|
+
"--rules-root",
|
|
280
|
+
type=Path,
|
|
281
|
+
default=Path(__file__).resolve().parent.parent / "references" / "rules",
|
|
282
|
+
help="Path containing frontend/backend/database/mobile rules resources.",
|
|
283
|
+
)
|
|
284
|
+
args = parser.parse_args()
|
|
285
|
+
report = validate_rules_root(args.rules_root.expanduser().resolve())
|
|
286
|
+
print(json.dumps(report, indent=2, ensure_ascii=False))
|
|
287
|
+
return 0 if report["valid"] else 1
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
if __name__ == "__main__":
|
|
291
|
+
raise SystemExit(main())
|
|
@@ -17,7 +17,7 @@ Use this checklist for manual validation when `validate-bug-list.py` is not avai
|
|
|
17
17
|
- [ ] `error_source.type`: one of `stack_trace`, `user_report`, `failed_test`, `log_pattern`, `monitoring_alert`
|
|
18
18
|
- [ ] `verification_type`: one of `automated`, `manual`, `hybrid`
|
|
19
19
|
- [ ] `acceptance_criteria`: non-empty array of strings
|
|
20
|
-
- [ ] `status`: one of `pending`, `in_progress`, `completed`, `failed`, `skipped`, `needs_info`; new bugs should start as `pending`
|
|
20
|
+
- [ ] `status`: one of `pending`, `in_progress`, `completed`, `failed`, `skipped`, `needs_info`, `auto_skipped`; new bugs should start as `pending`, while `auto_skipped` is runtime-managed for blocked descendants
|
|
21
21
|
- [ ] `dependencies` if present: array of existing `B-NNN` IDs; no self-dependencies
|
|
22
22
|
|
|
23
23
|
## Consistency Checks
|
|
@@ -29,7 +29,7 @@ from datetime import datetime, timezone
|
|
|
29
29
|
VALID_SEVERITIES = {"critical", "high", "medium", "low"}
|
|
30
30
|
VALID_SOURCE_TYPES = {"stack_trace", "user_report", "failed_test", "log_pattern", "monitoring_alert"}
|
|
31
31
|
VALID_VERIFICATION_TYPES = {"automated", "manual", "hybrid"}
|
|
32
|
-
VALID_STATUSES = {"pending", "in_progress", "completed", "failed", "skipped", "needs_info"}
|
|
32
|
+
VALID_STATUSES = {"pending", "in_progress", "completed", "failed", "skipped", "needs_info", "auto_skipped"}
|
|
33
33
|
BUG_ID_PATTERN = re.compile(r"^B-\d{3}$")
|
|
34
34
|
SCHEMA_VERSION = "dev-pipeline-bug-fix-list-v1"
|
|
35
35
|
|
|
@@ -67,7 +67,7 @@ Only the start intent uses the execution-mode and configuration rounds.
|
|
|
67
67
|
|
|
68
68
|
Use one `AskUserQuestion` call with exactly one question:
|
|
69
69
|
|
|
70
|
-
- **Foreground (Recommended)** — build `bugfix
|
|
70
|
+
- **Foreground (Recommended)** — build `run bugfix`; output remains attached to the user's terminal.
|
|
71
71
|
- **Background daemon** — build `daemon bugfix start`; the runtime manages detached execution and logs.
|
|
72
72
|
|
|
73
73
|
Wait for the response before asking configuration.
|
|
@@ -100,7 +100,7 @@ Foreground template:
|
|
|
100
100
|
|
|
101
101
|
```bash
|
|
102
102
|
VERBOSE=1 MAX_RETRIES=3 \
|
|
103
|
-
python3 ./.prizmkit/dev-pipeline/cli.py bugfix
|
|
103
|
+
python3 ./.prizmkit/dev-pipeline/cli.py run bugfix .prizmkit/plans/bug-fix-list.json
|
|
104
104
|
```
|
|
105
105
|
|
|
106
106
|
Background template:
|
|
@@ -118,7 +118,7 @@ Return:
|
|
|
118
118
|
|
|
119
119
|
```bash
|
|
120
120
|
python3 ./.prizmkit/dev-pipeline/cli.py daemon bugfix status
|
|
121
|
-
python3 ./.prizmkit/dev-pipeline/cli.py bugfix
|
|
121
|
+
python3 ./.prizmkit/dev-pipeline/cli.py status bugfix .prizmkit/plans/bug-fix-list.json
|
|
122
122
|
```
|
|
123
123
|
|
|
124
124
|
Do not infer completion from the plan file alone when runtime state may exist.
|
|
@@ -151,39 +151,47 @@ Optionally include the daemon status command for the user to run afterward. Do n
|
|
|
151
151
|
|
|
152
152
|
## Retry Command
|
|
153
153
|
|
|
154
|
-
Reset
|
|
154
|
+
Reset never starts execution and rejects `--run`. Every reset command must select exactly one mode: `--state-only`, `--fresh-checkout`, `--clean`, or `--preserve-runtime`. Always return reset and run as separate commands.
|
|
155
155
|
|
|
156
|
-
|
|
156
|
+
State-only recovery resets selected failed/skipped/needs-info roots and only causal `auto_skipped` descendants whose complete blocker set is recovered. It resets retry/session/continuation state but never changes checkout identity, branches, worktrees, source changes, checkpoints, or artifact files. Selecting an auto-skipped bug includes all of its transitive blocking roots; `--all` recovers all eligible chains. Fresh-checkout performs the former standard reset, can discard uncommitted changes on the active task branch, and records a new checkout boundary. Clean additionally deletes the task branch and clears task history/artifacts. Preserve-runtime scans the whole list for failed roots, retains runtime history and valid checkout/WIP, and alone may replace a missing branch from the recorded base and rewind implementation-and-later checkpoints. Never hide these effects behind the word “retry.”
|
|
157
157
|
|
|
158
158
|
Use `AskUserQuestion` before constructing retry commands:
|
|
159
159
|
|
|
160
|
-
- **
|
|
161
|
-
- **
|
|
160
|
+
- **State-only causal recovery** — recover one bug closure, or all closures with `--all`, while preserving Git/checkouts/checkpoints/artifact files.
|
|
161
|
+
- **Preserve failed runtime (Recommended for continuing failed work)** — recover all failed chains without cleanup while retaining history and continuation evidence; disclose that this is whole-list failed-only recovery.
|
|
162
|
+
- **Fresh-checkout retry** — reset one bug, then run it separately; warn that the active task branch can lose uncommitted changes.
|
|
162
163
|
- **Clean retry** — reset one bug, delete its task branch/history/artifacts, then run it separately.
|
|
163
164
|
- **Cancel** — return no reset or run command.
|
|
164
165
|
|
|
166
|
+
State-only template:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
python3 ./.prizmkit/dev-pipeline/cli.py reset bugfix B-001 --state-only .prizmkit/plans/bug-fix-list.json
|
|
170
|
+
python3 ./.prizmkit/dev-pipeline/cli.py run bugfix B-001 .prizmkit/plans/bug-fix-list.json
|
|
171
|
+
```
|
|
172
|
+
|
|
165
173
|
Preserve-runtime template:
|
|
166
174
|
|
|
167
175
|
```bash
|
|
168
176
|
python3 ./.prizmkit/dev-pipeline/cli.py reset bugfix --failed --preserve-runtime .prizmkit/plans/bug-fix-list.json
|
|
169
|
-
python3 ./.prizmkit/dev-pipeline/cli.py bugfix
|
|
177
|
+
python3 ./.prizmkit/dev-pipeline/cli.py run bugfix .prizmkit/plans/bug-fix-list.json
|
|
170
178
|
```
|
|
171
179
|
|
|
172
|
-
|
|
180
|
+
Fresh-checkout template:
|
|
173
181
|
|
|
174
182
|
```bash
|
|
175
|
-
python3 ./.prizmkit/dev-pipeline/cli.py reset bugfix B-001 .prizmkit/plans/bug-fix-list.json
|
|
176
|
-
python3 ./.prizmkit/dev-pipeline/cli.py bugfix
|
|
183
|
+
python3 ./.prizmkit/dev-pipeline/cli.py reset bugfix B-001 --fresh-checkout .prizmkit/plans/bug-fix-list.json
|
|
184
|
+
python3 ./.prizmkit/dev-pipeline/cli.py run bugfix B-001 .prizmkit/plans/bug-fix-list.json
|
|
177
185
|
```
|
|
178
186
|
|
|
179
187
|
Clean template:
|
|
180
188
|
|
|
181
189
|
```bash
|
|
182
190
|
python3 ./.prizmkit/dev-pipeline/cli.py reset bugfix B-001 --clean .prizmkit/plans/bug-fix-list.json
|
|
183
|
-
python3 ./.prizmkit/dev-pipeline/cli.py bugfix
|
|
191
|
+
python3 ./.prizmkit/dev-pipeline/cli.py run bugfix B-001 .prizmkit/plans/bug-fix-list.json
|
|
184
192
|
```
|
|
185
193
|
|
|
186
|
-
|
|
194
|
+
State-only requires exactly one ID or explicit `--all` and rejects ranges/status filters. Fresh-checkout and clean accept one ID, range, or supported status filter. Preserve-runtime requires `--failed`, scans the whole list, and rejects IDs, ranges, `--all`, clean, other modes, and other filters. Retry-budget flags belong on the run command, never reset. For fresh-checkout or clean retry, require explicit acknowledgement of possible uncommitted-change loss; clean additionally requires acknowledgement of branch/history/artifact deletion.
|
|
187
195
|
|
|
188
196
|
## Output Contract
|
|
189
197
|
|
|
@@ -71,7 +71,7 @@ Only the start intent uses the execution-mode and configuration rounds.
|
|
|
71
71
|
|
|
72
72
|
Use one `AskUserQuestion` call with exactly one question:
|
|
73
73
|
|
|
74
|
-
- **Foreground (Recommended)** — build `feature
|
|
74
|
+
- **Foreground (Recommended)** — build `run feature`; output remains attached to the user's terminal.
|
|
75
75
|
- **Background daemon** — build `daemon feature start`; the runtime manages detached execution and logs.
|
|
76
76
|
|
|
77
77
|
Wait for the response before asking configuration.
|
|
@@ -104,14 +104,14 @@ Foreground template:
|
|
|
104
104
|
|
|
105
105
|
```bash
|
|
106
106
|
VERBOSE=1 MAX_RETRIES=3 \
|
|
107
|
-
python3 ./.prizmkit/dev-pipeline/cli.py feature
|
|
107
|
+
python3 ./.prizmkit/dev-pipeline/cli.py run feature .prizmkit/plans/feature-list.json
|
|
108
108
|
```
|
|
109
109
|
|
|
110
110
|
Foreground subset template:
|
|
111
111
|
|
|
112
112
|
```bash
|
|
113
113
|
VERBOSE=1 MAX_RETRIES=3 \
|
|
114
|
-
python3 ./.prizmkit/dev-pipeline/cli.py feature
|
|
114
|
+
python3 ./.prizmkit/dev-pipeline/cli.py run feature .prizmkit/plans/feature-list.json --features F-001:F-005
|
|
115
115
|
```
|
|
116
116
|
|
|
117
117
|
Background template:
|
|
@@ -136,7 +136,7 @@ Return both commands when the user asks for overall status:
|
|
|
136
136
|
|
|
137
137
|
```bash
|
|
138
138
|
python3 ./.prizmkit/dev-pipeline/cli.py daemon feature status
|
|
139
|
-
python3 ./.prizmkit/dev-pipeline/cli.py feature
|
|
139
|
+
python3 ./.prizmkit/dev-pipeline/cli.py status feature .prizmkit/plans/feature-list.json
|
|
140
140
|
```
|
|
141
141
|
|
|
142
142
|
Do not infer completion from the plan file alone when runtime state may exist.
|
|
@@ -169,39 +169,47 @@ Optionally include the daemon status command for the user to run afterward. Do n
|
|
|
169
169
|
|
|
170
170
|
## Retry Command
|
|
171
171
|
|
|
172
|
-
Reset
|
|
172
|
+
Reset never starts execution and rejects `--run`. Every reset command must select exactly one mode: `--state-only`, `--fresh-checkout`, `--clean`, or `--preserve-runtime`. Always return reset and run as separate commands.
|
|
173
173
|
|
|
174
|
-
|
|
174
|
+
State-only recovery resets selected failed/skipped roots and only causal `auto_skipped` descendants whose complete blocker set is recovered. It resets retry/session/continuation state but never changes checkout identity, branches, worktrees, source changes, checkpoints, or artifact files. Selecting an auto-skipped feature includes all of its transitive blocking roots; `--all` recovers all eligible chains. Fresh-checkout performs the former standard reset, can discard uncommitted changes on the active task branch, and records a new checkout boundary. Clean additionally deletes the task branch and clears task history/artifacts. Preserve-runtime scans the whole list for failed roots, retains runtime history and valid checkout/WIP, and alone may replace a missing branch from the recorded base and rewind implementation-and-later checkpoints. Never hide these effects behind the word “retry.”
|
|
175
175
|
|
|
176
176
|
Use `AskUserQuestion` before constructing retry commands:
|
|
177
177
|
|
|
178
|
-
- **
|
|
179
|
-
- **
|
|
178
|
+
- **State-only causal recovery** — recover one feature closure, or all closures with `--all`, while preserving Git/checkouts/checkpoints/artifact files.
|
|
179
|
+
- **Preserve failed runtime (Recommended for continuing failed work)** — recover all failed chains without cleanup while retaining history and continuation evidence; disclose that this is whole-list failed-only recovery.
|
|
180
|
+
- **Fresh-checkout retry** — reset one feature, then run it separately; warn that the active task branch can lose uncommitted changes.
|
|
180
181
|
- **Clean retry** — reset one feature, delete its task branch/history/artifacts, then run it separately.
|
|
181
182
|
- **Cancel** — return no reset or run command.
|
|
182
183
|
|
|
184
|
+
State-only template:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
python3 ./.prizmkit/dev-pipeline/cli.py reset feature F-001 --state-only .prizmkit/plans/feature-list.json
|
|
188
|
+
python3 ./.prizmkit/dev-pipeline/cli.py run feature F-001 .prizmkit/plans/feature-list.json
|
|
189
|
+
```
|
|
190
|
+
|
|
183
191
|
Preserve-runtime template:
|
|
184
192
|
|
|
185
193
|
```bash
|
|
186
194
|
python3 ./.prizmkit/dev-pipeline/cli.py reset feature --failed --preserve-runtime .prizmkit/plans/feature-list.json
|
|
187
|
-
python3 ./.prizmkit/dev-pipeline/cli.py feature
|
|
195
|
+
python3 ./.prizmkit/dev-pipeline/cli.py run feature .prizmkit/plans/feature-list.json
|
|
188
196
|
```
|
|
189
197
|
|
|
190
|
-
|
|
198
|
+
Fresh-checkout template:
|
|
191
199
|
|
|
192
200
|
```bash
|
|
193
|
-
python3 ./.prizmkit/dev-pipeline/cli.py reset feature F-001 .prizmkit/plans/feature-list.json
|
|
194
|
-
python3 ./.prizmkit/dev-pipeline/cli.py feature
|
|
201
|
+
python3 ./.prizmkit/dev-pipeline/cli.py reset feature F-001 --fresh-checkout .prizmkit/plans/feature-list.json
|
|
202
|
+
python3 ./.prizmkit/dev-pipeline/cli.py run feature F-001 .prizmkit/plans/feature-list.json
|
|
195
203
|
```
|
|
196
204
|
|
|
197
205
|
Clean template:
|
|
198
206
|
|
|
199
207
|
```bash
|
|
200
208
|
python3 ./.prizmkit/dev-pipeline/cli.py reset feature F-001 --clean .prizmkit/plans/feature-list.json
|
|
201
|
-
python3 ./.prizmkit/dev-pipeline/cli.py feature
|
|
209
|
+
python3 ./.prizmkit/dev-pipeline/cli.py run feature F-001 .prizmkit/plans/feature-list.json
|
|
202
210
|
```
|
|
203
211
|
|
|
204
|
-
|
|
212
|
+
State-only requires exactly one ID or explicit `--all` and rejects ranges/status filters. Fresh-checkout and clean accept one ID, range, or supported status filter. Preserve-runtime requires `--failed`, scans the whole list, and rejects IDs, ranges, `--all`, clean, other modes, and other filters. Retry-budget flags belong on the run command, never reset. For fresh-checkout or clean retry, require explicit acknowledgement of possible uncommitted-change loss; clean additionally requires acknowledgement of branch/history/artifact deletion.
|
|
205
213
|
|
|
206
214
|
## Output Contract
|
|
207
215
|
|
|
@@ -57,7 +57,7 @@ prizmkit-plan
|
|
|
57
57
|
→ prizmkit-committer
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
For a one-entry experience, invoke `/prizmkit-workflow`; it owns ordering, state, repair routing, and stage invocation without replacing stage responsibilities. Atomic Skills receive explicit stage-local input, return stage-local results, and never read or write the coordinator's state. External injected Prompts may provide the same orchestration around atomic Skills.
|
|
61
61
|
|
|
62
62
|
All six stages are required for a formal requirement. A stage may choose a verification depth appropriate to the change, but it may not be silently skipped.
|
|
63
63
|
|
|
@@ -67,14 +67,14 @@ Typos, pure formatting, small documentation edits, and other explicitly low-risk
|
|
|
67
67
|
|
|
68
68
|
### Stage Responsibilities
|
|
69
69
|
|
|
70
|
-
| Stage | Responsibility |
|
|
70
|
+
| Stage | Responsibility | Stage-local success |
|
|
71
71
|
|---|---|---|
|
|
72
|
-
| `prizmkit-plan` | Clarify the requirement and create/review `spec.md` and `plan.md`. | `
|
|
73
|
-
| `prizmkit-implement` | Execute
|
|
74
|
-
| `prizmkit-code-review` |
|
|
75
|
-
| `prizmkit-test` |
|
|
76
|
-
| `prizmkit-retrospective` | Synchronize durable
|
|
77
|
-
| `prizmkit-committer` |
|
|
72
|
+
| `prizmkit-plan` | Clarify the requirement and create/review `spec.md` and `plan.md`. | `PLAN_READY` |
|
|
73
|
+
| `prizmkit-implement` | Execute supplied plan tasks and record completion. | `IMPLEMENTED` |
|
|
74
|
+
| `prizmkit-code-review` | Review, repair, verify, and converge on the supplied change. | `PASS` |
|
|
75
|
+
| `prizmkit-test` | Test the supplied change with a consistent report/result pair. | `TEST_PASS` |
|
|
76
|
+
| `prizmkit-retrospective` | Synchronize durable documentation or record no change. | `RETRO_COMPLETE` |
|
|
77
|
+
| `prizmkit-committer` | Create a confirmed interactive commit or prepare an exact Runtime request. | `COMMITTED` or `COMMIT_REQUEST_READY` |
|
|
78
78
|
|
|
79
79
|
### `prizmkit-workflow`
|
|
80
80
|
|
|
@@ -86,9 +86,8 @@ Use for a one-entry formal requirement workflow. It coordinates all six lifecycl
|
|
|
86
86
|
|
|
87
87
|
Initialization is a soft prerequisite, not a hard dependency:
|
|
88
88
|
|
|
89
|
-
- If project initialization is missing, `/prizmkit-
|
|
90
|
-
-
|
|
91
|
-
- Planning then reads the source tree, README, manifests, and available project rules as fallback context.
|
|
89
|
+
- If project initialization is missing, the user may still invoke `/prizmkit-init` separately or continue without it.
|
|
90
|
+
- Planning reads the source tree, README, manifests, and available project rules as fallback context.
|
|
92
91
|
- Later documentation synchronization reports when the Prizm documentation system is not initialized instead of pretending that synchronization completed.
|
|
93
92
|
|
|
94
93
|
## Independent Skills
|
|
@@ -110,7 +109,7 @@ REVIEW_NEEDS_FIXES
|
|
|
110
109
|
|
|
111
110
|
TEST_NEEDS_FIXES
|
|
112
111
|
→ preserve test-report.md and test-result.json
|
|
113
|
-
→ stop with the known correction
|
|
112
|
+
→ stop with the known correction
|
|
114
113
|
→ caller owns any later review/retest route
|
|
115
114
|
|
|
116
115
|
TEST_BLOCKED
|
|
@@ -122,7 +121,7 @@ Automatic outer repair is limited to three rounds. The workflow stops with a res
|
|
|
122
121
|
|
|
123
122
|
## Commit and Deployment Boundary
|
|
124
123
|
|
|
125
|
-
`prizmkit-committer`
|
|
124
|
+
The active coordinator validates all required evidence before invoking `prizmkit-committer` with exact `evidence_paths` and any caller-state `excluded_paths`. Interactive use confirms and creates a local commit. Preparation use returns `COMMIT_REQUEST_READY` without Git mutation; the injected Prompt records its pending state and Python Runtime executes the request.
|
|
126
125
|
|
|
127
126
|
Pushing to a remote is a separate explicit action. Deployment is always a separate invocation of `/prizmkit-deploy`.
|
|
128
127
|
|
|
@@ -131,10 +130,10 @@ Pushing to a remote is a separate explicit action. Deployment is always a separa
|
|
|
131
130
|
An active requirement may use:
|
|
132
131
|
|
|
133
132
|
```text
|
|
134
|
-
.prizmkit/state/workflows/<requirement-
|
|
133
|
+
.prizmkit/state/workflows/<requirement-identity>.json
|
|
135
134
|
```
|
|
136
135
|
|
|
137
|
-
This
|
|
136
|
+
`<requirement-identity>` is the validated exact basename of the active artifact directory. An existing state file that records a different artifact directory is a blocking collision and is never silently overwritten or suffixed. This coordinator-owned state records current stage, terminal status, repair scope, repair round, and resume entry. Atomic Skills do not read or write it. The target project controls whether the file is committed, ignored, or shared.
|
|
138
137
|
|
|
139
138
|
Read `${SKILL_DIR}/references/workflow-state-protocol.md` for the state protocol. If the state file is missing or stale, use `spec.md`, `plan.md`, the review report, `test-report.md`, and `test-result.json` to reconstruct the safest recoverable stage and report that reconstruction.
|
|
140
139
|
|
|
@@ -152,19 +151,19 @@ Then:
|
|
|
152
151
|
1. Optionally run /prizmkit-init when entering a project for the first time.
|
|
153
152
|
2. Start a formal requirement with /prizmkit-plan.
|
|
154
153
|
3. Continue through implement → code-review → test → retrospective → committer.
|
|
155
|
-
4.
|
|
154
|
+
4. For interactive use, confirm the local commit when `/prizmkit-committer operation=interactive-commit` presents it; pipeline runtimes use preparation mode and execute Git themselves.
|
|
156
155
|
5. Invoke /prizmkit-deploy separately when deployment or operations work is needed.
|
|
157
156
|
```
|
|
158
157
|
|
|
159
158
|
## Scope Boundary
|
|
160
159
|
|
|
161
|
-
This toolkit is self-contained
|
|
160
|
+
This toolkit is self-contained. External coordinators integrate through atomic stage inputs, stage-owned artifacts, and domain results; coordinator state and routing remain outside atomic Skill contracts.
|
|
162
161
|
|
|
163
162
|
The toolkit does not promise universal automatic handoff, universal deployment automation, automatic remote push, or access beyond the host platform's permissions and environment.
|
|
164
163
|
|
|
165
164
|
If the user says only "ship it", ask whether they mean:
|
|
166
165
|
|
|
167
|
-
1.
|
|
166
|
+
1. Use `/prizmkit-committer operation=interactive-commit` for a user-confirmed local commit, or preparation mode when a Python pipeline runtime owns Git execution.
|
|
168
167
|
2. Deploy or operate the project with `/prizmkit-deploy`.
|
|
169
168
|
|
|
170
169
|
Do not route an ambiguous "ship it" directly to either action.
|