Skip to content

feat(cli): add the engine-selection topic and archive add-vale-rule-engine - #95

Open
thecodedrift wants to merge 5 commits into
openspec/add-vale-rule-engine-3-orchestrationfrom
openspec/add-vale-rule-engine-4-topic
Open

feat(cli): add the engine-selection topic and archive add-vale-rule-engine#95
thecodedrift wants to merge 5 commits into
openspec/add-vale-rule-engine-3-orchestrationfrom
openspec/add-vale-rule-engine-4-topic

Conversation

@thecodedrift

@thecodedrift thecodedrift commented Aug 11, 2026

Copy link
Copy Markdown
Member

Stack (root → tip):

Unit 4 of add-vale-rule-engine — the tip. Stacked on #94, merging down. Tasks 3.1–3.7 and 4.x, plus the archive. This completes the change.

The topic

Answers one question — which engine enforces a rule — and is written evidence-first: state what you would have to look at, then name the engine. Naming an engine and justifying it afterwards is how prose-about-code lands in sg and cross-file questions land in vale.

It carries D7's three boundary cases because each is a wrong answer people actually reach for:

Boundary Why it trips people
Prose about code is still prose "comments must not say obviously" is vale; "every export has a doc comment" is sg. The difference is what you must read
Vale sees one document at a time Prose consistency across documents is runtime, despite being entirely about prose
Engine is not trust tier sg and vale are both static — conflating the axes makes a prose rule look like it needs login

A stale task line, corrected rather than followed

Task 3.2 said to note that "the Vale binary is external, so sg satisfies [availability] locally". That is no longer true — add-vale-binary-packages ships Vale as an optionalDependency, so both engines are normally present. The topic states the rule as the property D7 asks for — choose an engine you know is available — which stays correct on an unsupported arch and server-side alike. The task line is annotated with why it wasn't followed verbatim.

3.7 ships in the same commit as the topic file

TOPICS ∪ INTERNAL_TOPICS must account for every recipe on disk, and the test asserts it in both directions — so a recipe added without a classification turns the suite red. It goes in TOPICS rather than INTERNAL_TOPICS per D7: the generator already makes this decision, today with a binary static | runtime classifier that predates Vale and is therefore wrong. Exporting it is what lets the generator render the same guidance instead of keeping a second copy free to drift.

One unrelated fix the topic forced

The prompts-isolation guard scans a built chunk with a regex for from "…" — and a built chunk embeds every recipe as a string literal. The sentence a different axis from "which engine" failed it with dist/prompts.js graph imports which engine. The scan now ignores candidates that cannot be module specifiers at all (verified that every real specifier shape — node:fs, @clack/prompts, ../scan — still passes through). Any future recipe containing those words would have hit the same baffling failure.

Archive

openspec archive moved the change to archive/2026-08-11-add-vale-rule-engine and promoted 16 requirements into openspec/specs/, including the new cli-vale-rule-engine capability. openspec/changes/ is clean, so the archive gate passes on this tip.

The changeset lands here too and flows down to the bottom branch, which is the one that merges to main.

Verification

pnpm --filter @taskless/cli test531 passed; lint, typecheck, prettier clean. openspec validate --specs --strict passes for every spec this change touches — cli-rules and cli-update-engine fail, but they fail identically with this work stashed and neither is touched here.

Before merging the stack

#71 still carries skip-changeset. It was accurate when unit 1 shipped no user-visible behaviour; the stack now ships a minor feature, and the label must come off before the bottom PR merges to main or the release is recorded as shipping no note.

Refs OSS-21

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds the new engine-selection help topic (and cross-references) so agents/users can decide which rule engine (sg/vale/runtime) fits a rule request, updates the prompts export surface to include the topic, and archives add-vale-rule-engine into OpenSpec specs.

