Skip to content

chore(deps): tree-sitter 0.25 upgrade readiness monitor with daily Dependabot#847

Merged
magyargergo merged 6 commits into
mainfrom
chore/tree-sitter-drift-monitoring
Apr 16, 2026
Merged

chore(deps): tree-sitter 0.25 upgrade readiness monitor with daily Dependabot#847
magyargergo merged 6 commits into
mainfrom
chore/tree-sitter-drift-monitoring

Conversation

@magyargergo

@magyargergo magyargergo commented Apr 15, 2026

Copy link
Copy Markdown
Collaborator

Replaces the original ABI drift check with an upgrade readiness dashboard that tracks when all 14 tree-sitter grammars publish versions with peer dependencies compatible with tree-sitter@0.25.0. Runs daily alongside Dependabot so we catch the moment the upgrade becomes safe.

Why

We want to upgrade tree-sitter from 0.21.x to 0.25.x (unlocking ABI 15 and 8 grammar upgrades). But 9 of 14 grammars currently have peer dependencies that won't resolve against 0.25 without --legacy-peer-deps:

Status Count Grammars
Ready (peer dep satisfies 0.25) 5 c-sharp, dart, go, javascript, python
Unreleased (ABI 15 or relaxed peer on main, not yet published) 4 c, cpp, php, rust
Blocking (no compatible version anywhere) 5 java, kotlin, ruby, swift, typescript

Dependabot will catch new grammar releases. The readiness script catches what Dependabot can't see: upstream unreleased work, vendored proto drift, and peer-dep compatibility.

What changed

Upgrade readiness script (.github/scripts/check-tree-sitter-upgrade-readiness.py)

Replaces the old check-tree-sitter-drift.py. Stdlib only, no external deps. Produces a Markdown report with:

  • Grammar compatibility table — for each grammar: npm latest version, peer dep range, whether it satisfies 0.25, installed ABI, upstream (main branch) ABI, and status (Ready / Unreleased / Blocking / Unknown)
  • Vendored proto drift — whether vendor/tree-sitter-proto/ is in sync with upstream, and whether the upstream ABI is within the target runtime range
  • Blocker summary — actionable list of what's blocking the upgrade

Security and reliability hardening (from code review):

  • Fetches npm metadata via registry HTTPS API (no CLI dependency)
  • Uses GITHUB_TOKEN for authenticated GitHub API calls (5000 req/hr vs 60)
  • npm fetch failures add a blocker instead of silently marking grammars as Ready
  • Report passed to github-script via env var, not ${{ }} interpolation (prevents script injection)
  • 8s per-call timeout (44 calls × 8s = 352s, well within 10 min workflow limit)
  • CRLF-safe vendored proto comparison
  • Blocker tracking via dict (no prefix collision risk)
  • TARGET_RUNTIME_MAJOR_MINOR derived from TARGET_RUNTIME with startup assertion

Workflow (.github/workflows/tree-sitter-upgrade-readiness.yml)

  • Daily at 09:00 UTC (was weekly)
  • On scheduled runs with blockers: opens/updates a tracking issue titled "Tree-sitter 0.25 upgrade readiness"
  • On scheduled runs that come back clean: closes the tracking issue automatically
  • On PRs touching the script/workflow: runs the check as informational (warning, not failure)
  • Uses actions/github-script@v9.0.0 with SHA pin

Dependabot (.github/dependabot.yml)

  • Daily npm checking for gitnexus/ (was weekly) — catch new grammar releases ASAP
  • Tree-sitter grammars grouped into single PR
  • Runtime (tree-sitter) and CLI (tree-sitter-cli) pinned until upgrade is ready

Current readiness (2026-04-16)

5/14 grammars ready. 9 blockers remaining. See the script output for the full report.

Test plan

  • Script runs locally, produces correct readiness report, exits 1 with 9 blockers
  • npm fetch failure path tested — adds blocker instead of false-green
  • GITHUB_TOKEN auth works when available, falls back gracefully without
  • dependabot.yml validates via GitHub's schema
  • Workflow YAML is valid (concurrency convention, SHA pins verified)
  • Scheduled workflow runs daily and opens/updates tracking issue
  • When all grammars publish compatible versions, tracking issue closes automatically

…ring

Two things Dependabot cannot see on its own:

