Repository navigation
feat(verify): warn on multi-entry raw and pin Vale's raw and front-matter behavior - #380
Conversation
|
@claude /review |
|
Claude finished @theCodeDrift's task in 11m 36s —— View job Review completeReview mode: incremental — read 0 prior review thread(s) before reviewing.
Findings[New] The PR's design is sound: Four items posted inline, ranked most notable first:
No correctness bugs found in the core advisory logic, the |
Read in full. The medium item ( — AI Coding Agent |
e4951a1 to
84d3459
Compare
What
The code halves of #360 and #361. The recipe prose lands in a parallel PR; this one pins the underlying Vale behavior on the vendored binary and adds the
verifyadvisory #360 asks for.packages/cli/test/vale-vendor-contract.test.ts, three newdescribeblocks under "Vale vendor contract"): how anexistencerule'srawlist is read, which keynonwordgoverns, and which scopes see YAML front matter.verifyadvisory (packages/cli/src/schemas/vale-rule.ts, wired inpackages/cli/src/rules/inspect.ts):validateValeRulenow returnsadvisories: string[]besidevalid/errors, andverifyOneRulejoins them with the config layer's advisories onto the rule'snotice, inverifytext output and--json..changeset/vale-raw-authoring.md,patch.The exact advisory:
Measured
Every row below was run on the vendored Vale 3.22.0 before it was written down, and each is now a test.
rawjoinsraw: ["\bstops being\b", "[^.]{0,20}\band becomes\b"]It and becomes fun.(second entry alone)It stops being fun.(first entry alone)It stops being dull and becomes fun.stops being dull and becomesraw: [alpha, bravo, charlie]bravo alone. alphabravocharlie together.alphabravocharlieraw: ["(\bstops being\b|[^.]{0,20}\band becomes\b)"]nonwordis fortokenstokens: ["—"]This is a sentence — with an em dash.tokens: ["—"],nonword: truenonworddoes not touchrawraw: ["—"], with and withoutnonwordraw: ["(The|That|This) is the twist\."], with and withoutnonwordThat is the twist. Yes.scope: raw, tokentaskless---\ntarget: taskless\n---\n\nSee taskless in the body.scope: text, and noscopeat allscope: paragraph,scope: sentencescope: frontmatter.targetscope: frontmatter.titlescope: frontmatteroccurrence,min: 1,scope: raw,token: '(?m)^description: .+$'description:description:Two things the issues did not say, worth carrying into the recipe PR:
nonword: trueis atokensconcern. Vale wraps the pattern in\b…\bonly when the rule hastokensandnonwordis unset;rawis inserted verbatim. The "Right" example in create-vale-rule: raw entries concatenate, they do not alternate #360 carriesnonword: truebeside arawlist, where it is harmless and does nothing.scope: rawis not what lets a casing rule intotarget: taskless; a rule with noscopekey, andscope: text, see it as well.paragraphandsentencedo not. So "dropscope: raw" is not a fix on its own.Why advisory, not rejection
A multi-entry
rawhas a legitimate reading: it is how Vale lets an author split one long pattern across lines, and the join is exactly what such an author wants. The trap is the other reading, where the entries were meant as alternatives, and the schema cannot tell the two apart. So it is said rather than refused, the same linevale-config.tsholds for a repeated key or a[*]matcher:okstays true,errorsstays empty, and the message names the fix for the reading that is usually meant.The corpus in
vale-corpus.tshas three verdicts (accepted,ignored,rejected) and no "accepted, with a note", so the advisory is not a corpus row; its unit tests sit invale-schema-contract.test.tsbeside the other tests that ask the schema rather than the binary.How verified
pnpm build,pnpm typecheck,pnpm lint,pnpm --filter @taskless/cli test: 98 files, 1645 tests, all passing.tokenslist,Raw:casing, beside a rejection, empty list), 3 CLI-level tests inverify-test-commands.test.ts(notice present on two entries in text and--json, absent on one entry, joined with a config advisory).packages/cli/src/agent/create-vale-rule.md(the recipe PR owns it).Refs #360
Refs #361