Skip to content

fix: Migration checks are faster and more reliable#1376

Merged
hatayama merged 16 commits into
v3-betafrom
feature/improve-migrate-performance
Jun 20, 2026
Merged

fix: Migration checks are faster and more reliable#1376
hatayama merged 16 commits into
v3-betafrom
feature/improve-migrate-performance

Conversation

@hatayama

@hatayama hatayama commented Jun 20, 2026

Copy link
Copy Markdown
Owner

Summary

  • Custom Tool Migration checks now avoid expensive full scans when a lightweight preflight can decide the result.
  • The migration wizard now shows clearer actions and appears when migration state has not been recorded yet.

User Impact

  • Opening or checking migration status is faster in projects that do not need a full migration scan.
  • Users no longer see unusable actions such as disabled migration or duplicate close buttons in common wizard states.
  • Projects with missing migration state or temporarily unreadable assembly sidecar files are handled more reliably.

Changes

  • Added a preflight migration scan path and reusable migration plan caching with project fingerprints.
  • Tightened cache invalidation so changed source, assembly definition, assembly reference, or asmdef meta files rebuild the plan safely.
  • Updated the migration wizard action layout for check, migrate, nothing-to-migrate, and close states.
  • Covered the new scan, cache, locked-file, and wizard behaviors with focused EditMode tests.

Verification

  • cli/dist/darwin-arm64/uloop compile --project-path "$(git rev-parse --show-toplevel)" passed with 0 errors and 0 warnings.
  • cli/dist/darwin-arm64/uloop run-tests --project-path "$(git rev-parse --show-toplevel)" --test-mode EditMode --filter-type regex --filter-value "ThirdPartyToolMigrationFileServiceTests" passed: 119 tests.
  • /Users/a12115/.codex/skills/codex-review/scripts/codex-review v3-beta completed clean with no accepted/actionable findings.

Review in cubic

hatayama added 15 commits June 20, 2026 12:21
Run auto-scan preflight before opening the migration wizard, cache validated preview plans for apply, and skip deeper source analysis when files have no migration candidate text.
Short-circuit startup migration checks with a fixed-text streaming pass before building the full project inventory, while preserving the existing full scan for ambiguous migration markers.
Treat ripgrep and managed migration preflight scans through the same strategy pipeline so startup checks can use rg when available while preserving the managed fallback and full-scan behavior for inconclusive matches.
Continue managed migration preflight scans past inconclusive marker text so later direct migration targets can short-circuit startup checks. Remove the ripgrep strategy because measurement showed the external search path did not reduce the real bottleneck.
Collapse the unused preflight strategy abstraction back into a single scanner now that ripgrep is no longer used. Keep the ambiguous-marker scan behavior covered by the existing migration tests.
Increase only the custom tool migration action button height so the primary migration action is easier to notice without changing the other setup wizard buttons.
Hide the migration action row before the first check because it cannot be used yet, and remove the footer Close button so the wizard only shows useful in-window actions.
Treat missing prior setup wizard state as eligible for V3 migration auto-scan so projects without UserSettings can still surface existing custom tool migration targets.
Keep the migration wizard focused on the available Migrate action by disabling the Check button once migration targets are already shown.
Swap the footer action from Check to Close when no migration targets are found so the wizard only offers the useful next action.
Include asmdef meta state in migration plan fingerprints so cached previews are discarded when asmref GUID resolution changes before apply.
Record migration plan fingerprints before preview source reads so cached plans are rejected when files change during plan creation.
Make preflight asmdef checks reuse the migration JSON reader so malformed legacy asmdefs do not abort startup detection before the full scanner can inspect the project.
Include candidate file content hashes in migration plan fingerprints so cached plans are rejected even when same-size edits preserve timestamps.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 16 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread Packages/src/Editor/Presentation/Setup/ThirdPartyToolMigrationWizardWindow.cs Outdated
Keep automatic migration scans from leaking session state when preflight work fails, and defer unreadable preflight files to the full scanner so locked project files do not break migration checks.
@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds a preflight scanner for fast migration target detection, introduces MigrationProjectFingerprint / MigrationFileFingerprint structs for plan cache validity, wires fingerprinting into MigrationPlan and ThirdPartyToolMigrationFileService to reuse cached plans, restructures the auto-scan wizard window pipeline into RunAutoScanAsync, fixes ShouldAutoScanThirdPartyToolMigration for empty prior-version state, and refactors the wizard UI button row.

