From 82e19e4bba848b1ab28ad05e4c578a80dbabaee4 Mon Sep 17 00:00:00 2001 From: ayeshakhalid192007-dev Date: Sat, 18 Apr 2026 00:37:16 +0500 Subject: [PATCH 1/4] fix(tasks): require executable commands for governed operations in task bodies --- templates/commands/tasks.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/templates/commands/tasks.md b/templates/commands/tasks.md index 4e204abc1b..5555f73769 100644 --- a/templates/commands/tasks.md +++ b/templates/commands/tasks.md @@ -163,10 +163,31 @@ Every task MUST strictly follow this format: - ✅ CORRECT: `- [ ] T005 [P] Implement authentication middleware in src/middleware/auth.py` - ✅ CORRECT: `- [ ] T012 [P] [US1] Create User model in src/models/user.py` - ✅ CORRECT: `- [ ] T014 [US1] Implement UserService in src/services/user_service.py` +- ✅ CORRECT (governed): `- [ ] T008 Push packages to NuGet feed: \`dotnet nuget push "*.nupkg" --api-key $NUGET_API_KEY --source $NUGET_FEED_URL\`` - ❌ WRONG: `- [ ] Create User model` (missing ID and Story label) - ❌ WRONG: `T001 [US1] Create model` (missing checkbox) - ❌ WRONG: `- [ ] [US1] Create User model` (missing Task ID) - ❌ WRONG: `- [ ] T001 [US1] Create model` (missing file path) +- ❌ WRONG (governed): `- [ ] T008 Push packages to NuGet feed` (prose only — omits executable command for a governed operation) + +### Governed Operations (REQUIRED) + +A **governed operation** is any operation the project constitution defines with specific command syntax, required flags, or environment variable references (for example: a package publish step, a deployment command, a signed git tag). + +When a task covers a governed operation, its description MUST include the exact executable command from the constitution, parameterized with environment variable names rather than literal credential values. Do not paraphrase the command. + +```text +- [ ] [TaskID] [P?] [Story?] Description: `` +``` + +**Why this matters**: Tasks are surfaced in agent context every session. The constitution may be compacted out of active context by the time a task is executed — especially across session boundaries or late in long sessions. Embedding the exact command in the task body ensures the agent executes the correct syntax without re-reading the constitution. + +**How to identify governed operations while generating tasks**: + +- The constitution is in active context during `/speckit.tasks` execution — read it before generating tasks +- Any operation the constitution names with a specific tool, required flags, or env var references is governed +- If the constitution says "do not use X, use Y" (e.g., use the feed URL env var, not the config source name), the task body must use Y +- If a governed operation has multiple steps (e.g., pack then push), each step is its own task with its own command ### Task Organization From f677974f0c9ccca569dbacf4e5b08da70d1a72da Mon Sep 17 00:00:00 2001 From: ayeshakhalid192007-dev Date: Sat, 18 Apr 2026 00:37:44 +0500 Subject: [PATCH 2/4] docs(tasks-template): show governed-command format in Format legend --- templates/tasks-template.md | 1 + 1 file changed, 1 insertion(+) diff --git a/templates/tasks-template.md b/templates/tasks-template.md index 60f9be455d..b1c25763eb 100644 --- a/templates/tasks-template.md +++ b/templates/tasks-template.md @@ -17,6 +17,7 @@ description: "Task list template for feature implementation" - **[P]**: Can run in parallel (different files, no dependencies) - **[Story]**: Which user story this task belongs to (e.g., US1, US2, US3) - Include exact file paths in descriptions +- For **governed operations** (commands the constitution defines with specific syntax): append the exact command — e.g., `Push packages: \`dotnet nuget push "*.nupkg" --api-key $KEY --source $URL\`` ## Path Conventions From eecdec1037249cdb9f692e54e18b91b42a9c24da Mon Sep 17 00:00:00 2001 From: ayeshakhalid192007-dev Date: Sat, 18 Apr 2026 01:06:57 +0500 Subject: [PATCH 3/4] fix(lint): fix pre-existing markdownlint errors in hook blocks and code fences --- templates/commands/tasks.md | 19 ++++++++++++++----- templates/commands/taskstoissues.md | 8 ++++++-- tests/hooks/TESTING.md | 2 +- workflows/ARCHITECTURE.md | 2 +- workflows/README.md | 2 +- 5 files changed, 23 insertions(+), 10 deletions(-) diff --git a/templates/commands/tasks.md b/templates/commands/tasks.md index 5555f73769..3dfd096a02 100644 --- a/templates/commands/tasks.md +++ b/templates/commands/tasks.md @@ -25,6 +25,7 @@ You **MUST** consider the user input before proceeding (if not empty). ## Pre-Execution Checks **Check for extension hooks (before tasks generation)**: + - Check if `.specify/extensions.yml` exists in the project root. - If it exists, read it and look for entries under the `hooks.before_tasks` key - If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally @@ -34,7 +35,8 @@ You **MUST** consider the user input before proceeding (if not empty). - 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`): - ``` + + ```text ## Extension Hooks **Optional Pre-Hook**: {extension} @@ -44,16 +46,19 @@ You **MUST** consider the user input before proceeding (if not empty). Prompt: {prompt} To execute: `/{command}` ``` + - **Mandatory hook** (`optional: false`): - ``` + + ```text ## Extension Hooks **Automatic Pre-Hook**: {extension} Executing: `/{command}` EXECUTE_COMMAND: {command} - + Wait for the result of the hook command before proceeding to the Outline. ``` + - If no hooks are registered or `.specify/extensions.yml` does not exist, skip silently ## Outline @@ -106,7 +111,8 @@ You **MUST** consider the user input before proceeding (if not empty). - 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`): - ``` + + ```text ## Extension Hooks **Optional Hook**: {extension} @@ -116,14 +122,17 @@ You **MUST** consider the user input before proceeding (if not empty). Prompt: {prompt} To execute: `/{command}` ``` + - **Mandatory hook** (`optional: false`): - ``` + + ```text ## Extension Hooks **Automatic Hook**: {extension} Executing: `/{command}` EXECUTE_COMMAND: {command} ``` + - If no hooks are registered or `.specify/extensions.yml` does not exist, skip silently Context for task generation: {ARGS} diff --git a/templates/commands/taskstoissues.md b/templates/commands/taskstoissues.md index 77db7be130..a195f2ae3d 100644 --- a/templates/commands/taskstoissues.md +++ b/templates/commands/taskstoissues.md @@ -78,7 +78,8 @@ Check if `.specify/extensions.yml` exists in the project root. - 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`): - ``` + + ```text ## Extension Hooks **Optional Hook**: {extension} @@ -88,12 +89,15 @@ Check if `.specify/extensions.yml` exists in the project root. Prompt: {prompt} To execute: `/{command}` ``` + - **Mandatory hook** (`optional: false`): - ``` + + ```text ## Extension Hooks **Automatic Hook**: {extension} Executing: `/{command}` EXECUTE_COMMAND: {command} ``` + - If no hooks are registered or `.specify/extensions.yml` does not exist, skip silently diff --git a/tests/hooks/TESTING.md b/tests/hooks/TESTING.md index 6ab704442f..28471ffb2f 100644 --- a/tests/hooks/TESTING.md +++ b/tests/hooks/TESTING.md @@ -7,7 +7,7 @@ This directory contains a mock project to verify that LLM agents correctly ident 1. Open a chat with an LLM (like GitHub Copilot) in this project. 2. Ask it to generate tasks for the current directory: > "Please follow `/speckit.tasks` for the `./tests/hooks` directory." -3. **Expected Behavior**: +3. **Expected Behavior**: - Before doing any generation, the LLM should notice the `AUTOMATIC Pre-Hook` in `.specify/extensions.yml` under `before_tasks`. - It should state it is executing `EXECUTE_COMMAND: pre_tasks_test`. - It should then proceed to read the `.md` docs and produce a `tasks.md`. diff --git a/workflows/ARCHITECTURE.md b/workflows/ARCHITECTURE.md index 892333473c..e2df8a59bd 100644 --- a/workflows/ARCHITECTURE.md +++ b/workflows/ARCHITECTURE.md @@ -186,7 +186,7 @@ When `specify workflow add ` installs from catalog, it downloads the workflo ## Module Structure -``` +```text src/specify_cli/ ├── workflows/ │ ├── __init__.py # STEP_REGISTRY + _register_builtin_steps() diff --git a/workflows/README.md b/workflows/README.md index 31f736ff76..05fe9ff472 100644 --- a/workflows/README.md +++ b/workflows/README.md @@ -327,7 +327,7 @@ specify workflow catalog remove ## Repository Layout -``` +```text workflows/ ├── ARCHITECTURE.md # Internal architecture documentation ├── PUBLISHING.md # Guide for submitting workflows to the catalog From b957ccce39190e7a1560e0dfd3b6a7ffdb3da88f Mon Sep 17 00:00:00 2001 From: ayeshakhalid192007-dev Date: Sat, 18 Apr 2026 01:11:12 +0500 Subject: [PATCH 4/4] fix(lint): fix remaining hook-block lint errors in taskstoissues.md --- templates/commands/taskstoissues.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/templates/commands/taskstoissues.md b/templates/commands/taskstoissues.md index a195f2ae3d..78618b07f2 100644 --- a/templates/commands/taskstoissues.md +++ b/templates/commands/taskstoissues.md @@ -17,6 +17,7 @@ You **MUST** consider the user input before proceeding (if not empty). ## Pre-Execution Checks **Check for extension hooks (before tasks-to-issues conversion)**: + - Check if `.specify/extensions.yml` exists in the project root. - If it exists, read it and look for entries under the `hooks.before_taskstoissues` key - If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally @@ -26,7 +27,8 @@ You **MUST** consider the user input before proceeding (if not empty). - 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`): - ``` + + ```text ## Extension Hooks **Optional Pre-Hook**: {extension} @@ -36,8 +38,10 @@ You **MUST** consider the user input before proceeding (if not empty). Prompt: {prompt} To execute: `/{command}` ``` + - **Mandatory hook** (`optional: false`): - ``` + + ```text ## Extension Hooks **Automatic Pre-Hook**: {extension} @@ -46,6 +50,7 @@ You **MUST** consider the user input before proceeding (if not empty). Wait for the result of the hook command before proceeding to the Outline. ``` + - If no hooks are registered or `.specify/extensions.yml` does not exist, skip silently ## Outline @@ -69,7 +74,8 @@ git config --get remote.origin.url ## Post-Execution Checks **Check for extension hooks (after tasks-to-issues conversion)**: -Check if `.specify/extensions.yml` exists in the project root. + +- Check if `.specify/extensions.yml` exists in the project root. - If it exists, read it and look for entries under the `hooks.after_taskstoissues` 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.