Skip to content
3 changes: 2 additions & 1 deletion templates/commands/implement.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Comment thread
mnriem marked this conversation as resolved.

7. Implementation execution rules:
- **Setup first**: Initialize project structure, dependencies, configuration
Expand Down
40 changes: 35 additions & 5 deletions templates/commands/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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}
Expand All @@ -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
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you adding this in multiple places?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — removed the duplicate from tasks-template.md Format Legend (commit 498251f). The generation rule in tasks.md is the single authoritative source; the legend entry was redundant.

## 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
Expand Down Expand Up @@ -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}
Expand All @@ -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}
Expand Down Expand Up @@ -163,10 +172,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\``
Comment thread
mnriem marked this conversation as resolved.
Outdated
- ❌ 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: `<exact-command --flag $ENV_VAR>`
Comment on lines +186 to +189
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new governed-operations guidance conflicts with the earlier "Description with file path" requirement: both the governed example and the governed format snippet omit a file path entirely. Either update the governed examples/snippet to include a file path (while still embedding the exact executable command), or explicitly document that certain governed-operation tasks are exempt from the file-path requirement so agents don’t get contradictory instructions.

Suggested change
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: `<exact-command --flag $ENV_VAR>`
When a task covers a governed operation, it MUST still include the required file path from the standard task format, and 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?] path/to/file.ext Description: `<exact-command --flag $ENV_VAR>`

Copilot uses AI. Check for mistakes.
```
Comment thread
mnriem marked this conversation as resolved.

**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

Expand Down
20 changes: 15 additions & 5 deletions templates/commands/taskstoissues.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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}
Expand All @@ -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}
Expand All @@ -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
Expand All @@ -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.
Expand All @@ -78,7 +84,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}
Expand All @@ -88,12 +95,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
2 changes: 1 addition & 1 deletion tests/hooks/TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down
2 changes: 1 addition & 1 deletion workflows/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ When `specify workflow add <id>` installs from catalog, it downloads the workflo

## Module Structure

```
```text
src/specify_cli/
├── workflows/
│ ├── __init__.py # STEP_REGISTRY + _register_builtin_steps()
Expand Down
2 changes: 1 addition & 1 deletion workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ specify workflow catalog remove <index>

## Repository Layout

```
```text
workflows/
├── ARCHITECTURE.md # Internal architecture documentation
├── PUBLISHING.md # Guide for submitting workflows to the catalog
Expand Down
Loading