1. ABI consistency. The tree-sitter runtime supports a known range of
   grammar ABIs. When a grammar bumps past that range, require() silently
   fails and fallback paths mask the regression in test coverage.
2. Vendored upstream drift. vendor/tree-sitter-proto is a snapshot of
   coder3101/tree-sitter-proto regenerated against a pinned cli version.
   Upstream keeps moving. Nothing notices until a maintainer remembers to
   look.

Dependabot configuration
- Added npm ecosystems for gitnexus, gitnexus-web, gitnexus-shared.
- Grouped all tree-sitter-* grammar bumps into one PR (ecosystem moves in
  lockstep, one PR per grammar is noise).
- Pinned the tree-sitter runtime itself. Bumping 0.21 to 0.22+ changes
  which grammar ABIs load and requires coordinated updates to the
  vendored proto grammar. That stays a deliberate human decision.
- Pinned tree-sitter-cli for the same reason (it controls which ABI
  vendor/tree-sitter-proto/src/parser.c emits when regenerated).

Drift check (.github/scripts/check-tree-sitter-drift.py)
- Reads the tree-sitter runtime version from gitnexus/package.json.
- Walks every installed tree-sitter-* grammar plus the vendored proto
  and reports its LANGUAGE_VERSION against the runtime's supported ABI
  range (table maintained in the script; extend when bumping runtime).
- Fetches coder3101/tree-sitter-proto main parser.c and compares byte
  for byte to the vendored copy. Reports the upstream HEAD short SHA
  and the upstream ABI so a maintainer can act.
- Prints a Markdown report; exits 0 when everything is in range and
  matches upstream, 1 otherwise.
- Stdlib only, no external deps.

Drift workflow (.github/workflows/tree-sitter-drift-check.yml)
- Runs weekly (Mondays 09:00 UTC) to match Dependabot's cadence.
- Also runs on PRs that touch the script or workflow itself, where it
  fails the PR check on drift so the drift gate cannot land broken.
- On scheduled runs with drift, opens or updates a single tracking
  issue labeled tree-sitter-drift. On scheduled runs that come back
  clean, closes the open tracking issue (if any) with a comment.
@vercel

vercel Bot commented Apr 15, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
gitnexus Ready Ready Preview, Comment Apr 16, 2026 8:05am

Request Review

@github-actions github-actions Bot added the chore Maintenance and housekeeping label Apr 15, 2026
@github-actions

github-actions Bot commented Apr 15, 2026

Copy link
Copy Markdown
Contributor

CI Report

All checks passed

Pipeline Status

Stage Status Details
✅ Typecheck success tsc --noEmit
✅ Tests success unit tests, 3 platforms
✅ E2E success gitnexus-web changes only

Test Results

Tests Passed Failed Skipped Duration
6354 6257 0 97 239s

✅ All 6257 tests passed

