fix(cli): give every ast-grep rule a .tests/ so one rule can't fail them all - #154
Open
thecodedrift wants to merge 1 commit into
Open
fix(cli): give every ast-grep rule a .tests/ so one rule can't fail them all#154thecodedrift wants to merge 1 commit into
thecodedrift wants to merge 1 commit into
Conversation
…hem all Migration 0005 created a rule's `.tests/` only as a side effect of moving a test file into it, so an sg rule with no test at version 3 — or one whose filename lacked the `-YYYYMMDD-test.yml` suffix the migration attributes by — arrived at version 5 with no tests directory. Assembly named it in `testConfigs` anyway, and ast-grep 0.41.0 aborts the whole invocation on a `testDir` it cannot read (exit 6), which `--filter` does not scope away: `taskless test` on any rule died naming a rule the author never touched. 0005 now ensures `rules/sg/<id>/.tests/`, gitkeeped when empty so the repair survives a commit. `assembleSgConfig` separately omits a `testDir` that is not on disk, which rescues a project a nightly already stamped at version 5 — those never re-run the migration — and a rule directory made by hand. Neither turns a missing test into a pass; verify and test still report it. Fixes #150 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Cyga14bww8rmazH2XrF8ms
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Migration
0005creates a rule's.tests/only as a side effect of moving a test file into it.moveEngineRulescreatesrules/sg/<id>/and never touches.tests/;moveEngineTestscreates it only for a filename matching^(?<id>.+?)-\d{8}-test\.ya?ml$and skips anything else. So an sg rule that had no test file at version 3 — or one whose name lacked the timestamp — arrives at version 5 with no.tests/.assembleSgConfigthen emitted atestDirfor every rule id with no existence check, and ast-grep 0.41.0 treats a missingtestDiras fatal to the whole invocation:--filterdoes not scope it. The sharpest form, on an already-migrated project, is thattestscoped to a healthy rule still fails, naming the broken one:That is why adding a correct new rule does not help — the error names a rule the author never touched. sg-specific: Vale reads a missing tests directory as empty.
Fixes #150
Two parts, and the second is not redundant
0005now gives everyrules/sg/<id>/a.tests/, with a.gitkeepwhen it would otherwise be empty. Amended in place rather than added as0006:0004/0005are unreleased (stable is0.10.2; only@taskless/cli-nightlycarries them), and amending an unreleased migration is the established pattern here —0004was amended twice this way under.changeset/idempotent-rules-partition.md. The.gitkeepis committed, not gitignored: git does not track empty directories, so without it a repaired.tests/never reaches CI or a fresh clone. A test pins that.taskless/.gitignorenever mentions it.assembleSgConfignow omits atestDirthat is not on disk. This is what actually rescues an already-broken install:runMigrationsshort-circuits once the manifest reads version 5, so a project a nightly already stamped never re-runs the amended migration. It also covers a hand-mademkdir .taskless/rules/sg/<id>/on any version. Nothing becomes a silent pass —verifystill reports "No test file found" andteststill reports "Skipped: no test file found", both of which read the rule directory rather than this config.Measured against ast-grep 0.41.0
Empty
.tests/→sg testandsg scanboth exit 0. A.tests/.gitkeep→ both exit 0, and the.gitkeepis not picked up as a test case (Running 1 testsfor the tree, not 2).testConfigs:with no entries →Running 0 tests, exit 0, andcheckstill reports findings normally. The assembler emits exactly that bare-key form when nothing qualifies.The ingest path was deliberately left alone
commands/rules.tsgateswriteRuleTestFilebehindif (rule.tests), so an API-delivered rule with no tests also produces a rule directory with no.tests/. Not fixed there: the assemble check already makes any such directory harmless however it arose, so an ingest-sidemkdirwould be a third enforcement point for one invariant and the weakest of the three — it covers only rules arriving throughcreate/improve, and would not have fixed this bug, since those directories are already missing on disk. A rule delivered with no tests is precisely whatverifyexists to flag, and the nextimprove-ruleround-trip creates the directory by writing a real test into it.The migration is different, and does warrant its own fix: it is a one-shot transformation whose output the user then commits, with nothing coming back later to repair it.
Two sibling gaps, verified and not fixed here
Both reproduce, but neither is silent, which is the material difference:
.vale.ini— written only for matchers carrying atskl) rule = <id>breadcrumb, so a hand-written matcher yields an effectively disabled rule. But the migration prints a notice, leaves the orphan file, andverifysays "has no.vale.ini, so nothing scopes it and it will never run." Fixing it means inventing an attribution heuristic for hand-written matchers, which0005's docblock explicitly declines to guess at.captures/— created only when there is a loose*.ymlto move.verifyreports it, and nothing hard-fails, so no other rule is affected. This is the closest analogue to Possible Missing Test Migrations #150 and worth a follow-up; it is a two-line change to the same helper.Tests
assemble.test.ts— atestDirabsent from disk is omitted; the general invariant that every emittedtestDirexists (parsed andstated); and a config is still emitted when no rule has a tests directory, asserting the exact baretestConfigs:form measured as accepted.migrate-engine-layout.test.ts— the existing fixture already reproduced this bug via its untimestampedrule-tests/no-eval-test.yml, with a comment at:107noting0005"cannot attribute it and leaves it behind". It now asserts everyrules/sg/*/has a.tests/, thatno-evalgot a.gitkeep, and that a.gitkeepnever lands beside a real test file.