Changes

V3 Third-Party Tool Migration Infrastructure

Layer / File(s) Summary
Project fingerprint model and MigrationPlan update
Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationModels.cs
Adds MigrationProjectFingerprint (per-inventory snapshot with ordinal-sorted file list) and MigrationFileFingerprint (existence, length, UTC ticks, streaming content hash) structs. Updates MigrationPlan constructor and Empty to carry a ProjectFingerprint property.
Preflight scanner for fast migration target detection
Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationPreflightScanner.cs, Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationProjectFileInventory.cs
Adds ThirdPartyToolMigrationPreflightScanner (static) with FindMigrationTargetAsync that traverses Assets, inspects .cs files for candidate markers then runs fast detection, and inspects .asmdef files for legacy name/reference markers. Returns MigrationTargetPreflightResult (NoTargets, HasTargets, NeedsFullScan). Widens ShouldExcludeDirectory to internal for reuse.
Migration plan caching in FileService and PlanBuilder
Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationFileService.cs, Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationPlanBuilder.cs
FileService adds a shared _migrationCacheLock, plan-cache fields, StoreCachedPlan, GetCurrentMigrationPlan* helpers (with fingerprint validation), and InvalidateMigrationCaches. Preview paths now store the plan; apply paths retrieve from cache or rebuild. PlanBuilder captures MigrationProjectFingerprint in Create/CreateAsync and passes it to MigrationPlan.
Early candidate filtering and preflight integration
Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationTargetScanner.cs, Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAssemblyUsageAnalyzer.cs, Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAssemblyUsageAsyncAnalyzer.cs
TargetScanner.HasMigrationTargetAsync now runs the preflight scanner first and returns immediately on NoTargets/HasTargets; the C# loop fast-returns on ContainsFastCSharpMigrationTarget and skips non-candidates before rule checks. Both assembly usage analyzers move ContainsMigrationCandidateText to immediately after file-read, before any directory or rule accumulation.
Auto-scan wizard window flow and setup scan decision
Packages/src/Editor/Presentation/Setup/ThirdPartyToolMigrationWizardWindow.cs, Packages/src/Editor/Presentation/Setup/SetupWizardWindow.cs
ShowWindowForAutoScan delegates to new RunAutoScanAsync, which orchestrates background target detection, main-thread switching, ShouldOpenWindowAfterAutoScan predicate, exception logging, and session-state consumption in a finally block. ShouldAutoScanThirdPartyToolMigration now returns true for empty lastSeenVersion when the current major version equals 3.
Migration wizard UI button row and CSS
Packages/src/Editor/Presentation/Setup/ThirdPartyToolMigrationWizardView.cs, Packages/src/Editor/Presentation/Setup/SetupWizardWindow.uss
ThirdPartyToolMigrationWizardView adds _migrationButtonRow field; constructor, Create, and all state-transition methods show/hide the row and toggle _refreshButton/_closeButton via ViewDataBinder.SetVisible. CreateMigrationButtonRow is a new helper; CreateMigrateButton accepts an existing container. Adds .setup-button--migration-action { height: 66px } to the stylesheet.
Tests for fingerprinting, preflight scanner, plan cache invalidation, and auto-scan
Assets/Tests/Editor/ThirdPartyToolMigrationFileServiceTests.cs, Assets/Tests/Editor/ThirdPartyToolMigrationWizardWindowTests.cs, Assets/Tests/Editor/SetupWizardWindowTests.cs
Adds fingerprint mismatch tests (source/content/addition/asmdef-meta/locked-sidecar), preflight scanner tests (no targets, legacy marker vs comment, asmdef ref/malformed, ambiguous files, locked), async plan-cache invalidation test, auto-scan wizard tests (ShouldOpenWindowAfterAutoScan, RunAutoScanAsync on success/exception), FileChangingProgress helper update, and SetupWizardWindowTests expectation corrections.

