# Migration rules: v3.34 → v3.46

> Used by `/migrate --from=3.34 --to=3.46`.
> Covers the breaking changes shipped during the v3.46 alignment work
> (ApplicationZone removal, layout consolidation, NavigationApplication.Create
> signature change, Tailwind tokenisation, mergeRoutes deprecation, AI patterns).

## Rule list (machine-readable)

```yaml
rules:
  # ──────────────────────────────────────────────────────────────────
  # BACKEND — Domain / Seed / API
  # ──────────────────────────────────────────────────────────────────

  - id: R-BE-001
    category: backend
    description: "ApplicationZone enum and Zone column removed (replaced by IsPersonal + IsOpen flags on NavigationApplication)."
    detect_pattern: 'ApplicationZone\.\w+|Zone\s*=\s*ApplicationZone\.\w+|public\s+ApplicationZone\s+Zone'
    glob: "src/**/*.cs"
    severity: BLOCKING
    requires_confirmation: false
    references:
      - templates/skills/apex/references/core-seed-data.md
      - templates/skills/application/references/provider-template.md
      - templates/skills/apex/references/checks/seed-checks.sh   # check C66 enforces this

  - id: R-BE-002
    category: backend
    description: "NavigationApplication.Create() signature lost the first 'zone' parameter; gained isOpen + isPersonal at the end (both default false)."
    detect_pattern: 'NavigationApplication\.Create\(\s*ApplicationZone\.\w+'
    glob: "src/**/*.cs"
    severity: BLOCKING
    requires_confirmation: true   # contextual — review each call site
    references:
      - D:/01 - projets/SmartStack.app/features/IA-Workflow/src/SmartStack.Domain/Navigation/NavigationApplication.cs

  - id: R-BE-003
    category: backend
    description: "ApplicationMenuDto.Zone (string) field removed; consume IsPersonal + IsOpen instead."
    detect_pattern: '\.Zone\b|"Zone"'
    glob: "src/SmartStack.Application/Navigation/**/*.cs"
    severity: BLOCKING
    requires_confirmation: true

  - id: R-BE-004
    category: backend
    description: "Migration to drop the 'Zone' column and add IsPersonal/IsOpen columns. Generate via dotnet ef if missing."
    detect_pattern: 'Zone'
    glob: "src/SmartStack.Infrastructure/Persistence/Migrations/*.cs"
    severity: WARNING
    requires_confirmation: true
    note: "If the project's last migration is older than core_v3.46.0, prompt user to run /efcore:migration RemoveApplicationZone."

  - id: R-BE-005
    category: backend
    description: "Domain Hooks moved to SmartStack.Application.Common.Interfaces.Hooks namespace (previously: scattered)."
    detect_pattern: 'SmartStack\.(?:Domain|Application)\.(?!Common\.Interfaces\.Hooks)\w+\.(?:IBeforeCreate|IAfterCreate|IBeforeUpdate|IAfterUpdate|IBeforeDelete|IAfterDelete)'
    glob: "src/**/*.cs"
    severity: WARNING
    requires_confirmation: false
    references:
      - templates/skills/apex/references/smartstack-api.md   # Entity Lifecycle Hooks section

  - id: R-BE-006
    category: backend
    description: "BaseEntity gained an ExtensionData (string JSON) property + 7 typed methods. EF configuration must include builder.Property(x => x.ExtensionData).HasColumnType(\"nvarchar(max)\").HasDefaultValue(\"{}\")."
    detect_pattern: 'class\s+\w+Configuration\s*:\s*IEntityTypeConfiguration<\w+>'
    glob: "src/SmartStack.Infrastructure/Persistence/Configurations/**/*.cs"
    severity: WARNING
    requires_confirmation: true
    note: "Inspect each EF config file; only add the ExtensionData mapping where the entity inherits BaseEntity AND the table is meant to store extension data."

  # ──────────────────────────────────────────────────────────────────
  # FRONTEND — Layouts / Routing / Theme
  # ──────────────────────────────────────────────────────────────────

  - id: R-FE-001
    category: frontend
    description: "Legacy domain-specific layouts (AdminLayout / BusinessLayout / UserLayout / HRLayout / SalesLayout) consolidated into AppLayout (sole authenticated shell)."
    detect_pattern: '<\s*(AdminLayout|BusinessLayout|UserLayout|HRLayout|SalesLayout)\b|from\s+["\x27][^"\x27]*\/(AdminLayout|BusinessLayout|UserLayout|HRLayout|SalesLayout)["\x27]'
    glob: "web/**/*.tsx"
    severity: BLOCKING
    requires_confirmation: false
    references:
      - templates/skills/application/references/frontend-route-naming.md
      - templates/skills/apex/references/checks/seed-checks.sh   # check C67

  - id: R-FE-002
    category: frontend
    description: "Hardcoded Tailwind colors must be replaced by CSS variables. Mapping table in themes-db-driven.md (24 rows: bg-blue-* → var(--info-bg), text-gray-900 → var(--text-primary), etc.)."
    detect_pattern: '(bg|text|border)-(?!\[)(red|blue|green|gray|amber|yellow|orange|purple|indigo|violet|cyan|pink|emerald|rose|sky|teal|lime|fuchsia|stone|slate|zinc|neutral)-\d'
    glob: "web/**/*.tsx"
    severity: WARNING   # was BLOCKING in iteration 2 — but for a migration we relax to WARNING and let the user batch-fix
    requires_confirmation: true
    references:
      - templates/skills/application/references/themes-db-driven.md   # Section 5: Migration map
      - templates/skills/apex/references/checks/frontend-checks.sh    # check C9

  - id: R-FE-003
    category: frontend
    description: "Tailwind dark: prefix removed. SmartStack toggles a .dark class on root; CSS vars adapt automatically."
    detect_pattern: '\bdark:(bg|text|border)-'
    glob: "web/**/*.tsx"
    severity: WARNING
    requires_confirmation: false

  - id: R-FE-004
    category: frontend
    description: "mergeRoutes() and getStaticAppRoutes() removed in v3.7+. Pages must register via PageRegistry.register() in componentRegistry.generated.ts."
    detect_pattern: 'mergeRoutes\s*\(|getStaticAppRoutes\s*\('
    glob: "web/**/*.{ts,tsx}"
    severity: BLOCKING
    requires_confirmation: true
    references:
      - templates/skills/audit-route/references/routing-pattern.md
      - templates/skills/application/references/smartstack-provider.md

  - id: R-FE-005
    category: frontend
    description: "RouteGuard.tsx replaced by ProtectedRoute.tsx (API-driven permissions)."
    detect_pattern: 'RouteGuard|from\s+["\x27][^"\x27]*\/RouteGuard["\x27]'
    glob: "web/**/*.{ts,tsx}"
    severity: BLOCKING
    requires_confirmation: false

  - id: R-FE-006
    category: frontend
    description: "useParams must use generic { id: string } (not custom names like { ticketId, userId }). Custom names go in the destructuration."
    detect_pattern: 'useParams<\{\s*(?!id\s*:)\w+Id\s*:'
    glob: "web/**/*.tsx"
    severity: WARNING
    requires_confirmation: true
    note: "Refactor: const { id: ticketId } = useParams<{ id: string }>(). Param URL must be 'id' for DynamicRouter."

  - id: R-FE-007
    category: frontend
    description: "Client App.tsx must wrap with <SmartStackProvider config={...}> (replaces ad-hoc provider stacks)."
    detect_pattern: 'AuthProvider|TenantProvider|ThemeProvider'   # if present in App.tsx without SmartStackProvider above
    glob: "web/**/{App,main}.tsx"
    severity: WARNING
    requires_confirmation: true
    references:
      - templates/skills/application/references/smartstack-provider.md

  # ──────────────────────────────────────────────────────────────────
  # SEED — Navigation & Roles
  # ──────────────────────────────────────────────────────────────────

  - id: R-SEED-001
    category: seed
    description: "NavigationApplicationSeedEntry must include IsPersonal + IsOpen (both bool, default false). Replaces the Zone field."
    detect_pattern: 'NavigationApplicationSeedEntry'
    glob: "src/**/*NavigationApplicationSeedData.cs"
    severity: BLOCKING
    requires_confirmation: false

  - id: R-SEED-002
    category: seed
    description: "MySpace and Personal-scope apps should set IsPersonal = true."
    detect_pattern: 'NavigationApplicationSeedEntry'
    glob: "src/**/*MySpace*SeedData.cs"
    severity: WARNING
    requires_confirmation: true
    note: "Manual judgement: only set IsPersonal=true on apps that genuinely belong to user space."

  # ──────────────────────────────────────────────────────────────────
  # CONFIG — appsettings / package.json
  # ──────────────────────────────────────────────────────────────────

  - id: R-CFG-001
    category: config
    description: "@atlashub/smartstack peer must be at least 3.46.0."
    detect_pattern: '"@atlashub/smartstack":\s*"\^?[0-2]\.|^"@atlashub/smartstack":\s*"\^?3\.[0-3][0-9]\.|^"@atlashub/smartstack":\s*"\^?3\.4[0-5]\.'
    glob: "web/smartstack-web/package.json"
    severity: BLOCKING
    requires_confirmation: false
    note: "Bump @atlashub/smartstack to ^3.46.0 (or latest)."

  - id: R-CFG-002
    category: config
    description: "Tailwind v4 + @tailwindcss/vite required. Old @tailwindcss/postcss configuration deprecated."
    detect_pattern: '"@tailwindcss/postcss"'
    glob: "web/smartstack-web/package.json"
    severity: WARNING
    requires_confirmation: false
    note: "Install @tailwindcss/vite, remove @tailwindcss/postcss, update vite.config.ts plugins list."

  - id: R-CFG-003
    category: config
    description: "appsettings.json should include the new sections : SlaMonitoring, SolutionAutoClose, AutoAssignment, WorkflowExecution (added in v3.45/v3.46)."
    detect_pattern: '^\s*"(?:Logging|Email)\s*"'
    glob: "src/SmartStack.Api/appsettings.json"
    severity: WARNING
    requires_confirmation: true
    note: "Compare against templates/project/appsettings.json.template ; add missing sections (defaults are safe)."
```

