Skip to content

feat(res-to-affine): Phase-1 migration assistant skeleton (Refs #57)#314

Merged
hyperpolymath merged 1 commit into
mainfrom
feat-57-res-to-affine-tree-sitter
May 20, 2026
Merged

feat(res-to-affine): Phase-1 migration assistant skeleton (Refs #57)#314
hyperpolymath merged 1 commit into
mainfrom
feat-57-res-to-affine-tree-sitter

Conversation

@hyperpolymath
Copy link
Copy Markdown
Owner

Summary

Lands the .res→.affine migration assistant proposed in #57 as a tight Phase-1 cut:

  • tools/res-to-affine/ — OCaml CLI built by the repo's dune toolchain. Text-scans a .res source for 4 of the 6 anti-patterns from idaptik's Wave 3 pilot (side-effect imports, %raw blocks, untyped exceptions / Promise.catch, mutable globals via :=) and emits a .affine skeleton with migration markers + the quoted original at the bottom for reference.
  • editors/tree-sitter-rescript/ — manifest-only vendoring of the canonical rescript-lang/tree-sitter-rescript grammar, pinned at commit 990214a (v6.0.0, MIT). Not used yet — wired up in Phase 2.
  • docs/MIGRATION-ASSISTANT.adoc — architectural decision: canonical grammar, three-phase plan, alternatives considered.
  • tools/res-to-affine/test/ — alcotest snapshot suite. Synthetic fixture exercises all four Phase-1 patterns; spot-checked against gitbot-fleet/bots/sustainabot/bot-integration/src/*.res (correctly finds 0 in Config.res/Webhook.res/Oikos.res, 2 in Main.res, 3 in GitHubAPI.res).

This PR uses Refs not Closes for #57 — the proposal is multi-phase and this is Phase 1 only. Phase 2 (tree-sitter AST walker) and Phase 3 (partial translation of pure-structural forms) are scoped in tools/res-to-affine/README.md and the ADR.

Cross-references: hyperpolymath/gitbot-fleet#148 (consumer — 2,133 LOC ReScript subtree blocked on #57).

Why Phase-1 is text-scan, not tree-sitter

The user picked tree-sitter integration as the long-run direction (right per LESSONS.md "tree-sitter as canonical grammar source"), but committing all of that in one PR would leave no shippable artefact behind if Phase 2 stalls. Phase 1 is deliberately small and useful in isolation:

  • Runs against any .res file today.
  • Detects 4 of 6 anti-patterns reliably (regex-based; the other two need real AST).
  • Same Emitter interface Phase 2 will use, so the swap is local.
  • Gates the architectural commitment behind something that already pays its way.

See docs/MIGRATION-ASSISTANT.adoc for the full rationale and the alternatives considered (bs-tools typed AST, hand-rolled OCaml parser, pattern-detector-only).

Test plan

  • dune build clean repo-wide (no regression to existing 80+ modules under lib/).
  • dune test tools/res-to-affine/test/ — 3/3 OK (snapshot, scanner kinds, module-name derivation).
  • Run against gitbot-fleet .res files end-to-end: scanner output matches expectations (clean files report 0, files with try/Js.Exn/Promise.catch report the right line numbers).
  • CI (this PR) — first run on push.

Files

docs/MIGRATION-ASSISTANT.adoc                          NEW  130 lines  ADR
editors/tree-sitter-rescript/README.md                 NEW   46 lines  vendoring manifest
editors/tree-sitter-rescript/package.json              NEW   17 lines
editors/tree-sitter-rescript/scripts/install.sh        NEW   38 lines  +x
tools/res-to-affine/README.md                          NEW  117 lines  usage + Phase plan
tools/res-to-affine/dune                               NEW   11 lines
tools/res-to-affine/main.ml                            NEW   77 lines  cmdliner CLI
tools/res-to-affine/scanner.{ml,mli}                   NEW  ~120 lines
tools/res-to-affine/emitter.{ml,mli}                   NEW  ~100 lines
tools/res-to-affine/test/{dune,test_emit.ml}           NEW   80 lines
tools/res-to-affine/test/fixtures/sample.res           NEW   30 lines
tools/res-to-affine/test/expected/sample.affine        NEW   60 lines  snapshot

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

Lands the .res→.affine migration assistant proposed in #57 as a tight
Phase-1 cut: an OCaml CLI under tools/res-to-affine/ that text-scans a
ReScript source for four of the six anti-patterns from idaptik's Wave 3
pilot (side-effect imports, %raw blocks, untyped exceptions / Promise.catch,
mutable globals via :=) and emits a .affine skeleton with migration
markers + the quoted original.

Architecture decision recorded in docs/MIGRATION-ASSISTANT.adoc:
canonical .res grammar is rescript-lang/tree-sitter-rescript, vendored
manifest-only under editors/tree-sitter-rescript/ (pinned commit
990214a, MIT, compatible with this repo's MPL-2.0). Phase 2 swaps the
text scanner for a tree-sitter AST walker; Phase 3 does partial
translation of pure-structural forms. See tools/res-to-affine/README.md
for the full plan and tools/res-to-affine/test/ for snapshot tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@hyperpolymath hyperpolymath marked this pull request as ready for review May 20, 2026 23:18
@hyperpolymath hyperpolymath merged commit 7e9f181 into main May 20, 2026
0 of 13 checks passed
@hyperpolymath hyperpolymath deleted the feat-57-res-to-affine-tree-sitter branch May 20, 2026 23: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.

1 participant