Skip to content

feat(agent): per-tool create-legacy-rule recipes with --tool - #578

Draft
theCodeDrift wants to merge 5 commits into
legacy-linter-ladderfrom
legacy-linter-tools
Draft

theCodeDrift wants to merge 5 commits into
legacy-linter-ladderfrom
legacy-linter-tools

Conversation

@theCodeDrift

@theCodeDrift theCodeDrift commented Oct 23, 2026 •

Copy link
Copy Markdown
Member

Stack (root → tip):

Second of two PRs for legacy-linter-recipes, stacked on #577. This PR archives the change.

taskless agent create-legacy-rule --tool=<name> serves the recipe for one linter, named as detect reports it. An unknown name falls back to the generic recipe. ast-grep, vale, prettier, black, and anonymous are refused, with a pointer to what to use instead. The route paragraphs now name the --tool command.

Five recipes:

Tool What it always checks first How it confirms a rule exists in the installed version
Ruff banned-api (TID251) web search of the rules page, then ruff rule; preview-only rules are flagged
ESLint core rules, with no-restricted-syntax / no-restricted-imports and their @typescript-eslint/ versions as catch-alls; then eight curated plugins with docs links the plugin's docs for the installed version; notes the ESLint 10 peer gap and import-x
Biome noRestrictedImports / noRestrictedGlobals web search of the rules page, then biome explain
pylint deprecated-modules, preferred-modules, bad-functions --list-msgs / --help-msg, loading extensions with --load-plugins first
flake8 banned-modules (flake8-tidy-imports, I251) no discovery step

None of the recipes teach writing custom rules. An agent writes a new one only by copying the repository's own examples, as #577 describes.

Vendor contract. test/legacy-linter-vendor-contract.test.ts runs each linter at a pinned version and checks every behavior the recipes describe:

  • preview status in ruff rule JSON
  • TID251
  • --help-msg failing until the extension is loaded
  • I251
  • biome explain for both catch-all rules
  • unknown-rule failures

It downloads packages, so it only runs when TASKLESS_LEGACY_LINTER_CONTRACT=1 is set. That variable is set by a new Legacy linter contract workflow, which runs on changes to the legacy recipes and is kept out of Validate so a registry outage cannot block merges. All 10 cases pass locally.

The archive check passed: every scenario in cli-rule-routing, cli-agent, cli-detect, and cli-onboard survives, except "Both local paths viable surfaces a choice", which the change removes on purpose. openspec validate --all --strict passes.

Black support is a follow-up in #576.

@theCodeDrift
theCodeDrift added this pull request to stack #579 October 23, 2026 01:22
@theCodeDrift theCodeDrift changed the title legacy linter tools feat(agent): per-tool create-legacy-rule recipes with --tool Oct 23, 2026
@theCodeDrift

Copy link
Copy Markdown
Member Author

@claude /review

@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @theCodeDrift's task in 3m 32s —— View job


Review complete

Review mode: incremental — read 0 prior review thread(s) before reviewing. .prior-review.json contained no prior review threads, reviews, or top-level comments on this PR, so this is the first review and the whole diff was assessed fresh.

  • Read .prior-review.json (empty — nothing to reconcile)
  • Fetch PR diff and metadata
  • Review openspec changes (spec deltas, archive)
  • Review packages/cli/src/commands/agent.ts and packages/cli/src/prompts/recipes.ts
  • Review new recipe files (create-legacy-rule.*.md)
  • Review new/changed tests
  • Review new CI workflow file
  • Post final summary

Assessment

This is a clean, well-documented PR. The --tool resolution mechanism (packages/cli/src/commands/agent.ts, packages/cli/src/prompts/recipes.ts) reuses the existing .anonymous variant mechanism cleanly, the positional-arg parser correctly treats --tool/--tool= as a value flag either way, and the five new recipes are consistent with the archived spec's requirements (catch-alls checked first, discovery confirmed against the installed version, no rule-authoring catalog, five-rule threshold gating new rule authoring). The vendor contract test (legacy-linter-vendor-contract.test.ts) and its dedicated, opt-in workflow are a solid way to keep the recipes' version-specific claims honest without risking Validate on a registry outage, and the pinned versions (Ruff 0.17.2, pylint 4.1.3, flake8 7.4.1, Biome 2.6.2) line up with design.md's research record. The route/cli-rule-routing spec changes (legacy ladder, per-linter paragraphs, removing the "both local paths viable" choice) are mirrored correctly in legacyLintersBlock and covered by legacy-ladder.test.ts / prompts.test.ts.

Two minor, non-blocking items posted inline:

  • packages/cli/src/prompts/recipes.ts:88 — toolVariants() is exported but appears to be dead code: no caller in agent.ts (which uses its own hardcoded REFUSED_TOOLS map instead), not re-exported from the public prompts/index.ts entry, and no test references it.
  • packages/cli/src/commands/agent.ts:269 — the --tool refusal check fires before the topic is validated and for any topic, not just create-legacy-rule, so an unrelated/unknown topic combined with a refused tool name prints the "has no legacy recipe" message instead of "Unknown command". This matches the topic-agnostic wording the updated cli-agent spec gives the refusal rule, so it may well be intentional — flagging in case it isn't.

No correctness, security, or test-adequacy issues found beyond those two nits. The ESLint recipe's version-specific claims (ESLint 10 removing .eslintrc*, specific plugins' peer-range gaps) are the one tool not covered by the automated vendor contract — but design.md's Risks section already calls this out explicitly as an accepted trade-off, so it isn't a new finding.

Comment thread packages/cli/src/prompts/recipes.ts
Comment thread packages/cli/src/commands/agent.ts
@theCodeDrift

Copy link
Copy Markdown
Member Author

Re: @github-actions — "Claude finished @theCodeDrift's task in 3m 32s — Review complete…"
#578 (comment)

Thanks. I fixed both inline items in e5138cf. --tool refusal now applies only to topics that have tool variants, which gives toolVariants() its caller. The ESLint gap in the contract test is the accepted trade-off already noted in design.md, so I left it as is.

— AI Coding Agent

Adds Ruff, ESLint, Biome, pylint, and flake8 recipes, makes route name
the --tool command for each detected linter, and pins the recipes' claims
in an opt-in vendor contract test with its own workflow.
An unknown topic now reports itself as unknown instead of the legacy-recipe
refusal, and a topic without variants ignores --tool as the resolution order
already says. The check uses toolVariants(), which had no caller.

This branch has not been deployed

No deployments
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.

1 participant