You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
Copy file name to clipboardExpand all lines: templates/commands/analyze.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -19,7 +19,7 @@ You **MUST** consider the user input before proceeding (if not empty).
19
19
**Check for extension hooks (before analysis)**:
20
20
- Check if `.specify/extensions.yml` exists in the project root.
21
21
- 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 silentlyand 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
23
23
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
24
24
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
25
25
- 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
205
205
206
206
After reporting, check if `.specify/extensions.yml` exists in the project root.
207
207
- 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 silentlyand 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
209
209
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
210
210
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
211
211
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
Copy file name to clipboardExpand all lines: templates/commands/checklist.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -49,7 +49,7 @@ You **MUST** consider the user input before proceeding (if not empty).
49
49
**Check for extension hooks (before checklist generation)**:
50
50
- Check if `.specify/extensions.yml` exists in the project root.
51
51
- 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 silentlyand 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
53
53
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
54
54
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
55
55
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
@@ -350,7 +350,7 @@ Sample items:
350
350
**Check for extension hooks (after checklist generation)**:
351
351
Check if `.specify/extensions.yml` exists in the project root.
352
352
- 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 silentlyand 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
354
354
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
355
355
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
356
356
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
Copy file name to clipboardExpand all lines: templates/commands/clarify.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -23,7 +23,7 @@ You **MUST** consider the user input before proceeding (if not empty).
23
23
**Check for extension hooks (before clarification)**:
24
24
- Check if `.specify/extensions.yml` exists in the project root.
25
25
- 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 silentlyand 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
27
27
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
28
28
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
29
29
- 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}
245
245
Check if `.specify/extensions.yml` exists in the project root.
246
246
- If it does not exist, or no hooks are registered under `hooks.after_clarify`, skip to the Completion Report.
247
247
- 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 silentlyand 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.
249
249
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
250
250
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
251
251
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
Copy file name to clipboardExpand all lines: templates/commands/constitution.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -42,7 +42,7 @@ and commands read the constitution at runtime and are not modified here.
42
42
**Check for extension hooks (before constitution update)**:
43
43
- Check if `.specify/extensions.yml` exists in the project root.
44
44
- 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 silentlyand 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
46
46
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
47
47
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
48
48
- 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
150
150
**Check for extension hooks (after constitution update)**:
151
151
Check if `.specify/extensions.yml` exists in the project root.
152
152
- 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 silentlyand 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
154
154
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
155
155
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
156
156
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
Copy file name to clipboardExpand all lines: templates/commands/converge.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -20,7 +20,7 @@ You **MUST** consider the user input before proceeding (if not empty).
20
20
21
21
- Check if `.specify/extensions.yml` exists in the project root.
22
22
- 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 silentlyand 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
24
24
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
25
25
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
26
26
- 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:
238
238
After producing the result, check if `.specify/extensions.yml` exists in the project root.
239
239
240
240
- 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 silentlyand 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
242
242
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
243
243
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
244
244
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
Copy file name to clipboardExpand all lines: templates/commands/implement.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -19,7 +19,7 @@ You **MUST** consider the user input before proceeding (if not empty).
19
19
**Check for extension hooks (before implementation)**:
20
20
- Check if `.specify/extensions.yml` exists in the project root.
21
21
- 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 silentlyand 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
23
23
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
24
24
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
25
25
- 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
183
183
Check if `.specify/extensions.yml` exists in the project root.
184
184
- If it does not exist, or no hooks are registered under `hooks.after_implement`, skip to the Completion Report.
185
185
- 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 silentlyand 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.
187
187
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
188
188
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
189
189
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
Copy file name to clipboardExpand all lines: templates/commands/plan.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -27,7 +27,7 @@ You **MUST** consider the user input before proceeding (if not empty).
27
27
**Check for extension hooks (before planning)**:
28
28
- Check if `.specify/extensions.yml` exists in the project root.
29
29
- 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 silentlyand 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
31
31
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
32
32
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
33
33
- 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).
78
78
Check if `.specify/extensions.yml` exists in the project root.
79
79
- If it does not exist, or no hooks are registered under `hooks.after_plan`, skip to the Completion Report.
80
80
- 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 silentlyand 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.
82
82
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
83
83
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
84
84
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
Copy file name to clipboardExpand all lines: templates/commands/specify.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -23,7 +23,7 @@ You **MUST** consider the user input before proceeding (if not empty).
23
23
**Check for extension hooks (before specification)**:
24
24
- Check if `.specify/extensions.yml` exists in the project root.
25
25
- 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 silentlyand 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
27
27
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
28
28
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
29
29
- 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:
240
240
Check if `.specify/extensions.yml` exists in the project root.
241
241
- If it does not exist, or no hooks are registered under `hooks.after_specify`, skip to the Completion Report.
242
242
- 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 silentlyand 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.
244
244
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
245
245
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
246
246
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
Copy file name to clipboardExpand all lines: templates/commands/tasks.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -28,7 +28,7 @@ You **MUST** consider the user input before proceeding (if not empty).
28
28
**Check for extension hooks (before tasks generation)**:
29
29
- Check if `.specify/extensions.yml` exists in the project root.
30
30
- 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 silentlyand 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
32
32
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
33
33
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
34
34
- 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).
99
99
Check if `.specify/extensions.yml` exists in the project root.
100
100
- If it does not exist, or no hooks are registered under `hooks.after_tasks`, skip to the Completion Report.
101
101
- 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 silentlyand 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.
103
103
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
104
104
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
105
105
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
Copy file name to clipboardExpand all lines: templates/commands/taskstoissues.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -20,7 +20,7 @@ You **MUST** consider the user input before proceeding (if not empty).
20
20
**Check for extension hooks (before tasks-to-issues conversion)**:
21
21
- Check if `.specify/extensions.yml` exists in the project root.
22
22
- 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 silentlyand 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
24
24
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
25
25
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
26
26
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
**Check for extension hooks (after tasks-to-issues conversion)**:
78
78
Check if `.specify/extensions.yml` exists in the project root.
79
79
- 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 silentlyand 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
81
81
- Filter out hooks where `enabled` is explicitly `false`. Treat hooks without an `enabled` field as enabled by default.
82
82
- For each remaining hook, do **not** attempt to interpret or evaluate hook `condition` expressions:
83
83
- If the hook has no `condition` field, or it is null/empty, treat the hook as executable
0 commit comments