Sequence Diagram(s)

sequenceDiagram
  participant Editor as Unity Editor Startup
  participant SetupWizard as SetupWizardWindow
  participant WizardWindow as ThirdPartyToolMigrationWizardWindow
  participant PreflightScanner as ThirdPartyToolMigrationPreflightScanner
  participant TargetScanner as ThirdPartyToolMigrationTargetScanner
  participant FileService as ThirdPartyToolMigrationFileService
  participant PlanBuilder as ThirdPartyToolMigrationPlanBuilder

  Editor->>SetupWizard: ShouldAutoScanThirdPartyToolMigration(version, lastSeen)
  SetupWizard-->>Editor: true (major==3 or upgrade from <3)

  Editor->>WizardWindow: ShowWindowForAutoScan
  WizardWindow->>WizardWindow: RunAutoScanAsync
  WizardWindow->>TargetScanner: HasMigrationTargetAsync (Task.Run)
  TargetScanner->>PreflightScanner: FindMigrationTargetAsync
  PreflightScanner-->>TargetScanner: HasTargets / NoTargets / NeedsFullScan
  alt NoTargets
    TargetScanner-->>WizardWindow: false
  else HasTargets
    TargetScanner-->>WizardWindow: true
  else NeedsFullScan
    TargetScanner->>TargetScanner: full C# / asmdef scan
    TargetScanner-->>WizardWindow: bool
  end
  WizardWindow->>WizardWindow: SwitchToMainThreadForAutoScanAsync
  WizardWindow->>WizardWindow: ShouldOpenWindowAfterAutoScan
  alt hasMigrationTargets && !cancelled
    WizardWindow->>Editor: OpenWindowAfterAutoScan (refresh=true)
    Editor->>FileService: PreviewMigrationAsync
    FileService->>PlanBuilder: CreateAsync → CaptureFromInventory
    PlanBuilder-->>FileService: MigrationPlan(changes, count, fingerprint)
    FileService->>FileService: StoreCachedPlan
    Editor->>FileService: ApplyMigrationAsync
    FileService->>FileService: GetCurrentCachedPlan → fingerprint.Matches?
    alt fingerprint valid
      FileService->>FileService: apply cached plan
    else fingerprint stale
      FileService->>PlanBuilder: rebuild plan
    end
  end
  WizardWindow->>WizardWindow: ConsumeAutoScanSessionState (finally)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • hatayama/unity-cli-loop#1125: Directly related — both PRs modify when and how the migration wizard's auto-scan target check is triggered and scheduled at editor startup.
  • hatayama/unity-cli-loop#1181: Directly related — both PRs change SetupWizardWindow.ShouldAutoScanThirdPartyToolMigration logic and the session-state/wizard startup wiring that gates the V3 migration scan.
  • hatayama/unity-cli-loop#1374: Directly related — the retrieved PR overlaps on the same V3 migration infrastructure files (file service, plan builder, wizard window) and migration refactor scope as this PR.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main objective: improving performance and reliability of migration checks through preflight scanning and caching.
Description check ✅ Passed The description is well-detailed and directly related to the changeset, explaining the improvements, user impact, specific changes, and verification results.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/improve-migrate-performance

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAssemblyUsageAnalyzer.cs (1)

63-76: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Do not skip declared-type indexing for non-candidate files.

Moving the candidate guard above AddAssemblyScopedNames drops assembly type-name coverage and can misclassify rule matches in the second pass.

