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
[Bug]: #3991 still reproduces on 0.16.2 — Claude ARGUMENT_HINTS fallback injects into a folded description scalar (wrap presets without explicit argument-hint) #4044
#3991 (preset wrap composition drops core argument-hint and leaks its value into description) was closed by #3996, but on 0.16.2 the corruption still reproduces for the configuration that motivated the original report: Claude integration + a strategy: wrap preset whose description is long enough for the YAML dumper to fold it across multiple lines.
Two facts combine:
The [bug-fix] Fix preset-wrap-drops-argument-hint: inherit argument-hint from core template #3996 inheritance fix is a no-op for bundled core commands.[bug-fix] Fix preset-wrap-drops-argument-hint: inherit argument-hint from core template #3996 adds argument-hint to the wrap-composition inheritance allowlist (copied from core_frontmatter when the preset frontmatter doesn't declare it). But the bundled core command templates (e.g. core_pack/commands/specify.md) declare no argument-hint frontmatter at all — for Claude the hints live in the hardcoded ARGUMENT_HINTS dict in src/specify_cli/integrations/claude/__init__.py. The docstring of agents.py::apply_argument_hint states this explicitly: "Built-in templates carry no argument-hint, so this is a no-op for the core path." So there is nothing to inherit, and the composed frontmatter reaches serialization without the key.
The Claude ARGUMENT_HINTS fallback then injects by raw string insertion into already-serialized YAML.ClaudeIntegration.post_process_skill_content → inject_argument_hint inserts argument-hint: "…" directly after the first line starting with description:. With a short one-line description this happens to work (which is presumably why [bug-fix] Fix preset-wrap-drops-argument-hint: inherit argument-hint from core template #3996's regression test passes). With a description long enough that dump_frontmatter folds it into a multi-line scalar, the injected line lands inside the folded scalar.
Depending on whether the dumper quoted the scalar, the failure takes one of two forms (both reproduced on 0.16.2):
Plain (unquoted) folded scalar → the frontmatter becomes invalid YAML (yaml.parser.ParserError: expected <block end>, but found '<scalar>').
Create a wrap preset whose command frontmatter declares only a longdescription (~150+ chars, so it folds when dumped) and strategy: wrap — no argument-hint:
presets-src/demo-wrap/commands/speckit.specify.md
---description: "Create or update the feature specification from a natural language feature description. Also accepts an issue URL resolved via gh CLI (demo customization)."strategy: wrap---<!-- demo preamble -->
{CORE_TEMPLATE}
plus a matching preset.yml with provides.templates: [{type: command, name: speckit.specify, file: commands/speckit.specify.md, strategy: wrap}].
The composed frontmatter contains the preset's description exactly as declared, plus argument-hint: Describe the feature you want to specify as its own key (from the Claude ARGUMENT_HINTS mapping).
Actual Behavior
With the description above (plain scalar), the composed frontmatter is invalid YAML:
name: speckit-specifydescription: Create or update the feature specification from a natural language featureargument-hint: "Describe the feature you want to specify"description. Also accepts an issue URL resolved via gh CLI (demo customization).
yaml.parser.ParserError: while parsing a block mapping
...
expected <block end>, but found '<scalar>'
If the description contains a character that forces quoting (e.g. change it to ... Also accepts a GitHub issue/PR URL or #N reference resolved via gh CLI (demo).), the output parses but is silently corrupted:
description: 'Create or update the feature specification from a natural language featureargument-hint: "Describe the feature you want to specify" description. Also accepts a GitHub issue/PR URL or #N reference resolved via gh CLI (demo).'
Parsed result: no argument-hint key; the hint line is absorbed into the description string.
Workaround
Declare the hint explicitly in the preset command frontmatter (argument-hint: "Describe the feature you want to specify"). It is then carried structurally through apply_argument_hint before serialization, and inject_argument_hint skips injection (key already present).
Suggested fix: make the Claude ARGUMENT_HINTS fallback add the key to the frontmatter dict before YAML serialization (the same structured path as apply_argument_hint), instead of post-serialization line insertion — or make inject_argument_hint fold-aware. A regression test should compose against the actual bundled core template with a fold-length description.
Bug Description
#3991 (preset wrap composition drops core
argument-hintand leaks its value intodescription) was closed by #3996, but on 0.16.2 the corruption still reproduces for the configuration that motivated the original report: Claude integration + astrategy: wrappreset whosedescriptionis long enough for the YAML dumper to fold it across multiple lines.Two facts combine:
argument-hintto the wrap-composition inheritance allowlist (copied fromcore_frontmatterwhen the preset frontmatter doesn't declare it). But the bundled core command templates (e.g.core_pack/commands/specify.md) declare noargument-hintfrontmatter at all — for Claude the hints live in the hardcodedARGUMENT_HINTSdict insrc/specify_cli/integrations/claude/__init__.py. The docstring ofagents.py::apply_argument_hintstates this explicitly: "Built-in templates carry noargument-hint, so this is a no-op for the core path." So there is nothing to inherit, and the composed frontmatter reaches serialization without the key.ARGUMENT_HINTSfallback then injects by raw string insertion into already-serialized YAML.ClaudeIntegration.post_process_skill_content→inject_argument_hintinsertsargument-hint: "…"directly after the first line starting withdescription:. With a short one-line description this happens to work (which is presumably why [bug-fix] Fix preset-wrap-drops-argument-hint: inherit argument-hint from core template #3996's regression test passes). With a description long enough thatdump_frontmatterfolds it into a multi-line scalar, the injected line lands inside the folded scalar.Depending on whether the dumper quoted the scalar, the failure takes one of two forms (both reproduced on 0.16.2):
yaml.parser.ParserError: expected <block end>, but found '<scalar>').#, forcing single quotes) → the YAML stays valid but theargument-hintkey does not exist and its line is absorbed into thedescriptionstring — the same silent corruption as [Bug]: preset wrap composition drops core argument-hint and leaks its value into description #3991.Steps to Reproduce
Fresh project on 0.16.2:
Create a wrap preset whose command frontmatter declares only a long
description(~150+ chars, so it folds when dumped) andstrategy: wrap— noargument-hint:presets-src/demo-wrap/commands/speckit.specify.mdplus a matching
preset.ymlwithprovides.templates: [{type: command, name: speckit.specify, file: commands/speckit.specify.md, strategy: wrap}].Apply and inspect:
specify preset add --dev ./presets-src/demo-wrap head -8 .claude/skills/speckit-specify/SKILL.md python3 -c "import yaml; print(yaml.safe_load(open('.claude/skills/speckit-specify/SKILL.md').read().split('---')[1]))"Expected Behavior
The composed frontmatter contains the preset's
descriptionexactly as declared, plusargument-hint: Describe the feature you want to specifyas its own key (from the ClaudeARGUMENT_HINTSmapping).Actual Behavior
With the description above (plain scalar), the composed frontmatter is invalid YAML:
If the description contains a character that forces quoting (e.g. change it to
... Also accepts a GitHub issue/PR URL or #N reference resolved via gh CLI (demo).), the output parses but is silently corrupted:Parsed result: no
argument-hintkey; the hint line is absorbed into thedescriptionstring.Workaround
Declare the hint explicitly in the preset command frontmatter (
argument-hint: "Describe the feature you want to specify"). It is then carried structurally throughapply_argument_hintbefore serialization, andinject_argument_hintskips injection (key already present).Specify CLI Version
0.16.2
AI Agent
Claude Code
Operating System
macOS 15 (Apple Silicon, Darwin 25.5.0)
Python Version
3.14
Additional Context
ARGUMENT_HINTSfallback rather than the inheritance path it was meant to guard.ARGUMENT_HINTSfallback add the key to the frontmatter dict before YAML serialization (the same structured path asapply_argument_hint), instead of post-serialization line insertion — or makeinject_argument_hintfold-aware. A regression test should compose against the actual bundled core template with a fold-length description.