97 test(s) skipped — expand for details
  • Swift MethodExtractor > isTypeDeclaration > recognizes class_declaration
  • Swift MethodExtractor > isTypeDeclaration > recognizes protocol_declaration
  • Swift MethodExtractor > isTypeDeclaration > rejects import_declaration
  • Swift MethodExtractor > visibility > extracts public method
  • Swift MethodExtractor > visibility > extracts private method
  • Swift MethodExtractor > visibility > defaults to internal when no modifier
  • Swift MethodExtractor > protocol methods > marks protocol method as abstract
  • Swift MethodExtractor > static and class methods > detects static func as isStatic
  • Swift MethodExtractor > static and class methods > detects class func as isStatic
  • Swift MethodExtractor > parameters > extracts parameters with types and default values
  • Swift MethodExtractor > return type > extracts return type from -> annotation
  • Swift MethodExtractor > annotations > extracts @objc attribute
  • Swift MethodExtractor > isFinal > detects final func
  • Swift MethodExtractor > isFinal > is false when not final
  • Swift MethodExtractor > isAsync > detects async func
  • Swift MethodExtractor > isOverride > detects override method
  • buildTypeEnv > constructor inference (Tier 1 fallback) > lookupClassByName regression coverage > Swift lookupClassByName regression coverage > Swift cross-file constructor inference uses lookupClassByName
  • buildTypeEnv > constructor inference (Tier 1 fallback) > lookupClassByName regression coverage > Swift lookupClassByName regression coverage > Swift explicit init inference uses lookupClassByName
  • buildTypeEnv > constructor inference (Tier 1 fallback) > lookupClassByName regression coverage > Swift lookupClassByName regression coverage > Swift cross-file constructor inference does not bind plain functions
  • buildTypeEnv > known limitations (documented skip tests) > Ruby block parameter: users.each { |user| } — closure param inference, different feature
  • Swift constructor-inferred type resolution > detects User and Repo classes, both with save methods
  • Swift constructor-inferred type resolution > resolves user.save() to Models/User.swift via constructor-inferred type
  • Swift constructor-inferred type resolution > resolves repo.save() to Models/Repo.swift via constructor-inferred type
  • Swift constructor-inferred type resolution > emits exactly 2 save() CALLS edges (one per receiver type)
  • Swift self resolution > detects User and Repo classes, each with a save function
  • Swift self resolution > resolves self.save() inside User.process to User.save, not Repo.save
  • Swift parent resolution > detects BaseModel and User classes plus Serializable protocol
  • Swift parent resolution > emits EXTENDS edge: User → BaseModel
  • Swift parent resolution > emits IMPLEMENTS edge: User → Serializable (protocol conformance)
  • Swift cross-file User.init() inference > resolves user.save() via User.init(name:) inference
  • Swift cross-file User.init() inference > resolves user.greet() via User.init(name:) inference
  • Swift return type inference > detects User class and getUser function
  • Swift return type inference > detects save function on User (Swift class methods are Function nodes)
  • Swift return type inference > resolves user.save() to User#save via return type of getUser() -> User
  • Swift return-type inference via function return type > resolves user.save() to User#save via return type of getUser()
  • Swift return-type inference via function return type > user.save() does NOT resolve to Repo#save
  • Swift return-type inference via function return type > resolves repo.save() to Repo#save via return type of getRepo()
  • Swift implicit imports (cross-file visibility) > detects UserService class in Models.swift
  • Swift implicit imports (cross-file visibility) > resolves UserService() constructor call across files (no explicit import)
  • Swift implicit imports (cross-file visibility) > resolves service.fetchUser() member call across files
  • Swift implicit imports (cross-file visibility) > creates IMPORTS edges between files in the same module
  • Swift extension deduplication > detects Product class
  • Swift extension deduplication > resolves Product() constructor despite extension creating duplicate class node
  • Swift extension deduplication > resolves product.save() to Product.swift (primary definition)
  • Swift constructor call fallback (no new keyword) > resolves OCRService() as constructor call across files
  • Swift constructor call fallback (no new keyword) > resolves ocr.recognize() member call via constructor-inferred type
  • Swift export visibility (internal vs private) > resolves PublicService() constructor across files
  • Swift export visibility (internal vs private) > resolves internalHelper() across files (internal = module-scoped)
  • Swift if let / guard let binding resolution > detects User and Repo classes
  • Swift if let / guard let binding resolution > resolves user.save() inside if-let to User#save
  • Swift if let / guard let binding resolution > resolves repo.save() inside guard-let to Repo#save
  • Swift if let / guard let binding resolution > user.save() in if-let does NOT resolve to Repo#save
  • Swift await / try expression unwrapping > resolves user.save() via await fetchUser() return type
  • Swift await / try expression unwrapping > resolves repo.save() via try parseRepo() return type
  • Swift await / try expression unwrapping > detects fetchUser and parseRepo as functions
  • Swift for-in loop element type inference > detects User and Repo classes
  • Swift for-in loop element type inference > creates implicit import edges between files
  • Swift field-type resolution > detects classes and their properties
  • Swift field-type resolution > emits HAS_PROPERTY edges from class to field
  • Swift field-type resolution > resolves field-chain call user.address.save() → Address#save
  • Swift field-type resolution > emits ACCESSES edges for field reads in chains
  • Swift field-type resolution > populates field metadata (visibility, declaredType) on Property nodes
  • Swift call-result binding > resolves call-result-bound method call user.save() → User#save
  • Swift call-result binding > getUser() is present as a defined function
  • Swift call-result binding > emits processUser -> getUser CALLS edge for let-assigned free function call
  • Swift method enrichment > detects Animal protocol and Dog class
  • Swift method enrichment > emits IMPLEMENTS edge Dog -> Animal
  • Swift method enrichment > emits HAS_METHOD edges for Dog methods
  • Swift method enrichment > marks protocol Animal.speak as isAbstract
  • Swift method enrichment > marks Dog.speak as NOT isAbstract
  • Swift method enrichment > marks breathe as isFinal
  • Swift method enrichment > marks classify as isStatic
  • Swift method enrichment > captures @objc annotation on breathe
  • Swift method enrichment > populates parameterTypes for classify(_ name: String)
  • Swift method enrichment > records parameterCount for classify
  • Swift method enrichment > records returnType for speak
  • Swift method enrichment > resolves dog.speak() CALLS edge
  • Swift method enrichment > resolves Dog.classify("dog") CALLS edge
  • Swift abstract dispatch > detects Repository protocol and SqlRepository class
  • Swift abstract dispatch > emits IMPLEMENTS edge SqlRepository -> Repository
  • Swift abstract dispatch > emits HAS_METHOD edges for Repository.find and Repository.save
  • Swift abstract dispatch > emits HAS_METHOD edges for SqlRepository.find and SqlRepository.save
  • Swift abstract dispatch > marks base Repository.find as isAbstract
  • Swift abstract dispatch > marks base Repository.save as isAbstract
  • Swift abstract dispatch > marks concrete SqlRepository.find as NOT isAbstract
  • Swift abstract dispatch > resolves repo.find(id: 42) CALLS edge
  • Swift abstract dispatch > resolves repo.save(entity: user) CALLS edge
  • Swift abstract dispatch > populates parameterTypes for Repository.find
  • Swift abstract dispatch > populates parameterTypes for Repository.save
  • Swift abstract dispatch > records returnType for SqlRepository.find
  • Swift abstract dispatch > emits METHOD_IMPLEMENTS edges from SqlRepository methods → Repository protocol methods
  • Swift overloaded method disambiguation > detects 2 distinct find Method nodes on SqlRepository
  • Swift overloaded method disambiguation > emits METHOD_IMPLEMENTS edges for both find overloads
  • Swift overloaded method disambiguation > emits METHOD_IMPLEMENTS edge for save
  • Swift overloaded method disambiguation > emits exactly 3 METHOD_IMPLEMENTS edges total
  • Swift Child extends Parent — inherited method resolution (SM-9) > detects Parent and Child classes
  • Swift Child extends Parent — inherited method resolution (SM-9) > resolves c.parentMethod() to Parent.parentMethod via first-wins MRO walk

