{"record":{"id":"1f89f5c3cd47439a","repo":"github/spec-kit","slug":"invalid-strategy-strategy-for-script-scripts","errorCode":null,"errorMessage":"Invalid strategy '{strategy}' for script: scripts only support {sorted(VALID_SCRIPT_STRATEGIES)}","messagePattern":"Invalid strategy '(.+?)' for script: scripts only support (.+?)","errorType":"validation","errorClass":"PresetValidationError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/presets/__init__.py","lineNumber":467,"sourceCode":"\n            # Validate strategy field (optional, defaults to \"replace\")\n            strategy = tmpl.get(\"strategy\", \"replace\")\n            if not isinstance(strategy, str):\n                raise PresetValidationError(\n                    f\"Invalid strategy value: must be a string, \"\n                    f\"got {type(strategy).__name__}\"\n                )\n            strategy = strategy.lower()\n            # Persist normalized value so downstream code sees lowercase\n            if \"strategy\" in tmpl:\n                tmpl[\"strategy\"] = strategy\n            if strategy not in VALID_PRESET_STRATEGIES:\n                raise PresetValidationError(\n                    f\"Invalid strategy '{strategy}': \"\n                    f\"must be one of {sorted(VALID_PRESET_STRATEGIES)}\"\n                )\n            if tmpl[\"type\"] == \"script\" and strategy not in VALID_SCRIPT_STRATEGIES:\n                raise PresetValidationError(\n                    f\"Invalid strategy '{strategy}' for script: \"\n                    f\"scripts only support {sorted(VALID_SCRIPT_STRATEGIES)}\"\n                )\n\n            # Validate template name format\n            if tmpl[\"type\"] == \"command\":\n                # Commands use dot notation (e.g. speckit.specify)\n                if not re.match(r'^[a-z0-9.-]+$', tmpl[\"name\"]):\n                    raise PresetValidationError(\n                        f\"Invalid command name '{tmpl['name']}': \"\n                        \"must be lowercase alphanumeric with hyphens and dots only\"\n                    )\n            else:\n                if not re.match(r'^[a-z0-9-]+$', tmpl[\"name\"]):\n                    raise PresetValidationError(\n                        f\"Invalid template name '{tmpl['name']}': \"\n                        \"must be lowercase alphanumeric with hyphens only\"\n                    )","sourceCodeStart":449,"sourceCodeEnd":485,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/presets/__init__.py#L449-L485","documentation":"A stricter, script-specific strategy check: entries with `type: script` only support {\"replace\", \"wrap\"}. Prepending or appending to an executable script does not make sense semantically, so it is rejected even though those strategies are valid for other template types.","triggerScenarios":"preset.yml contains a template with `type: script` and `strategy: prepend` or `strategy: append`. The general strategy check (VALID_PRESET_STRATEGIES) passes first, then this script-specific check fails.","commonSituations":"Copying a command template entry (which supports prepend/append for content merging) and changing only its type to script, or assuming one uniform strategy vocabulary across all template types.","solutions":["Set the script template's strategy to \"replace\" or \"wrap\".","If you actually need prepend/append behavior, the entry is not a script — reconsider its type or restructure so the script itself composes content.","Omit strategy for scripts wanting full overwrite — default is \"replace\"."],"exampleFix":"# before\n  - name: setup\n    type: script\n    file: scripts/setup.sh\n    strategy: append\n\n# after\n    strategy: \"replace\"","handlingStrategy":"validation","validationCode":"SCRIPT_OK = {\"replace\", \"wrap\"}\nfor t in yaml.safe_load(open(\"preset.yml\")).get(\"templates\", []):\n    if t.get(\"type\") == \"script\":\n        s = str(t.get(\"strategy\", \"replace\")).lower()\n        assert s in SCRIPT_OK, f\"script {t.get('name')}: strategy must be replace or wrap, got {s!r}\"","typeGuard":"def script_strategy_ok(template: dict) -> bool:\n    return str(template.get(\"strategy\", \"replace\")).lower() in {\"replace\", \"wrap\"}","tryCatchPattern":"try:\n    manager.install_from_directory(src, version)\nexcept PresetValidationError as e:\n    if \"for script\" in str(e):\n        # script templates only accept replace/wrap; fix and retry\n        ...","preventionTips":["Scripts only support replace and wrap — prepend/append are content-merge strategies.","When copying a command entry and changing type to script, drop or change the strategy."],"tags":["preset","validation","script","strategy"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}