diff --git a/presets/lean/commands/speckit.constitution.md b/presets/lean/commands/speckit.constitution.md index 920337003e..32dcd6b3df 100644 --- a/presets/lean/commands/speckit.constitution.md +++ b/presets/lean/commands/speckit.constitution.md @@ -8,6 +8,24 @@ description: Create or update the project constitution. $ARGUMENTS ``` +## Scope Guard + +This command's own work is limited to creating or updating the project constitution and +propagating constitution-driven changes to dependent Spec Kit artifacts. + +- Classify every part of the user input as constitution content or a separate non-governance + intent. Feature implementation, code generation, refactoring, build, and deployment requests + are examples of non-governance intents. +- You **MUST NOT** execute any non-governance intent. Defer each one to `Next Actions`. +- You **MUST NOT** create, modify, or delete application source files or other artifacts + unrelated to the constitution workflow. +- If an instruction could be either constitution content or a non-governance intent, ask for + clarification before making changes. +- After updating the constitution, list each deferred intent in a `Next Actions` section with an + appropriate follow-up Spec Kit command, such as `__SPECKIT_COMMAND_SPECIFY__`, but do not + invoke it. +- Omit `Next Actions` when there are no non-governance intents. + ## Outline 1. Create or update the project constitution and store it in `.specify/memory/constitution.md`. diff --git a/templates/commands/constitution.md b/templates/commands/constitution.md index 9d2d4ccc1a..f4f549043e 100644 --- a/templates/commands/constitution.md +++ b/templates/commands/constitution.md @@ -14,6 +14,25 @@ $ARGUMENTS You **MUST** consider the user input before proceeding (if not empty). +## Scope Guard + +This command's own work is limited to updating the project constitution and propagating +constitution-driven changes to the dependent artifacts identified in this command. + +- Classify every part of the user input as either constitution content or a separate, + non-governance intent. +- If the input includes feature implementation, code generation, refactoring, building, or + deployment requests, you **MUST NOT** execute them. Extract them as deferred intents instead. +- You **MUST NOT** create, modify, or delete application source files, feature routes, + components, tests, deployment files, or other artifacts unrelated to the constitution + workflow and its required propagation. +- If it is unclear whether an instruction is constitution content, ask for clarification before + making changes. +- After completing the constitution update, include a `Next Actions` section for each deferred + intent. List the original intent and suggest the appropriate follow-up Spec Kit command, such + as `__SPECKIT_COMMAND_SPECIFY__`, without invoking it. +- If there are no non-governance intents, omit the `Next Actions` section. + ## Pre-Execution Checks **Check for extension hooks (before constitution update)**: @@ -104,6 +123,7 @@ Follow this execution flow: - New version and bump rationale. - Any files flagged for manual follow-up. - Suggested commit message (e.g., `docs: amend constitution to vX.Y.Z (principle additions + governance update)`). + - A `Next Actions` section for any deferred non-governance intents. Formatting & Style Requirements: diff --git a/tests/test_presets.py b/tests/test_presets.py index aeceaf6a8b..314524e265 100644 --- a/tests/test_presets.py +++ b/tests/test_presets.py @@ -5381,6 +5381,9 @@ def test_disable_corrupted_registry_entry(self, project_dir, pack_dir): LEAN_PRESET_DIR = Path(__file__).parent.parent / "presets" / "lean" +CORE_CONSTITUTION_COMMAND = ( + Path(__file__).parent.parent / "templates" / "commands" / "constitution.md" +) LEAN_COMMAND_NAMES = [ "speckit.specify", @@ -5391,6 +5394,31 @@ def test_disable_corrupted_registry_entry(self, project_dir, pack_dir): ] +@pytest.mark.parametrize( + "command_path", + [ + CORE_CONSTITUTION_COMMAND, + LEAN_PRESET_DIR / "commands" / "speckit.constitution.md", + ], + ids=["core", "lean"], +) +def test_constitution_commands_guard_against_non_governance_work(command_path): + """Constitution commands defer non-governance work instead of executing it.""" + content = command_path.read_text() + lower_content = content.lower() + normalized_content = " ".join(lower_content.split()) + + assert "## Scope Guard" in content + assert "**MUST NOT**" in content + assert "Classify every part" in content + assert "application source files" in content + assert "non-governance intent" in content + assert "`Next Actions`" in content + assert "__SPECKIT_COMMAND_SPECIFY__" in content + assert "omit" in lower_content + assert "do not invoke it" in normalized_content or "without invoking it" in normalized_content + + class TestLeanPreset: """Tests for the lean preset that ships with the repo."""