Config rules: an unquoted ": " in a rules item silently drops the artifact's entire rule set
Summary
A single malformed item in an artifact's rules: list makes the CLI ignore that artifact's entire rule set. The item is Keep the "Why" section concrete: what breaks today without the change — valid-looking YAML, but the unquoted : means YAML parses it as a mapping, so the list stops being an array of strings. The whole artifact's rules are then dropped, and the only signal is a warning on stderr.
Two things make this hard to notice:
openspec validatedoes not check the config, so a config whose rules are being ignored validates clean. (openspec new changeandopenspec instructionsdo warn — see the output below — but CI runsvalidate --all, which is silent.)- The failure is all-or-nothing. The list is rejected on its shape, not per item, so a project loses every rule for that artifact — including unrelated, well-formed ones.
Reproduction
# openspec/config.yaml
rules:
proposal:
- Keep the "Why" section concrete: what breaks today without the change
- List "What Changes" at the file level
- Declare "New Capabilities" / "Modified Capabilities" using existing capability names
specs:
- Requirements are declarative SHALL statements, testable via WHEN/THEN scenarios
- Delta specs use ADDED/MODIFIED/DEPRECATED/REMOVED requirement blocks
- Keep scenario wording consistent with existing captured specs
rules.proposal[0] contains an unquoted : , so YAML yields a mapping where the array expects a string:
[{"Keep the \"Why\" section concrete": "what breaks today without the change"}, "List ...", "Declare ..."]
$ openspec new change probe
- Creating change 'probe' with schema 'spec-driven'...
Rules for 'proposal' must be an array of strings, ignoring this artifact's rules
Created change 'probe' at openspec/changes/probe/
$ openspec instructions proposal --change probe --json 2>/dev/null | jq 'has("rules")'
false # key omitted entirely — all three rules gone, including the two well-formed ones
$ openspec instructions specs --change probe --json 2>/dev/null | jq '.rules | length'
3 # the well-formed artifact's rules load fine
$ openspec validate --all
# exits without a config-rules warning (the probe change itself is not valid yet:
# it has no delta, so no spec validation runs)
(The warning is on stderr, so capturing it alongside stdout needs 2>&1.)
Verified on 1.13.0 (installed) and 1.11.0 (npx @fission-ai/[email protected]) — identical warning text and behaviour on both.
What would help
Any of these, roughly in order of how much they'd have saved us:
- Report the offending item, not just the artifact — e.g.
rules.proposal[0]is not a string (found a mapping; did you mean to quote the scalar?). We bisected the list by hand to find it. - Fail or warn in
validate.validate --allis what CI runs; a config the CLI cannot fully read arguably belongs in its scope, or at least should not validate silently clean. - Consider not discarding the whole list — one unparseable item taking out its siblings' rules compounds the first problem.
Why it bit us
We declared rules for four artifacts in August. Two were being ignored from then until we found this ~5 weeks later, because the warning appears wherever the rules are read and we had been treating stderr on new change as noise; validate never showed it. The dropped set included Declare "New Capabilities" / "Modified Capabilities" using existing capability names — a rule we kept forgetting. We only found it by probing the CLI's read path directly (openspec instructions <artifact> --json | jq 'has("rules")'). Quoting the two offending scalars fixed it.
Source: Fission-AI/OpenSpec