Skip to content

chore(ast-grep): upgrade to ast-grep 0.51.0 - #545

Merged
theCodeDrift merged 3 commits into
mainfrom
vendor/ast-grep/upgrade
Oct 20, 2026
Merged

theCodeDrift merged 3 commits into
mainfrom
vendor/ast-grep/upgrade

Conversation

@github-actions

@github-actions github-actions Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Stack (root → tip):

Upstream ast-grep is ahead of the version this repository pins.

This moves every @ast-grep/cli* pin in packages/cli/package.json
from 0.45.3 to 0.51.0 and regenerates
pnpm-lock.yaml. The pins move together on purpose: the platform
packages are selected by optional dependency, so a straggler left at
the old version is a different ast-grep on one platform than on the
others. sg-detect.cjs fails the run rather than open a partial bump.

The rewrite replaces a version string in pins it can already
enumerate and refuses if the count does not match, so it cannot add a
dependency or reformat the file. It also moves AST_GREP_VERSION in
capabilities.ts and refetches src/generated/ast-grep-rule-schema.json
for the 0.51.0 tag, and the workflow ran
engine-version-consistency against the result before pushing. What
this does not carry is judgment: if ast-grep-vendor-contract
is red on this pull request, ast-grep 0.51.0 added a language
or changed a measured behaviour, and that edit, the update ledger,
and the release note's prose belong in a child pull request.

This pull request rolls: if upstream releases again before it merges,
the branch, title, and body are rewritten to the newer version rather
than a second pull request being opened. Push a commit to the branch
and that stops — the workflow will not force-push over a commit it did
not write.


Upstream release notes — 0.51.0

https://github.com/ast-grep/ast-grep/releases/tag/0.51.0

  • fix(cli): use ast-grep name in project setup hint #3001
  • fix: replace deprecated serde_yaml with serde-saphyr #2999
  • fix(deps): update rust crate tree-sitter-haskell to 0.24.0 #2988
  • fix(cli): preserve files when update-all writes fail #2995
  • Revise security policy for clarity and updates a7d3f69
  • fix: update wasm linting ea55463

Contains #548

#545 moves the @ast-grep/cli* pins to 0.51.0. This is what makes that bump correct, plus the deterministic rule migration it turned out to need.

What the pin bump alone left red

Measured, and not caught by any test

20 YAML edge cases were run against both binaries (duplicate keys, anchors, merge keys, tags, null relations, multi-document files, YAML 1.1 booleans, unknown keys, and others). Only error wording changed, except for one:

case 0.45.3 0.51.0
rule file ending in a stray ---, beside a second rule exit 8, neither rule reports exit 1, both rules report

That is a relaxation check users will see, so it is pinned under scan exit codes.

tree-sitter-haskell 0.23.1 → 0.24.1 renamed one kind, type_synomym → type_synonym (upstream fix!: typo). Diffed across both grammars' node-types.json: of 187 named kinds this is the only one that changed, and no field changed. A rule on the old spelling exits 8, which takes every sg rule in check down with it. Pinned both ways in a new Haskell names a type synonym block.

