From 7a7843b68b7597ba16beb0622752b5e555d199c6 Mon Sep 17 00:00:00 2001 From: Manfred Riem <15701806+mnriem@users.noreply.github.com> Date: Tue, 26 May 2026 16:57:03 -0500 Subject: [PATCH] fix: promote post-execution hook dispatch to H2 with directive language (#2713) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: promote post-execution hook dispatch to H2 with directive language Restructure the after_* hook dispatch in all five core command templates (specify, clarify, implement, plan, tasks) to make hook execution structurally unmissable by LLMs during interactive slash-command flows. Changes applied consistently across all five templates: 1. Promote hook block from a final numbered step to a top-level '## Mandatory Post-Execution Hooks' H2, placed before the completion report. An H2 boundary is salient in a way that a numbered sub-step buried after 'Report completion' is not. 2. Use directive language for mandatory hooks: 'You MUST emit EXECUTE_COMMAND: for each mandatory hook' and 'You MUST complete this section before reporting completion to the user.' The previous conditional framing ('check if', 'based on its optional flag') buried the mandatory cases. 3. List mandatory hooks before optional hooks in the dispatch block, so the required action appears first. 4. Add a terminal '## Done When' verification checklist at the end of each template, including 'Extension hooks dispatched' as an explicit completion criterion. This gives the model a structured opportunity to verify completion before exiting. 5. Extract the completion report into its own '## Completion Report' H2 section, clearly separated from the hook dispatch. These changes preserve the interactive-vs-workflow distinction and do not introduce auto-run. They raise the reliability of the existing best-effort dispatch mechanism. Fixes #2688 * fix: address review — narrow Done When wording and move to end of templates - Narrow the hooks checklist item from a broad condition to 'dispatched or skipped according to the rules in Mandatory Post-Execution Hooks above' so it does not contradict the filtering rules for disabled hooks or hooks with non-empty conditions. - Move the Done When section to the actual end of specify.md, plan.md, and tasks.md so it does not signal premature completion before the agent reads required guidance sections (Quick Guidelines, Phases/Key rules, Task Generation Rules). * fix: update stale step 8 reference in specify.md validation flow The old 'proceed to step 8' pointed to the completion report step that was renumbered when hook dispatch was promoted to its own H2 section. Update the reference to point to 'Mandatory Post-Execution Hooks'. --- templates/commands/clarify.md | 49 ++++++++++++--------- templates/commands/implement.md | 66 +++++++++++++++++----------- templates/commands/plan.md | 64 ++++++++++++++++----------- templates/commands/specify.md | 74 ++++++++++++++++++-------------- templates/commands/tasks.md | 76 +++++++++++++++++++-------------- 5 files changed, 194 insertions(+), 135 deletions(-) diff --git a/templates/commands/clarify.md b/templates/commands/clarify.md index 57cd01def..d4eb0048a 100644 --- a/templates/commands/clarify.md +++ b/templates/commands/clarify.md @@ -197,14 +197,6 @@ Execution steps: 7. Write the updated spec back to `FEATURE_SPEC`. -8. Report completion (after questioning loop ends or early termination): - - Number of questions asked & answered. - - Path to updated spec. - - Sections touched (list names). - - Coverage summary table listing each taxonomy category with Status: Resolved (was Partial/Missing and addressed), Deferred (exceeds question quota or better suited for planning), Clear (already sufficient), Outstanding (still Partial/Missing but low impact). - - If any Outstanding or Deferred remain, recommend whether to proceed to `__SPECKIT_COMMAND_PLAN__` or run `__SPECKIT_COMMAND_CLARIFY__` again later post-plan. - - Suggested next command. - Behavior rules: - If no meaningful ambiguities found (or all potential questions would be low-impact), respond: "No critical ambiguities detected worth formal clarification." and suggest proceeding. @@ -217,17 +209,27 @@ Behavior rules: Context for prioritization: {ARGS} -## Post-Execution Checks +## Mandatory Post-Execution Hooks + +**You MUST complete this section before reporting completion to the user.** -**Check for extension hooks (after clarification)**: Check if `.specify/extensions.yml` exists in the project root. -- If it exists, read it and look for entries under the `hooks.after_clarify` key -- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally +- If it does not exist, or no hooks are registered under `hooks.after_clarify`, skip to the Completion Report. +- If it exists, read it and look for entries under the `hooks.after_clarify` key. +- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue to the Completion Report. - Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default. - For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions: - If the hook has no `condition` field, or it is null/empty, treat the hook as executable - If the hook defines a non-empty `condition`, skip the hook and leave condition evaluation to the HookExecutor implementation - For each executable hook, output the following based on its `optional` flag: + - **Mandatory hook** (`optional: false`) — **You MUST emit `EXECUTE_COMMAND:` for each mandatory hook**: + ``` + ## Extension Hooks + + **Automatic Hook**: {extension} + Executing: `/{command}` + EXECUTE_COMMAND: {command} + ``` - **Optional hook** (`optional: true`): ``` ## Extension Hooks @@ -239,12 +241,19 @@ Check if `.specify/extensions.yml` exists in the project root. Prompt: {prompt} To execute: `/{command}` ``` - - **Mandatory hook** (`optional: false`): - ``` - ## Extension Hooks - **Automatic Hook**: {extension} - Executing: `/{command}` - EXECUTE_COMMAND: {command} - ``` -- If no hooks are registered or `.specify/extensions.yml` does not exist, skip silently +## Completion Report + +Report completion (after questioning loop ends or early termination): +- Number of questions asked & answered. +- Path to updated spec. +- Sections touched (list names). +- Coverage summary table listing each taxonomy category with Status: Resolved (was Partial/Missing and addressed), Deferred (exceeds question quota or better suited for planning), Clear (already sufficient), Outstanding (still Partial/Missing but low impact). +- If any Outstanding or Deferred remain, recommend whether to proceed to `__SPECKIT_COMMAND_PLAN__` or run `__SPECKIT_COMMAND_CLARIFY__` again later post-plan. +- Suggested next command. + +## Done When + +- [ ] Spec ambiguities identified and clarifications integrated into spec file +- [ ] Extension hooks dispatched or skipped according to the rules in Mandatory Post-Execution Hooks above +- [ ] Completion reported to user with questions answered, sections touched, and coverage summary diff --git a/templates/commands/implement.md b/templates/commands/implement.md index 52a042161..c416fa738 100644 --- a/templates/commands/implement.md +++ b/templates/commands/implement.md @@ -168,35 +168,49 @@ You **MUST** consider the user input before proceeding (if not empty). - Check that implemented features match the original specification - Validate that tests pass and coverage meets requirements - Confirm the implementation follows the technical plan - - Report final status with summary of completed work Note: This command assumes a complete task breakdown exists in tasks.md. If tasks are incomplete or missing, suggest running `__SPECKIT_COMMAND_TASKS__` first to regenerate the task list. -10. **Check for extension hooks**: After completion validation, check if `.specify/extensions.yml` exists in the project root. - - If it exists, read it and look for entries under the `hooks.after_implement` key - - If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally - - Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default. - - For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions: - - If the hook has no `condition` field, or it is null/empty, treat the hook as executable - - If the hook defines a non-empty `condition`, skip the hook and leave condition evaluation to the HookExecutor implementation - - For each executable hook, output the following based on its `optional` flag: - - **Optional hook** (`optional: true`): - ``` - ## Extension Hooks +## Mandatory Post-Execution Hooks - **Optional Hook**: {extension} - Command: `/{command}` - Description: {description} +**You MUST complete this section before reporting completion to the user.** - Prompt: {prompt} - To execute: `/{command}` - ``` - - **Mandatory hook** (`optional: false`): - ``` - ## Extension Hooks +Check if `.specify/extensions.yml` exists in the project root. +- If it does not exist, or no hooks are registered under `hooks.after_implement`, skip to the Completion Report. +- If it exists, read it and look for entries under the `hooks.after_implement` key. +- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue to the Completion Report. +- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default. +- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions: + - If the hook has no `condition` field, or it is null/empty, treat the hook as executable + - If the hook defines a non-empty `condition`, skip the hook and leave condition evaluation to the HookExecutor implementation +- For each executable hook, output the following based on its `optional` flag: + - **Mandatory hook** (`optional: false`) — **You MUST emit `EXECUTE_COMMAND:` for each mandatory hook**: + ``` + ## Extension Hooks - **Automatic Hook**: {extension} - Executing: `/{command}` - EXECUTE_COMMAND: {command} - ``` - - If no hooks are registered or `.specify/extensions.yml` does not exist, skip silently + **Automatic Hook**: {extension} + Executing: `/{command}` + EXECUTE_COMMAND: {command} + ``` + - **Optional hook** (`optional: true`): + ``` + ## Extension Hooks + + **Optional Hook**: {extension} + Command: `/{command}` + Description: {description} + + Prompt: {prompt} + To execute: `/{command}` + ``` + +## Completion Report + +Report final status with summary of completed work. + +## Done When + +- [ ] All tasks in tasks.md completed and marked `[X]` +- [ ] Implementation validated against specification, plan, and test coverage +- [ ] Extension hooks dispatched or skipped according to the rules in Mandatory Post-Execution Hooks above +- [ ] Completion reported to user with summary of completed work diff --git a/templates/commands/plan.md b/templates/commands/plan.md index 04db94ffa..b44854f45 100644 --- a/templates/commands/plan.md +++ b/templates/commands/plan.md @@ -70,36 +70,42 @@ You **MUST** consider the user input before proceeding (if not empty). - Phase 1: Update agent context by running the agent script - Re-evaluate Constitution Check post-design -4. **Stop and report**: Command ends after Phase 2 planning. Report branch, IMPL_PLAN path, and generated artifacts. +## Mandatory Post-Execution Hooks -5. **Check for extension hooks**: After reporting, check if `.specify/extensions.yml` exists in the project root. - - If it exists, read it and look for entries under the `hooks.after_plan` key - - If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally - - Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default. - - For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions: - - If the hook has no `condition` field, or it is null/empty, treat the hook as executable - - If the hook defines a non-empty `condition`, skip the hook and leave condition evaluation to the HookExecutor implementation - - For each executable hook, output the following based on its `optional` flag: - - **Optional hook** (`optional: true`): - ``` - ## Extension Hooks +**You MUST complete this section before reporting completion to the user.** - **Optional Hook**: {extension} - Command: `/{command}` - Description: {description} +Check if `.specify/extensions.yml` exists in the project root. +- If it does not exist, or no hooks are registered under `hooks.after_plan`, skip to the Completion Report. +- If it exists, read it and look for entries under the `hooks.after_plan` key. +- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue to the Completion Report. +- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default. +- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions: + - If the hook has no `condition` field, or it is null/empty, treat the hook as executable + - If the hook defines a non-empty `condition`, skip the hook and leave condition evaluation to the HookExecutor implementation +- For each executable hook, output the following based on its `optional` flag: + - **Mandatory hook** (`optional: false`) — **You MUST emit `EXECUTE_COMMAND:` for each mandatory hook**: + ``` + ## Extension Hooks - Prompt: {prompt} - To execute: `/{command}` - ``` - - **Mandatory hook** (`optional: false`): - ``` - ## Extension Hooks + **Automatic Hook**: {extension} + Executing: `/{command}` + EXECUTE_COMMAND: {command} + ``` + - **Optional hook** (`optional: true`): + ``` + ## Extension Hooks - **Automatic Hook**: {extension} - Executing: `/{command}` - EXECUTE_COMMAND: {command} - ``` - - If no hooks are registered or `.specify/extensions.yml` does not exist, skip silently + **Optional Hook**: {extension} + Command: `/{command}` + Description: {description} + + Prompt: {prompt} + To execute: `/{command}` + ``` + +## Completion Report + +Command ends after Phase 2 planning. Report branch, IMPL_PLAN path, and generated artifacts. ## Phases @@ -150,3 +156,9 @@ You **MUST** consider the user input before proceeding (if not empty). - Use absolute paths for filesystem operations; use project-relative paths for references in documentation and agent context files - ERROR on gate failures or unresolved clarifications + +## Done When + +- [ ] Plan workflow executed and design artifacts generated +- [ ] Extension hooks dispatched or skipped according to the rules in Mandatory Post-Execution Hooks above +- [ ] Completion reported to user with branch, plan path, and generated artifacts diff --git a/templates/commands/specify.md b/templates/commands/specify.md index cafa32f4e..76c548676 100644 --- a/templates/commands/specify.md +++ b/templates/commands/specify.md @@ -183,7 +183,7 @@ Given that feature description, do this: c. **Handle Validation Results**: - - **If all items pass**: Mark checklist complete and proceed to step 8 + - **If all items pass**: Mark checklist complete and proceed to the Mandatory Post-Execution Hooks section - **If items fail (excluding [NEEDS CLARIFICATION])**: 1. List the failing items and specific issues @@ -228,40 +228,46 @@ Given that feature description, do this: d. **Update Checklist**: After each validation iteration, update the checklist file with current pass/fail status -8. **Report completion** to the user with: - - `SPECIFY_FEATURE_DIRECTORY` — the feature directory path - - `SPEC_FILE` — the spec file path - - Checklist results summary - - Readiness for the next phase (`__SPECKIT_COMMAND_CLARIFY__` or `__SPECKIT_COMMAND_PLAN__`) +## Mandatory Post-Execution Hooks -9. **Check for extension hooks**: After reporting completion, check if `.specify/extensions.yml` exists in the project root. - - If it exists, read it and look for entries under the `hooks.after_specify` key - - If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally - - Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default. - - For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions: - - If the hook has no `condition` field, or it is null/empty, treat the hook as executable - - If the hook defines a non-empty `condition`, skip the hook and leave condition evaluation to the HookExecutor implementation - - For each executable hook, output the following based on its `optional` flag: - - **Optional hook** (`optional: true`): - ``` - ## Extension Hooks +**You MUST complete this section before reporting completion to the user.** - **Optional Hook**: {extension} - Command: `/{command}` - Description: {description} +Check if `.specify/extensions.yml` exists in the project root. +- If it does not exist, or no hooks are registered under `hooks.after_specify`, skip to the Completion Report. +- If it exists, read it and look for entries under the `hooks.after_specify` key. +- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue to the Completion Report. +- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default. +- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions: + - If the hook has no `condition` field, or it is null/empty, treat the hook as executable + - If the hook defines a non-empty `condition`, skip the hook and leave condition evaluation to the HookExecutor implementation +- For each executable hook, output the following based on its `optional` flag: + - **Mandatory hook** (`optional: false`) — **You MUST emit `EXECUTE_COMMAND:` for each mandatory hook**: + ``` + ## Extension Hooks - Prompt: {prompt} - To execute: `/{command}` - ``` - - **Mandatory hook** (`optional: false`): - ``` - ## Extension Hooks + **Automatic Hook**: {extension} + Executing: `/{command}` + EXECUTE_COMMAND: {command} + ``` + - **Optional hook** (`optional: true`): + ``` + ## Extension Hooks - **Automatic Hook**: {extension} - Executing: `/{command}` - EXECUTE_COMMAND: {command} - ``` - - If no hooks are registered or `.specify/extensions.yml` does not exist, skip silently + **Optional Hook**: {extension} + Command: `/{command}` + Description: {description} + + Prompt: {prompt} + To execute: `/{command}` + ``` + +## Completion Report + +Report completion to the user with: +- `SPECIFY_FEATURE_DIRECTORY` — the feature directory path +- `SPEC_FILE` — the spec file path +- Checklist results summary +- Readiness for the next phase (`__SPECKIT_COMMAND_CLARIFY__` or `__SPECKIT_COMMAND_PLAN__`) **NOTE:** Branch creation is handled by the `before_specify` hook (git extension). Spec directory and file creation are always handled by this core command. @@ -325,3 +331,9 @@ Success criteria must be: - "Database can handle 1000 TPS" (implementation detail, use user-facing metric) - "React components render efficiently" (framework-specific) - "Redis cache hit rate above 80%" (technology-specific) + +## Done When + +- [ ] Specification written to `SPEC_FILE` and validated against quality checklist +- [ ] Extension hooks dispatched or skipped according to the rules in Mandatory Post-Execution Hooks above +- [ ] Completion reported to user with feature directory, spec file path, and checklist results diff --git a/templates/commands/tasks.md b/templates/commands/tasks.md index e5af6793b..43ff37d76 100644 --- a/templates/commands/tasks.md +++ b/templates/commands/tasks.md @@ -89,42 +89,48 @@ You **MUST** consider the user input before proceeding (if not empty). - Parallel execution examples per story - Implementation strategy section (MVP first, incremental delivery) -5. **Report**: Output path to generated tasks.md and summary: - - Total task count - - Task count per user story - - Parallel opportunities identified - - Independent test criteria for each story - - Suggested MVP scope (typically just User Story 1) - - Format validation: Confirm ALL tasks follow the checklist format (checkbox, ID, labels, file paths) +## Mandatory Post-Execution Hooks -6. **Check for extension hooks**: After tasks.md is generated, check if `.specify/extensions.yml` exists in the project root. - - If it exists, read it and look for entries under the `hooks.after_tasks` key - - If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally - - Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default. - - For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions: - - If the hook has no `condition` field, or it is null/empty, treat the hook as executable - - If the hook defines a non-empty `condition`, skip the hook and leave condition evaluation to the HookExecutor implementation - - For each executable hook, output the following based on its `optional` flag: - - **Optional hook** (`optional: true`): - ``` - ## Extension Hooks +**You MUST complete this section before reporting completion to the user.** - **Optional Hook**: {extension} - Command: `/{command}` - Description: {description} +Check if `.specify/extensions.yml` exists in the project root. +- If it does not exist, or no hooks are registered under `hooks.after_tasks`, skip to the Completion Report. +- If it exists, read it and look for entries under the `hooks.after_tasks` key. +- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue to the Completion Report. +- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default. +- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions: + - If the hook has no `condition` field, or it is null/empty, treat the hook as executable + - If the hook defines a non-empty `condition`, skip the hook and leave condition evaluation to the HookExecutor implementation +- For each executable hook, output the following based on its `optional` flag: + - **Mandatory hook** (`optional: false`) — **You MUST emit `EXECUTE_COMMAND:` for each mandatory hook**: + ``` + ## Extension Hooks - Prompt: {prompt} - To execute: `/{command}` - ``` - - **Mandatory hook** (`optional: false`): - ``` - ## Extension Hooks + **Automatic Hook**: {extension} + Executing: `/{command}` + EXECUTE_COMMAND: {command} + ``` + - **Optional hook** (`optional: true`): + ``` + ## Extension Hooks - **Automatic Hook**: {extension} - Executing: `/{command}` - EXECUTE_COMMAND: {command} - ``` - - If no hooks are registered or `.specify/extensions.yml` does not exist, skip silently + **Optional Hook**: {extension} + Command: `/{command}` + Description: {description} + + Prompt: {prompt} + To execute: `/{command}` + ``` + +## Completion Report + +Output path to generated tasks.md and summary: +- Total task count +- Task count per user story +- Parallel opportunities identified +- Independent test criteria for each story +- Suggested MVP scope (typically just User Story 1) +- Format validation: Confirm ALL tasks follow the checklist format (checkbox, ID, labels, file paths) Context for task generation: {ARGS} @@ -201,3 +207,9 @@ Every task MUST strictly follow this format: - Within each story: Tests (if requested) → Models → Services → Endpoints → Integration - Each phase should be a complete, independently testable increment - **Final Phase**: Polish & Cross-Cutting Concerns + +## Done When + +- [ ] tasks.md generated with all phases, task IDs, and file paths +- [ ] Extension hooks dispatched or skipped according to the rules in Mandatory Post-Execution Hooks above +- [ ] Completion reported to user with task count, story breakdown, and MVP scope