Skip to content

feat(migration): auto-migration rename detection#2112

Merged
bpamiri merged 25 commits into
developfrom
peter/auto-migration-rename-detection
Apr 15, 2026
Merged

feat(migration): auto-migration rename detection#2112
bpamiri merged 25 commits into
developfrom
peter/auto-migration-rename-detection

Conversation

@bpamiri
Copy link
Copy Markdown
Collaborator

@bpamiri bpamiri commented Apr 15, 2026

Summary

Closes item 6 ("Migration rename detection") from docs/wheels-vs-frameworks.md "Where Wheels Trails" list — promoting it to "Where Wheels Leads" as item 16.

AutoMigrator.diff() now detects column renames via two complementary mechanisms:

  • Explicit hintsoptions.renames = {"oldCol": "newCol"} is authoritative. Invalid hints raise typed exceptions (Wheels.InvalidRenameHint, Wheels.RenameHintTypeMismatch, Wheels.DuplicateRenameHint).
  • Heuristic suggestions — normalized-token match + Levenshtein scoring. Unambiguous score-1.0 pairs auto-confirm as renames (e.g., full_namefullName); everything else surfaces as suggestedRenames requiring explicit confirmation. Ambiguity is pre-counted across the full candidate pool before greedy assignment, so ambiguous score-1.0 pairs are correctly demoted to suggestions rather than silently applied.

What's included

  • Core engine: new vendor/wheels/migrator/RenameDetector.cfc (pure logic, no DB/model deps, 275 lines, 39 unit specs)
  • AutoMigrator integration: diff(modelName, options) and diffAll(options) accept hints and threshold; generateMigrationCFC() emits renameColumn() in up() (first) and reversed renameColumn() in down() (last). Backward compatible — zero breaking changes.
  • CLI: new wheels dbmigrate diff [modelName] command with --rename=OLD:NEW, --threshold, --write, --name flags. For diffAll, use --rename=User.old_col:newCol.
  • MCP: wheels_migrate(action="diff") handler; returns full JSON envelope with renameColumns, suggestedRenames, addColumns, removeColumns, changeColumns.
  • Docs: moved item 6 from Trails → Leads in wheels-vs-frameworks.md, new dbmigrate-diff.md CLI reference, auto-migration sections in migrations.md CLI guide and the migrations tutorial README.md, CLAUDE.md quick reference.

Spec & plan

  • Spec: docs/superpowers/specs/2026-04-15-auto-migration-rename-detection-design.md
  • Plan: docs/superpowers/plans/2026-04-15-auto-migration-rename-detection.md

Stats

  • 23 commits, 12 files changed, +1548/-14 lines
  • 49 new test specs (39 RenameDetector + 10 AutoMigrator integration)
  • Full suite on Lucee 7 + SQLite: 2948 pass / 0 fail / 0 error

Intentional behavioral change

A model rename that previously produced removeColumn + addColumn (with data loss) will now produce renameColumn when the heuristic confirms it at score 1.0 unambiguously (e.g., case/underscore variants of the same tokens). This is the desired fix; noted here so anyone regenerating migrations is aware.

Test Plan

  • Full Lucee 7 + SQLite test suite: 2948 pass / 0 fail / 0 error
  • Manual CLI bridge smoke test (command=diff for both single-model and diffAll)
  • Manual MCP smoke test (wheels_migrate action="diff")
  • Adobe CF 2023 bridge smoke test — success: true confirms code loads and executes on Adobe flavor
  • Adobe CF 2025 full test suite — deferred. The container has a pre-existing graphqlclient package gap that blocks the suite runner. Not introduced by this branch. Recommend fixing that container setup separately and then running: docker compose up -d adobe2025 && curl -s "http://localhost:62025/wheels/core/tests?db=sqlite&format=json&directory=wheels.tests.specs.migrator"
  • Cross-database matrix — tested on SQLite only. Worth an Actions run on MySQL/PostgreSQL/MSSQL before merge.
  • MCP tool schema follow-up — the migrate tool's declared argument list only lists action; diff-specific params (modelName, hints, heuristicThreshold, write) are not in the schema. They work because we dispatch internally, but AI agents can't discover them from tool introspection. Fast-follow PR suggested.

🤖 Generated with Claude Code

bpamiri and others added 25 commits April 15, 2026 09:26
Design for closing item 6 (migration rename detection) from
docs/wheels-vs-frameworks.md "Where Wheels Trails". Covers explicit hints,
heuristic suggestions (normalized-token + Levenshtein), CLI command,
MCP integration, testing strategy, and documentation updates.

Targets v4.0.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
21-task TDD implementation plan for auto-migration rename detection.
Covers RenameDetector engine, AutoMigrator integration, CLI command,
MCP handler, and documentation. Targets v4.0.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Duplicate() is a deep copy in CFML; the prior comment said shallow.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds `case "diff":` to the CLI bridge switch statement in cli.cfm.
Wraps AutoMigrator.diff() and diffAll() with URL parameter parsing
for hints, threshold, write, and name options.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Downstream consumers (CLI and MCP) need the exception type to
distinguish error classes (InvalidRenameHint vs RenameHintTypeMismatch etc).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Matches the convention used by all other dbmigrate CLI commands
(info, latest, up, down, exec, reset, etc) for CI/scripting use.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Matches the encoding pattern used for every other parameter.
Low-risk but defense-in-depth.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The MCP spec defines diffAll input hints as model-keyed:
{"User": {"renames": {...}}, "Post": {...}}

But AutoMigrator.diffAll reads options.hints, so the MCP handler
must wrap model-keyed hints in {hints: ...} before forwarding to
the CLI bridge. Single-model path already uses the flat
{renames: {...}} shape that diff() reads directly.

Without this fix, MCP diffAll with hints silently ran the heuristic
without applying the hints.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@bpamiri bpamiri merged commit e64af7c into develop Apr 15, 2026
5 checks passed
@bpamiri bpamiri deleted the peter/auto-migration-rename-detection branch April 15, 2026 18:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant