From 82e19e4bba848b1ab28ad05e4c578a80dbabaee4 Mon Sep 17 00:00:00 2001 From: ayeshakhalid192007-dev Date: Sat, 18 Apr 2026 00:37:16 +0500 Subject: [PATCH 1/6] 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/6] 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/6] 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/6] 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. From 498251fe34351ac9a129676763e0a13c6a287136 Mon Sep 17 00:00:00 2001 From: ayeshakhalid192007-dev Date: Tue, 21 Apr 2026 19:25:54 +0500 Subject: [PATCH 5/6] =?UTF-8?q?fix(tasks,implement):=20address=20review=20?= =?UTF-8?q?=E2=80=94=20remove=20duplicate=20format=20note=20and=20fix=20co?= =?UTF-8?q?ntext-loss=20gap?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove governed-operation hint from tasks-template.md Format Legend; the generation rule in tasks.md is the single authoritative source, so the legend entry was redundant duplication. Add Task-level re-read instruction to implement.md step 6: before executing each task the agent re-reads that task's entry from tasks.md on disk. This closes the gap the reviewer identified — the initial tasks.md load can be compacted out of context during a long session, but a per-task re-read ensures embedded governed commands are always in active context at execution time. --- templates/commands/implement.md | 3 ++- templates/tasks-template.md | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/commands/implement.md b/templates/commands/implement.md index 9a91d2dc4b..fd85c9dd8e 100644 --- a/templates/commands/implement.md +++ b/templates/commands/implement.md @@ -142,10 +142,11 @@ You **MUST** consider the user input before proceeding (if not empty). 6. Execute implementation following the task plan: - **Phase-by-phase execution**: Complete each phase before moving to the next - - **Respect dependencies**: Run sequential tasks in order, parallel tasks [P] can run together + - **Respect dependencies**: Run sequential tasks in order, parallel tasks [P] can run together - **Follow TDD approach**: Execute test tasks before their corresponding implementation tasks - **File-based coordination**: Tasks affecting the same files must run sequentially - **Validation checkpoints**: Verify each phase completion before proceeding + - **Task-level re-read**: Before executing each individual task, re-read that task's entry from tasks.md to get its complete body. This ensures any embedded commands (e.g., governed operations with exact flags and env vars) are in active context even if the initial tasks.md load was compressed out during a long session. 7. Implementation execution rules: - **Setup first**: Initialize project structure, dependencies, configuration diff --git a/templates/tasks-template.md b/templates/tasks-template.md index b1c25763eb..60f9be455d 100644 --- a/templates/tasks-template.md +++ b/templates/tasks-template.md @@ -17,7 +17,6 @@ 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 786ed210944543c85c47d8a67e5bfd54b5ad3808 Mon Sep 17 00:00:00 2001 From: ayeshakhalid192007-dev Date: Tue, 21 Apr 2026 22:12:43 +0500 Subject: [PATCH 6/6] fix(tasks): address Copilot review feedback - Fix escaped-backtick rendering bug in governed example (tasks.md:175): use double-backtick outer span so inner backtick displays correctly - Add missing info strings (text) to four bare fenced code blocks in implement.md (MD040 compliance) - pre-hook and post-hook sections --- templates/commands/implement.md | 8 ++++---- templates/commands/tasks.md | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/templates/commands/implement.md b/templates/commands/implement.md index fd85c9dd8e..f7ae82d117 100644 --- a/templates/commands/implement.md +++ b/templates/commands/implement.md @@ -25,7 +25,7 @@ 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,7 +36,7 @@ You **MUST** consider the user input before proceeding (if not empty). To execute: `/{command}` ``` - **Mandatory hook** (`optional: false`): - ``` + ```text ## Extension Hooks **Automatic Pre-Hook**: {extension} @@ -181,7 +181,7 @@ Note: This command assumes a complete task breakdown exists in tasks.md. If task - 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} @@ -192,7 +192,7 @@ Note: This command assumes a complete task breakdown exists in tasks.md. If task To execute: `/{command}` ``` - **Mandatory hook** (`optional: false`): - ``` + ```text ## Extension Hooks **Automatic Hook**: {extension} diff --git a/templates/commands/tasks.md b/templates/commands/tasks.md index 3dfd096a02..8bac96e85f 100644 --- a/templates/commands/tasks.md +++ b/templates/commands/tasks.md @@ -172,7 +172,7 @@ 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\`` +- ✅ 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)