You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Recipe-only change. create-vale-rule goes v11 to v12 and verify-rule v3 to v4; no CLI behaviour moves, so no changeset (skip-changeset).
Merge order: land this AFTER #380 (feat/vale-raw-authoring-checks), the code half of #360/#361 (the multi-entry raw notice and the vendor-contract pins). This PR closes those two issues and its raw passage quotes the notice verify emits, which only exists once that branch is in.
What
create-vale-rule: raw entries concatenate, they do not alternate #360, raw entries concatenate. Step 3, a new bullet directly under the existing raw bullet: Vale joins every raw: entry into one pattern with nothing between them, alternation goes inside one entry as (a|b|c), the wrong/right YAML pair from the issue, and the notice text verify emits for a multi-entry list. Two corrections against the issue text, both from feat(verify): warn on multi-entry raw and pin Vale's raw and front-matter behavior #380's vendor-contract measurements: the "right" example drops nonword: true (it governs tokens; a raw entry is verbatim, so it was inert there), and the existing raw/nonword bullet now says which of the two nonword acts on.
create-vale-rule: scope: raw lints YAML front matter as text #361, front matter is text. Step 2, after the "raw subsumes code and text" paragraph: the target: taskless case. Per feat(verify): warn on multi-entry raw and pin Vale's raw and front-matter behavior #380's measurements, front matter is read by raw, text and a rule with no scope alike, so the passage says dropping raw alone fixes nothing and names the three fixes: scope: paragraph/sentence for a prose rule, frontmatter.<key> for a field rule, and a pass fixture carrying the machine key. Then the flip side (occurrence + min: 1 + scope: raw to require a front-matter field) with the issue's YAML.
create-vale-rule: verify with the vendored binary, never a system vale; and check already excludes .taskless/ #363, vendored binary; .taskless/** unnecessary. Step 6, after the empty-result passage: a bare vale proves nothing about check (the 3.15 vs 3.20 dogfood case), verify/test/check run the vendored binary, and check excludes .taskless/ on a whole-project walk so the [.taskless/**] … = NO block only acts under the invocation the passage tells you not to use; verify already advises on it (linked to step 4's advisory list). The recipe's own .vale.ini examples never carried such a matcher, so nothing to remove.
create-vale-rule: an empty result is not a clean pass; read notices and vale-parse-error findings #364, empty result is not a clean pass. Step 6, after "Read results there": results: [] is a pass only with no vale-parse-error finding, no notices and no failures; the measured vale-parse-error finding shape and the two notice texts; how the same gap shows under test. verify-rule.md gains a notice bullet and a paragraph on unparseable fixtures (a fail/ one reports "did not fire", a pass/ one stays green).
Source
The 2026-09-21 dogfood notes behind #360 to #364 (eleven voice rules over ~1,100 markdown files). Every measurement not quoted from the issues was re-run on this tree's vendored Vale 3.22.0 with a scratch project: the raw join (\bstops + being\b fires on It stops being. and on neither half alone), front-matter reach under raw, text, default, [raw, code, text] and ~frontmatter, the occurrence/min: 1 rule on a document with and without description, the vale-parse-error finding from an unquoted colon, and test on an unparseable fail/ and pass/ fixture.
Verified
pnpm build && pnpm cli agent create-vale-rule renders topic v12 with no %(…)s marker left; agent verify-rule renders v4.
pnpm cli check clean over the recipes (no-em-dashes, no-hedging, no-blocklist-phrases all quiet).
pnpm lint, pnpm typecheck, pnpm --filter @taskless/cli test (98 files, 1623 tests). Two tests caught a literal 0.11.2 and two ISO dates in the first draft; the passages now say "now" and spell the date out.
Noticed, not changed
A pass/ fixture whose front matter Vale cannot parse stays green under test even when it holds a violation, because test counts only findings from the rule under test and the parse-error finding is filed under vale-parse-error. Documented in both recipes; worth a follow-up so test reports the unread fixture.
.prior-review.json held no review threads, no review summaries, and no comments other than the @claude /review trigger itself. This is the first review of this PR — everything below is assessed fresh, nothing is being repeated or skipped as previously addressed.
Scope
Docs-only change to two agent recipe files (packages/cli/src/agent/create-vale-rule.md v11→v12, packages/cli/src/agent/verify-rule.md v3→v4), folding measured Vale gotchas from issues #360–#364 into the recipes. I fact-checked essentially every quoted JSON shape, error string, and behavioral claim in the new prose against the current source (rules/vale/run.ts, rules/vale/verify.ts, rules/dispatch.ts, rules/vale/formats.ts, rules/inspect.ts, schemas/check.ts, schemas/rules-verify.ts) rather than taking the prose on faith, since a wrong claim here silently misleads every agent that reads the recipe. Everything checked out exactly as written:
The vale-parse-error JSON shape and "Vale could not check this file: …" message match parseErrorResult/formatValeConfigError in run.ts verbatim, including severity: "error".
notices being absent from --json output when empty matches schemas/check.ts (.optional()) and commands/check.ts:314 (...(runNotices.length > 0 ? { notices: runNotices } : {})).
"fail fixture did not fire: …" matches rules/inspect.ts:447 verbatim; the pass/fail asymmetry for an unparseable fixture matches the firedIn filtering logic in rules/vale/verify.ts (vale-parse-error findings never match result.ruleId === ruleId, so they're invisible to both missingFailures and unexpectedFindings).
The Vale notice field description in verify-rule.md matches schemas/rules-verify.ts's valeVerifyOutputSchema.notice description almost word for word.
The .taskless/** advisory cross-reference ("step 4 lists the advisory") and the W101 cross-reference ("the mistake step 4 warns of") both point at real content in step 4 of create-vale-rule.md.
occurrence's field is token (singular), matching the existing field table elsewhere in the same file; no em dashes were introduced (consistent with the "Verified" section's no-em-dashes claim); all markdown code fences are balanced.
[New] One real risk: the raw-notice passage documents behavior that doesn't exist in this tree yet
packages/cli/src/agent/create-vale-rule.md, the new "raw entries concatenate" bullet (approx. lines 502–538 in the current file) quotes a verify notice:
<id>: raw has N entries; Vale joins them into one pattern with no separator, so the second never matches on its own. Write one entry with (a|b) unless the join is intended.
I grepped the whole packages/cli/src/rules/vale/ and packages/cli/src/schemas/ trees for this text ("raw has", "joins them into one pattern") and for any raw-multi-entry check in verify.ts — there is none. The PR body is explicit about why: this passage "quotes the notice verify emits, which only exists once that branch is in" (#380, feat/vale-raw-authoring-checks), and states this PR must land after#380. I confirmed #380 is still OPEN (mergedAt: null), based on main, not yet merged.
The problem is that PR #381 is opened against main directly rather than stacked on top of feat/vale-raw-authoring-checks, so nothing in the repo mechanically prevents merging #381 before #380. If that happens, the built create-vale-rule recipe (topic v12) will tell every agent that verify reports a raw-entries notice in a specific format, when verify does not actually emit it — the exact "silent, nothing fails, docs quietly stop matching reality" failure mode the rest of this repo's conventions are so careful about elsewhere (see the OpenSpec archive guidance in root CLAUDE.md). The "Verified" section in the PR body only confirms the placeholders render with no %(…)s markers left; it doesn't (and can't, since the code isn't there) verify the notice text against a real verify run.
This isn't a defect in the prose itself — it reads correctly once #380 lands — but it is a real correctness gap in the sequencing, worth calling out explicitly rather than trusting the PR description alone: confirm the merge order is actually enforced (e.g., land #380 first, or convert this into a PR stacked on feat/vale-raw-authoring-checks) before this is merged to main.
No other findings survived verification. The prose is unusually well fact-checked against the vendored Vale binary and the current source; I could not find a factual error, a broken cross-reference, a dangling code fence, or a CLAUDE.md convention violation anywhere else in the diff.
Re:@claude[bot] — "Claude finished @thecodedrift's task in 5m 27s … One real risk: the raw-notice passage documents behavior that doesn't exist in this tree yet" #381 (comment)
Confirmed: the quoted notice text has no source in this tree (grep "raw has" packages/cli/src outside the recipe returns nothing) and #380 is still open against main, so nothing enforces the order beyond the PR body. The sequencing question (land #380 first, or restack this PR on feat/vale-raw-authoring-checks) is being raised with the maintainer rather than decided here; no change to the prose is needed either way.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
skip-changesetPR intentionally ships no release note (bypasses the changeset requirement)
1 participant
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Recipe-only change.
create-vale-rulegoes v11 to v12 andverify-rulev3 to v4; no CLI behaviour moves, so no changeset (skip-changeset).Merge order: land this AFTER #380 (
feat/vale-raw-authoring-checks), the code half of #360/#361 (the multi-entryrawnotice and the vendor-contract pins). This PR closes those two issues and itsrawpassage quotes the noticeverifyemits, which only exists once that branch is in.What
rawentries concatenate. Step 3, a new bullet directly under the existingrawbullet: Vale joins everyraw:entry into one pattern with nothing between them, alternation goes inside one entry as(a|b|c), the wrong/right YAML pair from the issue, and thenoticetextverifyemits for a multi-entry list. Two corrections against the issue text, both from feat(verify): warn on multi-entry raw and pin Vale's raw and front-matter behavior #380's vendor-contract measurements: the "right" example dropsnonword: true(it governstokens; arawentry is verbatim, so it was inert there), and the existingraw/nonwordbullet now says which of the twononwordacts on.rawsubsumescodeandtext" paragraph: thetarget: tasklesscase. Per feat(verify): warn on multi-entry raw and pin Vale's raw and front-matter behavior #380's measurements, front matter is read byraw,textand a rule with noscopealike, so the passage says droppingrawalone fixes nothing and names the three fixes:scope: paragraph/sentencefor a prose rule,frontmatter.<key>for a field rule, and a pass fixture carrying the machine key. Then the flip side (occurrence+min: 1+scope: rawto require a front-matter field) with the issue's YAML.fail/fixture plus the legitimate uses aspass/; run a new branch over the corpus beforewarningand record the count beside the branch (the comment block from the issue). Documents the workable count (check --json | jqonruleId) and points at check: run a single rule over the project to measure a new branch #379, filed for a realcheck --rule <id>..taskless/**unnecessary. Step 6, after the empty-result passage: a barevaleproves nothing aboutcheck(the 3.15 vs 3.20 dogfood case),verify/test/checkrun the vendored binary, andcheckexcludes.taskless/on a whole-project walk so the[.taskless/**] … = NOblock only acts under the invocation the passage tells you not to use;verifyalready advises on it (linked to step 4's advisory list). The recipe's own.vale.iniexamples never carried such a matcher, so nothing to remove.resultsthere":results: []is a pass only with novale-parse-errorfinding, nonoticesand nofailures; the measuredvale-parse-errorfinding shape and the two notice texts; how the same gap shows undertest.verify-rule.mdgains anoticebullet and a paragraph on unparseable fixtures (afail/one reports "did not fire", apass/one stays green).Source
The 2026-09-21 dogfood notes behind #360 to #364 (eleven voice rules over ~1,100 markdown files). Every measurement not quoted from the issues was re-run on this tree's vendored Vale 3.22.0 with a scratch project: the
rawjoin (\bstops+being\bfires onIt stops being.and on neither half alone), front-matter reach underraw,text, default,[raw, code, text]and~frontmatter, theoccurrence/min: 1rule on a document with and withoutdescription, thevale-parse-errorfinding from an unquoted colon, andteston an unparseablefail/andpass/fixture.Verified
pnpm build && pnpm cli agent create-vale-rulerenders topic v12 with no%(…)smarker left;agent verify-rulerenders v4.pnpm cli checkclean over the recipes (no-em-dashes,no-hedging,no-blocklist-phrasesall quiet).pnpm lint,pnpm typecheck,pnpm --filter @taskless/cli test(98 files, 1623 tests). Two tests caught a literal0.11.2and two ISO dates in the first draft; the passages now say "now" and spell the date out.Noticed, not changed
A
pass/fixture whose front matter Vale cannot parse stays green undertesteven when it holds a violation, becausetestcounts only findings from the rule under test and the parse-error finding is filed undervale-parse-error. Documented in both recipes; worth a follow-up sotestreports the unread fixture.Fixes #360
Fixes #361
Fixes #362
Fixes #363
Fixes #364