fix(schema): validate the apply block against declared artifacts (#1868)
* fix(schema): validate the apply block against declared artifacts parseSchema checked each artifact's requires but never the apply block, so schema validate passed a schema whose apply.requires named an artifact that does not exist or whose apply.tracks named a file no artifact generates. At run time apply skipped the unknown id, turning the apply gate off, or blocked forever on a tracked file nothing produces. Reject both in parseSchema, naming the bad value and what the schema declares. tracks is compared to generates by exact string, the same comparison the tracked-tasks lookups use, so any schema that parses is one they can resolve. * fix(schema): warn instead of failing the load on an unmatched apply.tracks apply.tracks is a path that apply reads as written, not an artifact id. A schema that tracks a hand-written TODO.md, or one file under a glob generates such as tasks/main.md, loads and applies correctly on main. Rejecting it in parseSchema made every command on that schema fail. Keep the unknown apply.requires id as a load error, the same as an unknown artifact requires. Report an apply.tracks path that matches no artifact's generates as a warning from `openspec schema validate`, which still exits 0. Move the docs note from legacy docs/customization.md into docs-lab (schema-yaml.md validation section, cli.md schema validate). The schema-yaml.md table had said unknown apply.requires IDs go unreported. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(schema): describe apply.tracks as a generates mismatch, not as ungenerated The apply.tracks check compares `tracks` to each artifact's `generates` with exact string equality, but its warning said the tracked file "is not generated by any artifact". That is false for the case this PR deliberately supports: `tracks: tasks/main.md` under `generates: tasks/*.md`, where the glob really does generate the file and only the strings differ. The diagnostic now names the real condition (the `tracks` value does not exactly match any `generates` value, so OpenSpec cannot tell which artifact's progress it tracks) and keeps both remedies. Both docs-lab pages, the changeset and the JSDoc that repeated the claim are corrected the same way, and a new CLI test pins that the glob case is described as a mismatch and never as ungenerated. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test(schema): pin apply.tracks matching across Windows path separators The tracked-tasks lookup compares apply.tracks and generates as plain strings, so a backslash on one side and a forward slash on the other must warn, and the same backslash spelling on both sides must not. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Clay Good <hi@claygood.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
D
Dwin Gharibi committed
7090e16d74dfe588dad72bc4fda9bf124e71b0af
Parent: a5bf5c6
Committed by GitHub <noreply@github.com>
on 9/16/2026, 7:11:23 PM