The rest needs nothing: the custom-language opt-in and trust policy (#2960, #2971, #2972) is unreachable because we assemble our own config with no customLanguages; null relation fields are refused identically on both versions, and our schema rejects them first; the vendored rule schema changed only its provenance comment; outline output is not exposed.

Rule migrations, keyed on engine version rather than on the scaffold

The Haskell rename is mechanical, so it is applied rather than handed to an agent. It is not a scaffold migration: 0011 already shipped, so a 0012 would move every project's scaffold to 12 and make every pinned CLI refuse its project with SCAFFOLD_VERSION_MISMATCH, for a typo that affects almost no one.

Instead, update --rules applies rule migrations keyed on rules.engines, the engine versions the rules were last reconciled against, which it records and nothing else does:

  • A migration runs when the recorded engine version is below its engineVersion and the bundled engine is at or above it; a project with no recorded engines gets all of them. Each engine is keyed on its own version.
  • They run before the stamp. A failure part-way leaves the marker where it was, and every migration is idempotent, so a rerun finishes.
  • update --json lists what is pending (ruleMigrations); update --rules names every file it wrote (migrated).
  • update --rules now refuses with SCAFFOLD_MIGRATION_REQUIRED when the layout is behind, so a rewrite never writes into an old layout.

The contract each migration must meet is stated in rule-migrate.ts: idempotent, edit bytes in place rather than parse and re-serialize, and rewrite only shapes the service never issued, or has already migrated in its own corpus. A rewritten issued rule reads as "edited since Taskless issued it", and rule restore writes the old bytes straight back; accepting other bytes client-side would be a signature bypass. The service has issued no rule naming type_synomym, confirmed on the Cloud side.

The Haskell migration touches only rules whose language resolves to Haskell (the hs alias included), rewrites only kind: values (block, flow, quoted, list item), and never reads .tests/. Vale migrations for #546 add one file and one registry line.

Ledger and changeset

update.md gains a Migrating to 0.13.3 section telling agents not to rename by hand, step 5 explains the rewrites, and the error table lists the new refusal. Topic v17 → v18. The changeset is grown in place, still patch.

Typecheck, lint, and the full CLI suite pass: 136 files, 2487 tests.

Stacked on #545. Merges down into vendor/ast-grep/upgrade.

Refs #545

@theCodeDrift

Copy link
Copy Markdown
Member

@claude /review

@github-actions

github-actions Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor Author

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


Review complete

  • Read .prior-review.json (no prior threads/reviews found — this is the first review)
  • Fetch PR diff and changed files
  • Review each changed file for correctness, security, performance, test adequacy, clarity
  • Post inline comments for concrete issues
  • Post final summary

Review mode: incremental — read 0 prior review thread(s) before reviewing. .prior-review.json held no reviewThreads, reviews, or top-level comments beyond the trigger comment itself, so this is the first review of this PR; everything below is assessed fresh against the whole diff.

Summary

This PR bumps the vendored ast-grep pin to 0.51.0 and lands the follow-on work needed to make that land green: a Zig language addition, a measured check exit-code relaxation, a re-pinned severity error string, and a new rule-migration framework (rule-migrate.ts + the Haskell type_synomym → type_synonym rewrite) keyed on rules.engines rather than the .taskless/ scaffold version. The design is careful about ordering (migrations run before the reconciliation stamp, so a partial failure leaves the marker unmoved and a rerun is idempotent) and about not touching service-issued rule signatures. Test coverage is thorough: the migration's regex is exercised against block/flow/quoted/list-item kind: shapes and look-alike prose, pendingRuleMigrations is tested for ordering and engine-keying, and the end-to-end CLI path (update --json, update --rules, the SCAFFOLD_MIGRATION_REQUIRED refusal) is covered in reconcile-marker.test.ts.

Two findings, both posted inline:

  1. [New] packages/cli/src/rules/rule-migrations/sg-0-50-0-haskell-type-synonym.ts (around line 56-61): the catch around the top-level readdir(engineDirectory, ...) swallows every error as "no sg rules tree, nothing to rewrite," not just ENOENT. A permission or transient I/O error on that directory would be silently treated as "nothing to migrate," and update --rules would then stamp the project's rules.engines.sg as fully migrated even though the Haskell rename never ran — with no error surfaced. Worth narrowing to the ENOENT case specifically, consistent with how the inner per-file readdir/readFile calls in the same function are left uncaught (and correctly fail loudly).

  2. [New] packages/cli/src/agent/update.md:498 (and the matching literal pinned in test/reconcile-marker.test.ts's "carries the 0.13.3 ledger entry" test): the new section is titled "Migrating to 0.13.3," but the repo currently has 5 other pending patch changesets queued against the released 0.13.1, plus this PR's own patch changeset. Changesets collapses same-level pending changesets into one version bump at release, so if all 6 ship together the next version is 0.13.2, not 0.13.3. Worth confirming the intended release sequencing before merge, since this is user/agent-facing documentation that would otherwise name a version the installed CLI never reports.

Everything else — the pin bump itself, the capabilities/schema updates, the vendor-contract test additions for the Haskell rename, the trailing---- relaxation, and the severity-wording re-pin — reads correct and consistent with the measured behavior described in the PR body.

Comment thread packages/cli/src/agent/update.md
github-actions Bot and others added 3 commits October 20, 2026 16:34
The pin bump alone left two contract pins red. ast-grep 0.50.0 added Zig,
so AST_GREP_LANGUAGES gains it. 0.51.0 replaced serde_yaml with
serde-saphyr, which drops the backticks from the severity error; the pin
records the new wording. No Taskless user reads that text, since validate
rejects the severity against the schema first.

Two measured changes no test caught are now pinned. A rule file ending in
a stray `---` loads on 0.51.0, where 0.45.3 exited 8 and took every sg
rule in the config down with it. And tree-sitter-haskell 0.24.1 renamed
the kind `type_synomym` to `type_synonym`, the only kind or field that
changed across the two grammars' node-types.json; a rule on the old
spelling now exits 8.

That rename is fixed deterministically. `update --rules` now applies rule
migrations keyed on the engine versions the rules were last reconciled
against (`rules.engines`), not on the scaffold version, so a rewrite that
touches a handful of projects never bumps every project's scaffold and
breaks pinned CLIs in CI. Migrations run before the stamp, are idempotent,
edit bytes in place, and must only rewrite shapes the service never
issued; the service has issued no rule on the old Haskell kind.
`update --json` lists pending migrations, and `update --rules` refuses
with SCAFFOLD_MIGRATION_REQUIRED when the layout is behind. The ledger
gains a 0.13.3 entry and the update topic moves to v18.
Only a missing sg rules tree means there is nothing to rewrite. Any other
readdir error was read the same way, and the stamp that follows would have
recorded the rename as applied with no later run retrying it.
@theCodeDrift
theCodeDrift force-pushed the vendor/ast-grep/upgrade branch from 405297a to 5471e2e Compare October 20, 2026 23:35
@theCodeDrift

Copy link
Copy Markdown
Member

Re: @github-actions[bot]: "Claude finished @theCodeDrift's task in 3m 58s"
#545 (comment)

Both findings are handled in 5471e2e. The readdir catch now passes only ENOENT and rethrows everything else, with a test. The 0.13.3 heading was correct: the branch had been sitting two commits behind main, which had already released 0.13.2. It is now rebased, so this PR's changeset is the only one pending.

— AI Coding Agent

@theCodeDrift
theCodeDrift merged commit db89879 into main Oct 20, 2026
6 checks passed
@theCodeDrift
theCodeDrift deleted the vendor/ast-grep/upgrade branch October 20, 2026 23:49
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