diff --git a/roadmap.md b/roadmap.md index 0772f9f..05db204 100644 --- a/roadmap.md +++ b/roadmap.md @@ -75,7 +75,7 @@ scope. | N.1 | ~~**Code-split the bundle.**~~ ✅ Done — app `949→738 KiB` (#26), popup `478→190 KiB`. Advanced/Migration screens (app) and the non-default popup tabs lazy-load from `dist/chunks/`. | `app` was 949 KiB / `popup` 478 KiB vs a 244 KiB budget. | M | | N.2 | ~~**Clear the `any` warnings + CI gates.**~~ ✅ Done (#26) — 0 lint warnings; `typecheck`/`lint --max-warnings=0`/`test`/`build` run in CI. | Locked in before it regresses. | S | | N.3 | **Tests for the new 0.2 flows** — Convert round-trips, Compare, export multi-format, file parsing ✅ (#26); streaming capture ✅; still want scheduled-snapshot scheduling/retention + import dry-run. | These are the features users actually touch now. | M | -| N.4 | **Decide the migration suite's fate.** Either (a) test it to a trustable bar, or (b) gate it behind an explicit "experimental" flag in Settings so the demoted-but-present state isn't a silent liability. | Shipping barely-tested complex code is the biggest correctness risk in the repo. | S–L | +| N.4 | ~~**Decide the migration suite's fate.**~~ ✅ Done — gated behind an **experimental** flag in Settings (default off). The Migration nav hides unless opted in, and direct navigation shows an opt-in notice. Testing it to a trustable bar remains optional future work if it graduates. | Shipping barely-tested complex code was the biggest correctness risk. | S | --- diff --git a/src/core/types/storage.ts b/src/core/types/storage.ts index 52c0e3e..acfe61b 100644 --- a/src/core/types/storage.ts +++ b/src/core/types/storage.ts @@ -112,6 +112,8 @@ export interface UiSettings { accentColor?: string; /** User-assigned org colors for visual differentiation, keyed by orgId */ orgColorMap?: Record; + /** Opt-in to the experimental, thinly-tested cross-org Migration suite. Default off. */ + experimentalMigration?: boolean; } /** Saved data template for reusable data pushes */ diff --git a/src/ui/app/AppRoot.tsx b/src/ui/app/AppRoot.tsx index 3757293..f176e5d 100644 --- a/src/ui/app/AppRoot.tsx +++ b/src/ui/app/AppRoot.tsx @@ -94,6 +94,7 @@ export function AppRoot(): VNode { const [undoPanelOpen, setUndoPanelOpen] = useState(false); const [showOnboarding, setShowOnboarding] = useState(false); const [activeProjectId, setActiveProjectId] = useState(null); + const [experimentalMigration, setExperimentalMigration] = useState(false); async function refreshTabs(): Promise { try { @@ -129,6 +130,7 @@ export function AppRoot(): VNode { const resolved = resolveTheme(savedTheme); applyTheme(resolved); applyAccentColor(settings.accentColor); + setExperimentalMigration(settings.experimentalMigration ?? false); }).catch(e => { console.error('Failed to load theme:', e); applyTheme('light'); @@ -245,7 +247,9 @@ export function AppRoot(): VNode { { key: 'convert', label: 'Convert' }, ], }, - { + // The Migration suite is experimental (thinly tested) — only surface it + // when the user has opted in via Settings. + ...(experimentalMigration ? [{ key: 'migration', label: 'Migration', items: [ { key: 'migration/projects', label: 'Migration Projects' }, { key: 'migration/validation', label: 'Migration Validation' }, @@ -253,7 +257,7 @@ export function AppRoot(): VNode { { key: 'migration/templates', label: 'Migration Templates' }, { key: 'migration/idMaps', label: 'ID Maps' }, ], - }, + }] as NavGroup[] : []), { key: 'extras', label: 'Library', items: [ { key: 'templates', label: 'Templates' }, @@ -391,7 +395,37 @@ export function AppRoot(): VNode { if (route === 'schedules') return ; if (route === 'diff') return ; - // ── Migration suite (top-level) ── + // ── Migration suite (top-level, experimental) ── + // Guard direct navigation (legacy aliases, command palette, saved routes) + // when the suite hasn't been enabled in Settings. + if (route.startsWith('migration/') && !experimentalMigration) { + return ( +
+
+
+
🧪
+

Migration is an experimental feature

+

+ The cross-org Migration suite is still being hardened. Enable it in + Settings to try it. +

+
+ + +
+
+
+
+ ); + } if (route === 'migration/projects' && activeProjectId) return setActiveProjectId(null)} />; if (route === 'migration/projects') return setActiveProjectId(id)} />; if (route === 'migration/validation' && activeProjectId) return setActiveProjectId(null)} />; diff --git a/src/ui/screens/SettingsScreen.tsx b/src/ui/screens/SettingsScreen.tsx index 81da8da..34a0149 100644 --- a/src/ui/screens/SettingsScreen.tsx +++ b/src/ui/screens/SettingsScreen.tsx @@ -90,6 +90,15 @@ export function SettingsScreen(props: { sf: SfApi; mode: 'app' | 'panel' | 'popu Panel pinned + +
Theme
Accent Color