Code Coverage

Tests

Metric Coverage Covered Base Delta Status
Statements 73.23% 16993/23204 73.23% = 0.0 🟢 ██████████████░░░░░░
Branches 61.79% 10772/17433 61.81% 📉 -0.0 🔴 ████████████░░░░░░░░
Functions 78.92% 1595/2021 78.92% = 0.0 🟢 ███████████████░░░░░
Lines 75.65% 15438/20407 75.65% = 0.0 🟢 ███████████████░░░░░

📋 View full run · Generated by CI

…ness monitor

Replace the ABI drift pass/fail gate with a daily upgrade readiness
dashboard that tracks peer-dep compatibility of all 14 grammars with
tree-sitter@0.25.0 and reports which are ready, unreleased, or blocking.

Key changes:
- Rename drift-check → upgrade-readiness (script, workflow, job id)
- Fix P0: pass report via env var, not ${{ }} template interpolation
- Fix P1: npm fetch failure now adds a blocker instead of false-green
- Fix P1: pass GITHUB_TOKEN for authenticated GitHub API calls
- Switch Dependabot to daily for tree-sitter grammars
- Use dict for blockers (no prefix collision), derive TARGET_RUNTIME
  constant, reuse GRAMMARS parser_path, normalize CRLF in comparisons
- Reduce per-call HTTP timeout from 15s to 8s for workflow budget
- PR runs warn on blockers instead of hard-failing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@magyargergo magyargergo changed the title chore(deps): add tree-sitter aware Dependabot config and drift monitoring chore(deps): tree-sitter 0.25 upgrade readiness monitor with daily Dependabot Apr 16, 2026
The ci-global-upgrade.yml workflow tested npm global install upgrades
over a specific release candidate (1.6.2-rc.8). That RC has shipped
and the workflow is no longer needed. Remove it and all references
from ci.yml (needs, env vars, gate check).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Each daily run now posts a comment summarizing what changed before
updating the issue body. Comments include the ready/blocker counts
and a diff of grammar status changes (e.g. tree-sitter-cpp:
Unreleased -> Ready). Gives a timeline of how the upgrade unblocks.
@magyargergo magyargergo merged commit d024119 into main Apr 16, 2026
15 checks passed
@magyargergo magyargergo deleted the chore/tree-sitter-drift-monitoring branch April 16, 2026 08:17
@magyargergo magyargergo mentioned this pull request Apr 18, 2026
5 tasks
github714801013 pushed a commit to github714801013/GitNexus that referenced this pull request Apr 28, 2026
…pendabot (abhigyanpatwari#847)

* chore(deps): add tree-sitter aware Dependabot config and drift monitoring

Two things Dependabot cannot see on its own:

1. ABI consistency. The tree-sitter runtime supports a known range of
   grammar ABIs. When a grammar bumps past that range, require() silently
   fails and fallback paths mask the regression in test coverage.
2. Vendored upstream drift. vendor/tree-sitter-proto is a snapshot of
   coder3101/tree-sitter-proto regenerated against a pinned cli version.
   Upstream keeps moving. Nothing notices until a maintainer remembers to
   look.

Dependabot configuration
- Added npm ecosystems for gitnexus, gitnexus-web, gitnexus-shared.
- Grouped all tree-sitter-* grammar bumps into one PR (ecosystem moves in
  lockstep, one PR per grammar is noise).
- Pinned the tree-sitter runtime itself. Bumping 0.21 to 0.22+ changes
  which grammar ABIs load and requires coordinated updates to the
  vendored proto grammar. That stays a deliberate human decision.
- Pinned tree-sitter-cli for the same reason (it controls which ABI
  vendor/tree-sitter-proto/src/parser.c emits when regenerated).

Drift check (.github/scripts/check-tree-sitter-drift.py)
- Reads the tree-sitter runtime version from gitnexus/package.json.
- Walks every installed tree-sitter-* grammar plus the vendored proto
  and reports its LANGUAGE_VERSION against the runtime's supported ABI
  range (table maintained in the script; extend when bumping runtime).
- Fetches coder3101/tree-sitter-proto main parser.c and compares byte
  for byte to the vendored copy. Reports the upstream HEAD short SHA
  and the upstream ABI so a maintainer can act.
- Prints a Markdown report; exits 0 when everything is in range and
  matches upstream, 1 otherwise.
- Stdlib only, no external deps.

Drift workflow (.github/workflows/tree-sitter-drift-check.yml)
- Runs weekly (Mondays 09:00 UTC) to match Dependabot's cadence.
- Also runs on PRs that touch the script or workflow itself, where it
  fails the PR check on drift so the drift gate cannot land broken.
- On scheduled runs with drift, opens or updates a single tracking
  issue labeled tree-sitter-drift. On scheduled runs that come back
  clean, closes the open tracking issue (if any) with a comment.

* refactor(deps): rewrite drift check as tree-sitter 0.25 upgrade readiness monitor

Replace the ABI drift pass/fail gate with a daily upgrade readiness
dashboard that tracks peer-dep compatibility of all 14 grammars with
tree-sitter@0.25.0 and reports which are ready, unreleased, or blocking.

Key changes:
- Rename drift-check → upgrade-readiness (script, workflow, job id)
- Fix P0: pass report via env var, not ${{ }} template interpolation
- Fix P1: npm fetch failure now adds a blocker instead of false-green
- Fix P1: pass GITHUB_TOKEN for authenticated GitHub API calls
- Switch Dependabot to daily for tree-sitter grammars
- Use dict for blockers (no prefix collision), derive TARGET_RUNTIME
  constant, reuse GRAMMARS parser_path, normalize CRLF in comparisons
- Reduce per-call HTTP timeout from 15s to 8s for workflow budget
- PR runs warn on blockers instead of hard-failing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore(ci): remove global-upgrade smoke test workflow

The ci-global-upgrade.yml workflow tested npm global install upgrades
over a specific release candidate (1.6.2-rc.8). That RC has shipped
and the workflow is no longer needed. Remove it and all references
from ci.yml (needs, env vars, gate check).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(ci): add changelog comments to upgrade readiness tracking issue

Each daily run now posts a comment summarizing what changed before
updating the issue body. Comments include the ready/blocker counts
and a diff of grammar status changes (e.g. tree-sitter-cpp:
Unreleased -> Ready). Gives a timeline of how the upgrade unblocks.

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

chore Maintenance and housekeeping

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant