Skip to content

Commit b600576

Browse files
authored
fix(templates): report an unreadable extensions.yml instead of skipping hooks silently (#4456)
* fix(templates): report an unreadable extensions.yml instead of skipping hooks silently All ten core command templates told the agent: "If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally". A `.specify/extensions.yml` with one malformed line was therefore treated exactly like a manifest with no hooks, and a mandatory hook (`optional: false`, as registered by the bundled git extension) was disabled without anything saying so. The agent now tells the user that the manifest could not be read (with the parser error) and that no hooks were checked, then continues as before. Control flow is unchanged; only the silence is removed. Adds tests/test_command_template_hooks.py, which pins the wording at both hook sites of every template that reads extensions.yml. * test(templates): pin every clause of the parse-failure instruction The regression test only pinned two of the five clauses the new instruction carries. Dropping the parser error, the mandatory-hook warning or the continuation clause from every template would have left it green. It now pins all five: the manifest could not be read, the parser error is included, no hooks were checked, mandatory (optional: false) hooks are named, and the command then continues. Checked by mutation: removing any one clause from all ten templates fails the ten parametrized cases.
1 parent 4dd2402 commit b600576

11 files changed

Lines changed: 100 additions & 20 deletions

File tree

templates/commands/analyze.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ You **MUST** consider the user input before proceeding (if not empty).
1919
**Check for extension hooks (before analysis)**:
2020
- Check if `.specify/extensions.yml` exists in the project root.
2121
- If it exists, read it and look for entries under the `hooks.before_analyze` key
22-
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally
22+
- If the YAML cannot be parsed or is invalid, do not skip silently: tell the user that `.specify/extensions.yml` could not be read (include the parser error) and that no hooks were checked, including any mandatory (`optional: false`) hooks registered there, then continue normally
2323
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
2424
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
2525
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
@@ -205,7 +205,7 @@ Ask the user: "Would you like me to suggest concrete remediation edits for the t
205205
206206
After reporting, check if `.specify/extensions.yml` exists in the project root.
207207
- If it exists, read it and look for entries under the `hooks.after_analyze` key
208-
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally
208+
- If the YAML cannot be parsed or is invalid, do not skip silently: tell the user that `.specify/extensions.yml` could not be read (include the parser error) and that no hooks were checked, including any mandatory (`optional: false`) hooks registered there, then continue normally
209209
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
210210
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
211211
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable

templates/commands/checklist.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ You **MUST** consider the user input before proceeding (if not empty).
4949
**Check for extension hooks (before checklist generation)**:
5050
- Check if `.specify/extensions.yml` exists in the project root.
5151
- If it exists, read it and look for entries under the `hooks.before_checklist` key
52-
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally
52+
- If the YAML cannot be parsed or is invalid, do not skip silently: tell the user that `.specify/extensions.yml` could not be read (include the parser error) and that no hooks were checked, including any mandatory (`optional: false`) hooks registered there, then continue normally
5353
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
5454
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
5555
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
@@ -350,7 +350,7 @@ Sample items:
350350
**Check for extension hooks (after checklist generation)**:
351351
Check if `.specify/extensions.yml` exists in the project root.
352352
- If it exists, read it and look for entries under the `hooks.after_checklist` key
353-
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally
353+
- If the YAML cannot be parsed or is invalid, do not skip silently: tell the user that `.specify/extensions.yml` could not be read (include the parser error) and that no hooks were checked, including any mandatory (`optional: false`) hooks registered there, then continue normally
354354
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
355355
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
356356
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable

templates/commands/clarify.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ You **MUST** consider the user input before proceeding (if not empty).
2323
**Check for extension hooks (before clarification)**:
2424
- Check if `.specify/extensions.yml` exists in the project root.
2525
- If it exists, read it and look for entries under the `hooks.before_clarify` key
26-
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally
26+
- If the YAML cannot be parsed or is invalid, do not skip silently: tell the user that `.specify/extensions.yml` could not be read (include the parser error) and that no hooks were checked, including any mandatory (`optional: false`) hooks registered there, then continue normally
2727
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
2828
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
2929
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
@@ -245,7 +245,7 @@ Context for prioritization: {ARGS}
245245
Check if `.specify/extensions.yml` exists in the project root.
246246
- If it does not exist, or no hooks are registered under `hooks.after_clarify`, skip to the Completion Report.
247247
- If it exists, read it and look for entries under the `hooks.after_clarify` key.
248-
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue to the Completion Report.
248+
- If the YAML cannot be parsed or is invalid, do not skip silently: tell the user that `.specify/extensions.yml` could not be read (include the parser error) and that no hooks were checked, including any mandatory (`optional: false`) hooks registered there, then continue to the Completion Report.
249249
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
250250
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
251251
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable

templates/commands/constitution.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ and commands read the constitution at runtime and are not modified here.
4242
**Check for extension hooks (before constitution update)**:
4343
- Check if `.specify/extensions.yml` exists in the project root.
4444
- If it exists, read it and look for entries under the `hooks.before_constitution` key
45-
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally
45+
- If the YAML cannot be parsed or is invalid, do not skip silently: tell the user that `.specify/extensions.yml` could not be read (include the parser error) and that no hooks were checked, including any mandatory (`optional: false`) hooks registered there, then continue normally
4646
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
4747
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
4848
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
@@ -150,7 +150,7 @@ Write only `.specify/memory/constitution.md`; do not create or modify template s
150150
**Check for extension hooks (after constitution update)**:
151151
Check if `.specify/extensions.yml` exists in the project root.
152152
- If it exists, read it and look for entries under the `hooks.after_constitution` key
153-
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally
153+
- If the YAML cannot be parsed or is invalid, do not skip silently: tell the user that `.specify/extensions.yml` could not be read (include the parser error) and that no hooks were checked, including any mandatory (`optional: false`) hooks registered there, then continue normally
154154
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
155155
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
156156
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable

templates/commands/converge.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ You **MUST** consider the user input before proceeding (if not empty).
2020

2121
- Check if `.specify/extensions.yml` exists in the project root.
2222
- If it exists, read it and look for entries under the `hooks.before_converge` key
23-
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally
23+
- If the YAML cannot be parsed or is invalid, do not skip silently: tell the user that `.specify/extensions.yml` could not be read (include the parser error) and that no hooks were checked, including any mandatory (`optional: false`) hooks registered there, then continue normally
2424
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
2525
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
2626
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
@@ -238,7 +238,7 @@ Append to the **end** of `tasks.md`, per the append contract:
238238
After producing the result, check if `.specify/extensions.yml` exists in the project root.
239239

240240
- If it exists, read it and look for entries under the `hooks.after_converge` key
241-
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally
241+
- If the YAML cannot be parsed or is invalid, do not skip silently: tell the user that `.specify/extensions.yml` could not be read (include the parser error) and that no hooks were checked, including any mandatory (`optional: false`) hooks registered there, then continue normally
242242
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
243243
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
244244
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable

templates/commands/implement.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ You **MUST** consider the user input before proceeding (if not empty).
1919
**Check for extension hooks (before implementation)**:
2020
- Check if `.specify/extensions.yml` exists in the project root.
2121
- If it exists, read it and look for entries under the `hooks.before_implement` key
22-
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally
22+
- If the YAML cannot be parsed or is invalid, do not skip silently: tell the user that `.specify/extensions.yml` could not be read (include the parser error) and that no hooks were checked, including any mandatory (`optional: false`) hooks registered there, then continue normally
2323
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
2424
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
2525
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
@@ -183,7 +183,7 @@ Note: This command assumes a complete task breakdown exists in tasks.md. If task
183183
Check if `.specify/extensions.yml` exists in the project root.
184184
- If it does not exist, or no hooks are registered under `hooks.after_implement`, skip to the Completion Report.
185185
- If it exists, read it and look for entries under the `hooks.after_implement` key.
186-
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue to the Completion Report.
186+
- If the YAML cannot be parsed or is invalid, do not skip silently: tell the user that `.specify/extensions.yml` could not be read (include the parser error) and that no hooks were checked, including any mandatory (`optional: false`) hooks registered there, then continue to the Completion Report.
187187
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
188188
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
189189
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable

templates/commands/plan.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ You **MUST** consider the user input before proceeding (if not empty).
2727
**Check for extension hooks (before planning)**:
2828
- Check if `.specify/extensions.yml` exists in the project root.
2929
- If it exists, read it and look for entries under the `hooks.before_plan` key
30-
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally
30+
- If the YAML cannot be parsed or is invalid, do not skip silently: tell the user that `.specify/extensions.yml` could not be read (include the parser error) and that no hooks were checked, including any mandatory (`optional: false`) hooks registered there, then continue normally
3131
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
3232
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
3333
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
@@ -78,7 +78,7 @@ You **MUST** consider the user input before proceeding (if not empty).
7878
Check if `.specify/extensions.yml` exists in the project root.
7979
- If it does not exist, or no hooks are registered under `hooks.after_plan`, skip to the Completion Report.
8080
- If it exists, read it and look for entries under the `hooks.after_plan` key.
81-
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue to the Completion Report.
81+
- If the YAML cannot be parsed or is invalid, do not skip silently: tell the user that `.specify/extensions.yml` could not be read (include the parser error) and that no hooks were checked, including any mandatory (`optional: false`) hooks registered there, then continue to the Completion Report.
8282
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
8383
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
8484
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable

templates/commands/specify.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ You **MUST** consider the user input before proceeding (if not empty).
2323
**Check for extension hooks (before specification)**:
2424
- Check if `.specify/extensions.yml` exists in the project root.
2525
- If it exists, read it and look for entries under the `hooks.before_specify` key
26-
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally
26+
- If the YAML cannot be parsed or is invalid, do not skip silently: tell the user that `.specify/extensions.yml` could not be read (include the parser error) and that no hooks were checked, including any mandatory (`optional: false`) hooks registered there, then continue normally
2727
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
2828
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
2929
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
@@ -240,7 +240,7 @@ Given that feature description, do this:
240240
Check if `.specify/extensions.yml` exists in the project root.
241241
- If it does not exist, or no hooks are registered under `hooks.after_specify`, skip to the Completion Report.
242242
- If it exists, read it and look for entries under the `hooks.after_specify` key.
243-
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue to the Completion Report.
243+
- If the YAML cannot be parsed or is invalid, do not skip silently: tell the user that `.specify/extensions.yml` could not be read (include the parser error) and that no hooks were checked, including any mandatory (`optional: false`) hooks registered there, then continue to the Completion Report.
244244
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
245245
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
246246
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable

templates/commands/tasks.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ You **MUST** consider the user input before proceeding (if not empty).
2828
**Check for extension hooks (before tasks generation)**:
2929
- Check if `.specify/extensions.yml` exists in the project root.
3030
- If it exists, read it and look for entries under the `hooks.before_tasks` key
31-
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally
31+
- If the YAML cannot be parsed or is invalid, do not skip silently: tell the user that `.specify/extensions.yml` could not be read (include the parser error) and that no hooks were checked, including any mandatory (`optional: false`) hooks registered there, then continue normally
3232
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
3333
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
3434
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
@@ -99,7 +99,7 @@ You **MUST** consider the user input before proceeding (if not empty).
9999
Check if `.specify/extensions.yml` exists in the project root.
100100
- If it does not exist, or no hooks are registered under `hooks.after_tasks`, skip to the Completion Report.
101101
- If it exists, read it and look for entries under the `hooks.after_tasks` key.
102-
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue to the Completion Report.
102+
- If the YAML cannot be parsed or is invalid, do not skip silently: tell the user that `.specify/extensions.yml` could not be read (include the parser error) and that no hooks were checked, including any mandatory (`optional: false`) hooks registered there, then continue to the Completion Report.
103103
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
104104
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
105105
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable

templates/commands/taskstoissues.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ You **MUST** consider the user input before proceeding (if not empty).
2020
**Check for extension hooks (before tasks-to-issues conversion)**:
2121
- Check if `.specify/extensions.yml` exists in the project root.
2222
- If it exists, read it and look for entries under the `hooks.before_taskstoissues` key
23-
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally
23+
- If the YAML cannot be parsed or is invalid, do not skip silently: tell the user that `.specify/extensions.yml` could not be read (include the parser error) and that no hooks were checked, including any mandatory (`optional: false`) hooks registered there, then continue normally
2424
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
2525
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
2626
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
@@ -77,7 +77,7 @@ git config --get remote.origin.url
7777
**Check for extension hooks (after tasks-to-issues conversion)**:
7878
Check if `.specify/extensions.yml` exists in the project root.
7979
- If it exists, read it and look for entries under the `hooks.after_taskstoissues` key
80-
- If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally
80+
- If the YAML cannot be parsed or is invalid, do not skip silently: tell the user that `.specify/extensions.yml` could not be read (include the parser error) and that no hooks were checked, including any mandatory (`optional: false`) hooks registered there, then continue normally
8181
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
8282
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
8383
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable

0 commit comments

Comments
 (0)