Suggested fix
-                if (!ThirdPartyToolMigrationRules.ContainsMigrationCandidateText(source))
-                {
-                    continue;
-                }
-
                 string assemblyDirectory = FindNearestAssemblyDirectory(
                     csharpFilePath,
                     asmdefDirectories,
                     assemblyReferenceDirectories,
                     projectRoot);
                 AddAssemblyScopedNames(
                     assemblyDeclaredTypeNamesByDirectory,
                     assemblyDirectory,
                     ThirdPartyToolMigrationRules.GetDeclaredTypeNames(source));
+                if (!ThirdPartyToolMigrationRules.ContainsMigrationCandidateText(source))
+                {
+                    continue;
+                }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAssemblyUsageAnalyzer.cs`
around lines 63 - 76, The candidate guard check using
ThirdPartyToolMigrationRules.ContainsMigrationCandidateText is currently
positioned before the AddAssemblyScopedNames call, which causes non-candidate
files to be skipped entirely and prevents their declared type names from being
indexed. Restructure the code so that AddAssemblyScopedNames is always called to
index declared type names regardless of whether the file is a migration
candidate, while keeping the candidate check guard around only the operations
that specifically depend on candidate status (like FindNearestAssemblyDirectory
if needed). This ensures complete assembly type-name coverage for the first pass
analysis.
Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAssemblyUsageAsyncAnalyzer.cs (1)

95-108: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Apply the same declared-type indexing order in async analyzer.

The early candidate continue now bypasses AddAssemblyScopedNames for non-candidate files, which can produce incomplete assembly type context for downstream per-assembly checks.

Suggested fix
-                if (!ThirdPartyToolMigrationRules.ContainsMigrationCandidateText(source))
-                {
-                    continue;
-                }
-
                 string assemblyDirectory = FindNearestAssemblyDirectory(
                     csharpFilePath,
                     asmdefDirectories,
                     assemblyReferenceDirectories,
                     projectRoot);
                 AddAssemblyScopedNames(
                     assemblyDeclaredTypeNamesByDirectory,
                     assemblyDirectory,
                     ThirdPartyToolMigrationRules.GetDeclaredTypeNames(source));
+                if (!ThirdPartyToolMigrationRules.ContainsMigrationCandidateText(source))
+                {
+                    continue;
+                }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAssemblyUsageAsyncAnalyzer.cs`
around lines 95 - 108, The early continue statement based on
ContainsMigrationCandidateText check bypasses the AddAssemblyScopedNames call
for non-candidate files, resulting in incomplete assembly type context. Move the
FindNearestAssemblyDirectory and AddAssemblyScopedNames calls in the
ThirdPartyToolMigrationAssemblyUsageAsyncAnalyzer to execute before the
candidate text check, ensuring that declared types are indexed for all files to
build complete assembly context, while keeping any migration-specific processing
conditional on the candidate check.
Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationTargetScanner.cs (1)

91-111: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve assembly declared-type collection before candidate short-circuit.

The early ContainsMigrationCandidateText continue now skips AddAssemblyScopedNames for non-candidate files, which can make assembly-level disambiguation incomplete and lead to incorrect target detection.

Suggested fix
-                if (!ThirdPartyToolMigrationRules.ContainsMigrationCandidateText(source))
-                {
-                    inspectedEntryCount++;
-                    if (inspectedEntryCount % ThirdPartyToolMigrationFileServiceConstants.PreviewYieldBatchSize == 0)
-                    {
-                        await Task.Yield();
-                    }
-
-                    continue;
-                }
-
                 string assemblyDirectory = FindNearestAssemblyDirectory(
                     csharpFilePath,
                     asmdefDirectories,
                     assemblyReferenceDirectories,
                     projectRoot);
                 string[] declaredTypeNames = ThirdPartyToolMigrationRules.GetDeclaredTypeNames(source);
                 AddAssemblyScopedNames(
                     assemblyDeclaredTypeNamesByDirectory,
                     assemblyDirectory,
                     declaredTypeNames);
+                if (!ThirdPartyToolMigrationRules.ContainsMigrationCandidateText(source))
+                {
+                    inspectedEntryCount++;
+                    if (inspectedEntryCount % ThirdPartyToolMigrationFileServiceConstants.PreviewYieldBatchSize == 0)
+                    {
+                        await Task.Yield();
+                    }
+
+                    continue;
+                }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationTargetScanner.cs`
around lines 91 - 111, The early continue statement when
ContainsMigrationCandidateText returns false causes AddAssemblyScopedNames to be
skipped for non-candidate files, making assembly-level type name collection
incomplete. Move the AddAssemblyScopedNames call (which populates
assemblyDeclaredTypeNamesByDirectory with the results from GetDeclaredTypeNames)
to execute before the ContainsMigrationCandidateText check, so that all files
contribute their declared type names to the assembly scope regardless of whether
they contain migration candidate text. This ensures complete disambiguation
information is available for target detection.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In
`@Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAssemblyUsageAnalyzer.cs`:
- Around line 63-76: The candidate guard check using
ThirdPartyToolMigrationRules.ContainsMigrationCandidateText is currently
positioned before the AddAssemblyScopedNames call, which causes non-candidate
files to be skipped entirely and prevents their declared type names from being
indexed. Restructure the code so that AddAssemblyScopedNames is always called to
index declared type names regardless of whether the file is a migration
candidate, while keeping the candidate check guard around only the operations
that specifically depend on candidate status (like FindNearestAssemblyDirectory
if needed). This ensures complete assembly type-name coverage for the first pass
analysis.

In
`@Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAssemblyUsageAsyncAnalyzer.cs`:
- Around line 95-108: The early continue statement based on
ContainsMigrationCandidateText check bypasses the AddAssemblyScopedNames call
for non-candidate files, resulting in incomplete assembly type context. Move the
FindNearestAssemblyDirectory and AddAssemblyScopedNames calls in the
ThirdPartyToolMigrationAssemblyUsageAsyncAnalyzer to execute before the
candidate text check, ensuring that declared types are indexed for all files to
build complete assembly context, while keeping any migration-specific processing
conditional on the candidate check.

In
`@Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationTargetScanner.cs`:
- Around line 91-111: The early continue statement when
ContainsMigrationCandidateText returns false causes AddAssemblyScopedNames to be
skipped for non-candidate files, making assembly-level type name collection
incomplete. Move the AddAssemblyScopedNames call (which populates
assemblyDeclaredTypeNamesByDirectory with the results from GetDeclaredTypeNames)
to execute before the ContainsMigrationCandidateText check, so that all files
contribute their declared type names to the assembly scope regardless of whether
they contain migration candidate text. This ensures complete disambiguation
information is available for target detection.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8a854c97-7eda-4101-a36e-09f22659df0f

📥 Commits

Reviewing files that changed from the base of the PR and between 7dec89f and ad9eb43.

⛔ Files ignored due to path filters (1)
  • Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationPreflightScanner.cs.meta is excluded by none and included by none
📒 Files selected for processing (15)
  • Assets/Tests/Editor/SetupWizardWindowTests.cs
  • Assets/Tests/Editor/ThirdPartyToolMigrationFileServiceTests.cs
  • Assets/Tests/Editor/ThirdPartyToolMigrationWizardWindowTests.cs
  • Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAssemblyUsageAnalyzer.cs
  • Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAssemblyUsageAsyncAnalyzer.cs
  • Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationFileService.cs
  • Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationModels.cs
  • Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationPlanBuilder.cs
  • Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationPreflightScanner.cs
  • Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationProjectFileInventory.cs
  • Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationTargetScanner.cs
  • Packages/src/Editor/Presentation/Setup/SetupWizardWindow.cs
  • Packages/src/Editor/Presentation/Setup/SetupWizardWindow.uss
  • Packages/src/Editor/Presentation/Setup/ThirdPartyToolMigrationWizardView.cs
  • Packages/src/Editor/Presentation/Setup/ThirdPartyToolMigrationWizardWindow.cs

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