diff --git a/.changeset/vale-rule-engine.md b/.changeset/vale-rule-engine.md new file mode 100644 index 00000000..11f1e297 --- /dev/null +++ b/.changeset/vale-rule-engine.md @@ -0,0 +1,17 @@ +--- +"@taskless/cli": minor +--- + +Add Vale as a second static-tier rule engine. + +`check` now dispatches by engine directory and runs ast-grep, Vale, and runtime +rules concurrently, merging their findings into one result set. Vale rules live +in `.taskless/vale/` and execute against the committed `.vale.ini`; an +unavailable Vale reports itself and the other engines still return, while a Vale +that times out or rejects its config fails the check rather than passing as a +clean run. Vale rules are verified from `rule-tests//pass|fail` fixtures +against a generated per-rule config. + +Adds the `engine-selection` knowledge topic — which engine enforces a given +rule, and why — available from `taskless help engine-selection` and exported +through `@taskless/cli/prompts`. diff --git a/openspec/changes/add-vale-rule-engine/.openspec.yaml b/openspec/changes/add-vale-rule-engine/.openspec.yaml new file mode 100644 index 00000000..9e5b8a19 --- /dev/null +++ b/openspec/changes/add-vale-rule-engine/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-07-23 diff --git a/openspec/changes/add-vale-rule-engine/design.md b/openspec/changes/add-vale-rule-engine/design.md new file mode 100644 index 00000000..7729d390 --- /dev/null +++ b/openspec/changes/add-vale-rule-engine/design.md @@ -0,0 +1,126 @@ +## Context + +`partition-rules-by-engine` established the layout this change executes: `.taskless//{config, rules/, rule-tests/}`, dispatch by directory rather than by parsing files, each engine's committed native config as the source of truth, and a scaffolded but inert `vale/`. Read that change's design for the layout, migration `0004`, ingest defaulting, and legacy-path tolerance — none of it is restated here. + +What remains is the engine itself. The design was derived empirically against `vale 3.15.1` and `sg 0.41.0`; the facts that shaped it: + +- **Vale `.vale.ini` scoping composes cleanly and predictably** (all verified): `[] Style.Rule = YES` enables a rule for a path (no `BasedOnStyles` needed); overlapping enables **union**; `[] Style.Rule = NO` **disables** and wins over an enable, order-independent; globs support brace alternation with slashes (`[{marketing/**,docs/**}]`) and whole-section negation (`[!x]`). So arbitrary per-rule include/exclude lives in the `.ini` itself. +- **Vale requires a StyleName directory level** (`StylesPath/