## Implementation recipes

The rules above are declarative ; some need a step-by-step recipe when a single regex isn't enough. The skill's `--apply` mode falls back to these recipes for any rule with `requires_confirmation: true`.

### R-BE-001 — `ApplicationZone` removal

For each file matched :

1. Remove every line that matches `^\s*Zone\s*=\s*ApplicationZone\.\w+,?\s*$`
2. After the line `Id = ApplicationId,` (or the first property of the SeedEntry), insert :
   ```
   IsPersonal = false,
   IsOpen = false,
   ```
3. Replace `public ApplicationZone Zone { get; init; }` (DTO) by :
   ```csharp
   public bool IsPersonal { get; init; }
   public bool IsOpen { get; init; }
   ```
4. Remove `using` directives for `SmartStack.Domain.Navigation` if `ApplicationZone` was the only reason they were imported.

### R-BE-002 — `NavigationApplication.Create()` signature

For each call site :

- Old signature : `NavigationApplication.Create(ApplicationZone.X, code, label, description, icon, iconType, route, displayOrder)`
- New signature : `NavigationApplication.Create(code, label, description, icon, iconType, route, displayOrder, isOpen: bool, isPersonal: bool)`

**Recipe** :
1. Remove the first argument (`ApplicationZone.X`).
2. Append two named arguments at the end : `isOpen: false, isPersonal: false` (or `true` based on context — see R-SEED-002).
3. **Manual review** : if the original call passed `Personal` zone, propose `isPersonal: true`. If `Admin` zone (system app), propose `isOpen: true`.

### R-FE-001 — Layout consolidation

For each `.tsx` file containing `<AdminLayout>`, `<BusinessLayout>`, `<UserLayout>`, `<HRLayout>`, or `<SalesLayout>` :

1. Replace the JSX tag (open + close) by `<AppLayout>` / `</AppLayout>`.
2. Update the import : `import { AppLayout } from '@/layouts/AppLayout'`.
3. Remove the old import line.

If the file was `src/layouts/AdminLayout.tsx` (or similar), DELETE the file — it no longer has a purpose.

### R-FE-002 — Tailwind tokenisation

This rule is **bulk** but **noisy**. Apply only if the user accepts (default WARNING in migration ; was BLOCKING in iteration 2).

Use the migration map in `templates/skills/application/references/themes-db-driven.md` Section 5. For each `(bg|text|border)-{color}-{shade}` occurrence, propose the canonical CSS-var replacement based on context :
- Status badges (success/warning/error/info) → `var(--{status}-bg)` / `var(--{status}-text)` / `var(--{status}-border)`
- Action buttons → `var(--color-accent-600)` / `hover:bg-[var(--color-accent-700)]`
- Body text → `var(--text-primary)` / `var(--text-secondary)`
- Card backgrounds → `var(--bg-card)`
- Borders → `var(--border-color)`

When the context is ambiguous (e.g. random `bg-blue-100`), prompt for the intent (info badge ? accent ? branding ?) and apply accordingly.

### R-FE-004 — `mergeRoutes` / `getStaticAppRoutes` removal

These calls usually live in `src/main.tsx` or `src/App.tsx`. Recipe :

1. Open the file.
2. Find the call to `mergeRoutes(...)` or `getStaticAppRoutes(...)`.
3. For each route registered :
   - Convert to `PageRegistry.register('module.section', lazy(() => import(...)))`
   - Place those calls in `src/extensions/componentRegistry.generated.ts` (create the file if missing).
4. Remove the original `mergeRoutes` call ; remove its imports.
5. Ensure `main.tsx` imports `./extensions/componentRegistry.generated` early (before render).

This is a refactor — the skill SHOULD prompt the user to confirm and provide a diff preview.

### R-CFG-002 — Tailwind v4 migration

```bash
# Inside web/smartstack-web/
npm uninstall @tailwindcss/postcss
npm install --save-dev @tailwindcss/vite

# Edit vite.config.ts:
#  + import tailwindcss from '@tailwindcss/vite';
#  + plugins: [react(), tailwindcss()],

# Edit index.css to use the new @theme {} syntax (see themes-db-driven.md Section 3.3)
```

The skill prompts and applies one step at a time, since these touch different files.

## Notes

- This rule file is the single source of truth for `v3.34 → v3.46`. Add new rules here as breaking changes are discovered.
- For future migrations (e.g. `v3.46 → v3.47`), create `references/v3.46-to-v3.47.md` and register it in the SKILL.md mapping table.
- Always run with `--dry-run` first. Always commit before `--apply`.
