Skip to content

fix(migrations): preserve migrated settings state#1213

Merged
chhoumann merged 1 commit into
masterfrom
fix/migration-settings-store-sync
May 26, 2026
Merged

fix(migrations): preserve migrated settings state#1213
chhoumann merged 1 commit into
masterfrom
fix/migration-settings-store-sync

Conversation

@chhoumann
Copy link
Copy Markdown
Owner

@chhoumann chhoumann commented May 26, 2026

Preserve migration results when startup migrations mix direct plugin.settings updates with settingsStore updates.

The file-exists behavior consolidation could mark consolidateFileExistsBehavior complete while stale store state restored legacy template choice fields. That left choices with setFileExistsBehavior / fileExistsMode but no fileExistsBehavior, causing runtime fallback to the collision prompt.

This PR keeps settingsStore and plugin.settings synchronized around each migration, uses full store replacement for loaded settings, and adds a repair migration for users who already have the stale legacy shape with the earlier consolidation flag marked complete.

Rollout notes:

  • Existing stuck configs are repaired by repairTemplateFileExistsBehavior.
  • Already-migrated choices are preserved.
  • The migration runner now reconciles store-backed and plugin-settings-backed migrations before marking each migration complete.

Validation:

  • bun run test -- src/migrations/migrate.test.ts src/migrations/consolidateFileExistsBehavior.test.ts
  • bun run test
  • bun run test:e2e -- tests/e2e/file-exists-behavior.test.ts
  • bun run test:e2e
  • bun run lint
  • bun run build

Summary by CodeRabbit

  • New Features

    • Added a new migration to repair legacy template file collision settings.
  • Bug Fixes

    • Improved settings initialization during startup for more reliable state management.
  • Tests

    • Enhanced migration test suite with additional lifecycle management and coverage for legacy settings repair scenarios.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 26, 2026

📝 Walkthrough

Walkthrough

This PR introduces a repair migration that re-runs legacy template file-exists setting normalization, with updated store API to synchronize migration execution, startup initialization using full state replacement, and comprehensive unit and end-to-end validation.

Changes

Template File-Exists Behavior Repair Migration

Layer / File(s) Summary
Settings contract and store API enhancement
src/settings.ts, src/settingsStore.ts
QuickAddSettings.migrations gains repairTemplateFileExistsBehavior: boolean initialized to false. settingsStore exposes replaceState(state) method delegating to Zustand's replace behavior for full state replacement.
Migration module definition and registry
src/migrations/repairTemplateFileExistsBehavior.ts, src/migrations/migrate.ts
New repairTemplateFileExistsBehavior migration module delegates to consolidateFileExistsBehavior.migrate and is registered in the migrations object for conditional execution.
Store-synchronized migration execution logic
src/migrations/migrate.ts
migrate function replaces settingsStore before migrations, captures store state before each migration, conditionally updates plugin.settings from store if changed, marks migration complete and persists to settingsStore, and reverts both plugin.settings and settingsStore on error.
Startup store initialization
src/main.ts
QuickAdd.onload initializes settingsStore via replaceState(this.settings) instead of setState to fully replace store state.
Migration execution unit tests
src/migrations/migrate.test.ts
Adds Vitest beforeEach/afterEach hooks for store subscription lifecycle management. Two new interaction pattern tests verify the repair migration does not restore stale legacy choices and that legacy settings are repaired when earlier consolidation migration is marked complete.
End-to-end migration validation
tests/e2e/file-exists-behavior.test.ts
E2E test seeds template choices with legacy and migrated file-exists settings, enables repair migration, waits for completion, and verifies stale do-nothing behavior is repaired to new shape while already-migrated overwrite behavior is preserved.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

  • chhoumann/quickadd#1152: Targets template file-exists behavior normalization by ensuring legacy settings are repaired via consolidateFileExistsBehavior.migrate, directly aligned with this PR's repair migration delegation.
  • chhoumann/quickadd#1150: Works on template file-exists behavior normalization for legacy collision settings, with this PR adding the repair mechanism to re-run that normalization path.

Suggested labels

released

Poem

🐰 A migration hops along,
Healing templates old and wrong,
Store syncs true from dawn to dusk,
Legacy fields dust to dust—
Rabbit's work, your templates fixed! ✨

🚥 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(migrations): preserve migrated settings state' accurately and specifically describes the main change: fixing a migration bug by synchronizing settings state between the plugin settings and the settings store.
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 fix/migration-settings-store-sync

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.

@cloudflare-workers-and-pages
Copy link
Copy Markdown

Deploying quickadd with  Cloudflare Pages  Cloudflare Pages

Latest commit: 61e19a9
Status: ✅  Deploy successful!
Preview URL: https://9b9f0daf.quickadd.pages.dev
Branch Preview URL: https://fix-migration-settings-store.quickadd.pages.dev

View logs

@chhoumann chhoumann marked this pull request as ready for review May 26, 2026 19:08
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 4 additional findings.

Open in Devin Review

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/main.ts (1)

56-56: ⚡ Quick win

Avoid reference aliasing in settingsStore.replaceState(this.settings)

settingsStore.replaceState is implemented as setState(state, true), so replaceState(this.settings) stores the exact same object reference in the Zustand store (no cloning). If this.settings is later mutated in-place without going through settingsStore.setState/replaceState, store subscribers won’t be notified.

In this file the only in-place mutation found is this.settings.version = currentVersion in announceUpdate(), which runs after migrate(this). Migrations usually replace the store state with deepClone(...), which breaks the aliasing—but if migrationsToRun.length === 0, the aliasing can persist and the version update will be “silent”.

Consider cloning on startup to make this robust:

Proposed fix
+import { deepClone } from "./utils/deepClone";
@@
-		settingsStore.replaceState(this.settings);
+		settingsStore.replaceState(deepClone(this.settings));
🤖 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 `@src/main.ts` at line 56, The call settingsStore.replaceState(this.settings)
stores the same object reference, so later in-place edits like
this.settings.version = currentVersion (in announceUpdate) can mutate the stored
state silently; fix by cloning before replacing the store state (e.g., use a
deep clone/structuredClone/JSON parse-stringify) and/or switch the in-place
mutation in announceUpdate to update via settingsStore.setState/replaceState so
subscribers are notified; update the code paths around
settingsStore.replaceState, this.settings, and announceUpdate to perform a clone
or use store setters instead.
🤖 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.

Nitpick comments:
In `@src/main.ts`:
- Line 56: The call settingsStore.replaceState(this.settings) stores the same
object reference, so later in-place edits like this.settings.version =
currentVersion (in announceUpdate) can mutate the stored state silently; fix by
cloning before replacing the store state (e.g., use a deep
clone/structuredClone/JSON parse-stringify) and/or switch the in-place mutation
in announceUpdate to update via settingsStore.setState/replaceState so
subscribers are notified; update the code paths around
settingsStore.replaceState, this.settings, and announceUpdate to perform a clone
or use store setters instead.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d81bbabb-471c-4265-8a28-47426a9845c8

📥 Commits

Reviewing files that changed from the base of the PR and between d38644d and 61e19a9.

📒 Files selected for processing (7)
  • src/main.ts
  • src/migrations/migrate.test.ts
  • src/migrations/migrate.ts
  • src/migrations/repairTemplateFileExistsBehavior.ts
  • src/settings.ts
  • src/settingsStore.ts
  • tests/e2e/file-exists-behavior.test.ts

@chhoumann chhoumann merged commit 04b2e79 into master May 26, 2026
4 checks passed
@chhoumann chhoumann deleted the fix/migration-settings-store-sync branch May 26, 2026 19:19
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