Changes:

  • Add engine-selection embedded help topic, register it in taskless help, and add references from the routing recipes.
  • Export the new topic via @taskless/cli/prompts (TOPICS) and extend tests/guards around the built prompts bundle.
  • Archive add-vale-rule-engine, promoting requirements into openspec/specs/* and adding a changeset.

Reviewed changes

Copilot reviewed 14 out of 22 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/cli/test/prompts.test.ts Tightens the built-prompts import graph guard to ignore obvious prose false positives.
packages/cli/test/help-extensions.test.ts Extends CLI help tests to cover the new engine-selection topic and index listing.
packages/cli/src/prompts/index.ts Adds engine-selection to exported prompt topics.
packages/cli/src/help/static.txt Adds a link from the static recipe to engine-selection.
packages/cli/src/help/route.txt Adds a link from the route recipe to engine-selection.
packages/cli/src/help/engine-selection.txt Introduces the new engine-selection topic content.
packages/cli/src/commands/help.ts Registers engine-selection in the help index recipe topics list.
openspec/specs/cli-vale-rule-engine/spec.md Adds the promoted “Vale rule engine” capability spec.
openspec/specs/cli-rule-routing/spec.md Adds engine-selection requirements to rule routing spec.
openspec/specs/cli-rule-format/spec.md Adds a requirement describing Vale StyleName layout/identity.
openspec/specs/cli-help/spec.md Adds requirements for registering the engine-selection topic and recipe references.
openspec/specs/cli-check/spec.md Adds requirements describing concurrent multi-engine execution and merge.
openspec/changes/archive/2026-08-11-add-vale-rule-engine/tasks.md Marks the archived change tasks as completed and annotates the stale task line.
openspec/changes/archive/2026-08-11-add-vale-rule-engine/specs/cli-vale-rule-engine/spec.md Archive record of added requirements for the Vale engine spec.
openspec/changes/archive/2026-08-11-add-vale-rule-engine/specs/cli-rule-routing/spec.md Archive record of added routing requirements.
openspec/changes/archive/2026-08-11-add-vale-rule-engine/specs/cli-rule-format/spec.md Archive record of added rule-format requirements.
openspec/changes/archive/2026-08-11-add-vale-rule-engine/specs/cli-help/spec.md Archive record of added help requirements.
openspec/changes/archive/2026-08-11-add-vale-rule-engine/specs/cli-check/spec.md Archive record of added check requirements.
openspec/changes/archive/2026-08-11-add-vale-rule-engine/proposal.md Archived proposal for the Vale engine change.
openspec/changes/archive/2026-08-11-add-vale-rule-engine/design.md Archived design notes for the Vale engine change.
openspec/changes/archive/2026-08-11-add-vale-rule-engine/.openspec.yaml Archive metadata for the change directory.
.changeset/vale-rule-engine.md Adds a minor-version changeset for the Vale engine + new knowledge topic.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread openspec/specs/cli-rule-routing/spec.md Outdated
@thecodedrift

Copy link
Copy Markdown
Member Author

@claude /review

@claude

claude Bot commented Aug 11, 2026

Copy link
Copy Markdown

Claude finished @thecodedrift's task in 4m 17s —— View job


I'll analyze this and get back to you.

thecodedrift added a commit that referenced this pull request Aug 11, 2026
…required

Two gaps in the "One changeset, at the bottom of the stack" guidance,
both found on the #71/#93/#94/#95 merging-down stack:

- The section said where the changeset lives but never when to create
  it. Document the trigger: write it on the base branch before cutting
  the children, so every child inherits a file it can append to.
- "Grow it incrementally" is merge-forward reasoning. Under merging
  down nothing lands until everything does, so a whole-change note is
  accurate at the only moment it is read. Scope the rule to forward,
  and state the constraint that holds in both shapes: the file must be
  in the bottom PR's own diff.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jwc9FFroR3mTZ4hLiSkkX3
@claude

claude Bot commented Aug 12, 2026

Copy link
Copy Markdown

Claude finished @thecodedrift's task in 5m 7s —— View job


Claude finished @thecodedrift's task

Todo list

  • Gather context (diff, changed files, CLAUDE.md conventions)
  • Run finder angles across the diff
  • Verify candidate findings
  • Post inline comments for concrete issues
  • Post final top-level assessment

Overall assessment

Solid unit. The engine-selection topic itself is well written (evidence-before-answer, three boundary cases, worked-example table), the TOPICS export change is mechanically correct (auto-registered via the import.meta.glob in recipes.ts, so no manual mapping to forget), the prompts.test.ts completeness check still holds both directions, and the archive move is a clean rename with openspec/changes/ left empty. Tests were added for the new topic's content, index listing, and unknown-topic resolution.

One real defect found, plus two lower-priority nits — both posted as inline comments.

1. Spec requirement contradicts itself, design.md, and the shipped topic text (confirmed, openspec/specs/cli-rule-routing/spec.md:265)

The new "Ambiguity defaults to an engine known to be available" requirement says the topic "SHALL default to sg" in the same sentence that says it must be stated "as a property... rather than as a bare fact about sg." That's self-contradictory, and it also contradicts:

  • design.md D7, which explicitly rejects naming sg as the default ("Stated as that property rather than as a bare fact about sg")
  • The actual shipped engine-selection.txt (step 4), which never names sg and explicitly says "rather than defaulting to a name"

This is also what @Copilot's review flagged inline on the same line — independently confirmed here. Since this spec is what promotes into openspec/specs/ as ground truth (and is the reference future consumers, e.g. the platform generator, would follow per D7's stated goal), the hardcoded sg should be replaced with the property-based framing before this lands, or a future reader could reasonably implement "always default to sg" — which is precisely the wrong behavior the topic text and design doc were written to avoid.

2. Fragile band-aid in the import-graph guard (plausible, packages/cli/test/prompts.test.ts:252-254)

looksLikeSpecifier fixes the one collision that broke (from "which engine", which has a space), but the guard is still a regex scan over bundled prose. A future recipe with a space-free quoted phrase after "from" (e.g. differs from "static-tier") would still match the specifier shape and reintroduce the same false-positive failure. Not blocking — just narrower than "any future recipe... would have hit the same failure" fully implies.

3. Minor test-coverage gap (not blocking)

packages/cli/test/help-routing-telemetry.test.ts (untouched by this PR) still only exercises ["route", "existing", "static", "remote"] for cli_help telemetry capture, not engine-selection. Confirmed the actual telemetry capture path in help.ts is generic to any topic string, so this isn't a functional gap — just a missed opportunity to extend an existing table-test alongside the new topic's other test coverage.

Verification note

Per instructions, I did not run the build/lint/test suite locally — CI reports that on the PR itself. The findings above are from static reading of the diff plus the design.md/spec cross-references within this PR's own scope.
· branch: openspec/add-vale-rule-engine-4-topic

Comment thread openspec/specs/cli-rule-routing/spec.md Outdated
Comment thread packages/cli/test/prompts.test.ts Outdated
@thecodedrift

Copy link
Copy Markdown
Member Author

Re: @claude[bot] — "Claude finished @thecodedrift's task in 4m 17s —— I'll analyze this and get back to you."
#95 (comment)

Flagging that this review produced no content — the body is just "I'll analyze this and get back to you," unchanged since it posted. So #95 has effectively had no Claude review; its only substantive feedback was Copilot's ambiguity-default finding, now fixed and resolved above. A rerun is planned once the stack below this branch finishes rebasing.

Two commits since that run, both docs-only:

  • 4c866c7 — the routing spec no longer names sg as the ambiguity default (Copilot's finding). It was the only artifact still saying so; the topic text, help-extensions.test.ts:248, and D7 all already said otherwise.
  • 01d0458 — the Vale verify requirement now states that both pass/ and fail/ must hold a document before a rule can be reported verified. The implementation on feat(cli): verify Vale rules from per-rule fixture directories #93 enforces this; the spec was silent on an unpopulated bucket, and that silence is what let a rule with only pass/ fixtures satisfy every stated assertion while proving nothing. Recorded on this branch rather than feat(cli): verify Vale rules from per-rule fixture directories #93 because this PR git mvs the file into the archive, so an edit one branch down would be dropped when that move replays during the rebase.

Note for the rerun: this branch will be rebased onto a rewritten #94 before it merges, so the SHA reviewed now will not be the one that lands.

— AI Coding Agent

thecodedrift and others added 5 commits August 11, 2026 19:45
…ngine

Unit 4, tasks 3.1-3.7 and 4.x, plus the archive. This completes the change.

The topic answers one question — which engine enforces a rule — and is written
evidence-first: state what you would have to look at, then name the engine.
Naming an engine and justifying it afterwards is how prose-about-code lands in
sg and cross-file questions land in vale.

It carries the three boundary cases from D7 because each is a wrong answer
people actually reach for. Prose about code is still prose: "comments must not
say obviously" is vale, while "every export has a doc comment" is sg, and the
difference is what you must read rather than what the subject is. Vale sees one
document at a time, so prose consistency ACROSS documents is runtime despite
being entirely about prose. And engine is not trust tier — sg and vale are both
static, so conflating the axes makes a prose rule look like it needs login.

Task 3.2's parenthetical was stale and is corrected in the task rather than
followed: it said the Vale binary is external, but add-vale-binary-packages now
ships it as an optionalDependency, so both engines are normally present. The
topic states the rule as the property D7 asks for — choose an engine you know is
available — which stays true on an unsupported arch and server-side alike.

3.7 puts the topic in TOPICS, in the same commit as the file, because
TOPICS union INTERNAL_TOPICS must account for every recipe on disk and the test
asserts it in both directions. TOPICS rather than INTERNAL_TOPICS per D7: the
generator already makes this decision, today with a binary static|runtime
classifier that predates Vale and is therefore wrong, and exporting the topic is
what lets it render the same guidance instead of keeping a second copy.

One unrelated fix the topic forced. The prompts bundle guard scans a built chunk
with a regex for `from "…"`, and a built chunk embeds every recipe as a string —
so the sentence `a different axis from "which engine"` failed it with
`dist/prompts.js graph imports which engine`. The scan now ignores candidates
that cannot be module specifiers at all. Any future recipe containing those
words would have hit the same confusing failure.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jwc9FFroR3mTZ4hLiSkkX3
The requirement's own title already said "an engine known to be
available", but its body and scenario both said the topic SHALL default
to `sg`. The shipped topic does the opposite on purpose: step 4 of
`engine-selection.txt` says to choose an engine you know is available
and explicitly declines to name one, and `help-extensions.test.ts:248`
pins that ("states the ambiguity default as a property, not as `sg`"),
citing D7.

So the spec was the only artifact carrying the old wording, and it
contradicted the text, the test, and the design decision at once. State
the requirement as the property the title always claimed, and say why a
named default cannot be right: either binary can be the missing one on
an unsupported arch, and server-side `sg` is the only ungated route, so
a fixed fallback is wrong in whichever case it did not anticipate.

Both copies updated -- the promoted spec and the archived delta.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jwc9FFroR3mTZ4hLiSkkX3
The verify implementation now refuses to report a rule verified unless
both `pass/` and `fail/` hold a document, but the spec was silent on an
unpopulated bucket -- it only said what must happen for the fixtures
that exist. That silence is what let the code and the spec drift: a rule
with only `pass/` fixtures satisfied every stated assertion while
proving nothing, passing on an empty set of expected failures.

State the requirement the code enforces, and why: a `fail/` fixture
proves the rule fires, a `pass/` fixture proves it does not over-fire,
and either alone is half a claim. The report must also keep a
half-written fixture set distinct from a rule with no fixtures at all,
which is the distinction `ValeFixtureCoverage` encodes.

Recorded here rather than on the branch that implements it: this file is
`git mv`'d into the archive by this PR, so an edit one branch down would
be dropped or conflict when that move replays during the rebase.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jwc9FFroR3mTZ4hLiSkkX3
…ng it

The test asserted that the built prompts entry never reaches the CLI
entry or a host capability, and established that by regex-scanning built
JavaScript for `from "..."` to reconstruct the import graph. A built
chunk embeds every recipe as a string literal, so the engine-selection
recipe's `a different axis from "which engine"` was read as an import and
reported as `dist/prompts.js graph imports which engine`.

Filtering candidates by specifier shape only narrowed the accident. It
passed because that phrase contains a space; `differs from "static-tier"`
is a bare hyphenated name with no whitespace and would have been reported
as an import all the same. Whether the guard holds should not depend on
how a recipe author punctuates.

Rollup already knows the answer. `OutputChunk.imports`/`dynamicImports`
is the resolved graph, so a plugin walks it from the prompts entry and
fails the build on any external import or any path reaching the bin
entry. Verified both ways: the build passes as-is, and adding a
`node:fs` import to `src/prompts/index.ts` fails it with
"prompts entry graph imports node:fs".

Checking this in the build rather than over the artifact is the point.
A build that refuses to emit a leaking bundle makes the bad artifact
unproducible; a test that inspects one afterwards only notices, having
already written it to disk. It also avoids acquiring a parser dependency
to re-derive information the generator had.

The source-level allowlist stays a test: it constrains what we wrote
rather than what the build produced, and a regex is sound over
hand-written modules that embed no prose.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jwc9FFroR3mTZ4hLiSkkX3
The `cli_help` capture path in `help.ts` is generic over the topic
string, so this was never a functional gap -- just a table that listed
every routing topic except the one this change adds.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jwc9FFroR3mTZ4hLiSkkX3
@thecodedrift
thecodedrift force-pushed the openspec/add-vale-rule-engine-4-topic branch from 0726c6d to f926791 Compare August 12, 2026 03:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants