---
name: switch-backend-ed7ffe041cfe
description: >
  Cambia el backend de tracking de un proyecto entre los 4 (FreeForm / Trello /
  Plane / Native) de forma guiada y sin pérdida de avance. Migra US/UC/AC/comments/
  estado al destino, actualiza atómicamente los 3 lugares de verdad, y ofrece
  regenerar evidencias. Use when the user says "switch backend", "cambiar backend",
  "migrar de FreeForm a Trello/Plane/Native", "mover el tracking a", "change tracking
  backend".
context: direct
---

# /switch-backend (US-BACKEND-SWITCH / US-BACKEND-SWITCH-NATIVE)

Orquestador guiado del cambio de backend de tracking entre los 4 backends de
spec-driven. Envuelve la tool atómica MCP `switch_project_backend` (que internamente
hace migrate + seed + switch + exit-report como **una operación todo-o-nada**) y
`regenerate_evidence` con una UX segura de principio a fin: preview obligatorio con
**confirmación de conteo**, confirmación literal y reporte auditable.

> **Garantía de no-pérdida**: la migración es **aditiva** — el backend origen
> permanece intacto y legible hasta que confirmes. Nada se borra. La evidencia de
> acceptance (`.quality/evidence/`) tampoco se toca: vive en el filesystem,
> independiente del board.

> **Garantía de atomicidad (US-BACKEND-SWITCH-NATIVE)**: el cambio es **todo-o-nada**.
> O queda todo coherente (datos migrados + los 3 lugares de config conmutados) o no
> queda nada a medias — si cualquier paso falla, se hace rollback total (incluida la
> migración de datos a Postgres si el destino era Native nuevo).

## Online-first (MCP remoto + cliente local)

> **Cambio US-BACKEND-SWITCH-NATIVE**: este skill **ya NO exige MCP local**. Cumple
> la decisión canónica "Transporte único MCP remoto + content-passing" (UC-668): el
> skill lee el source del **cliente** (con `Read`) y lo pasa por **content-passing**;
> al confirmar, escribe el contenido devuelto de los 3 lugares de config en el
> filesystem del **cliente**. El servidor MCP nunca toca un filesystem ajeno.

Implicación práctica: en MCP remoto, **nunca** dejes que el server resuelva el source
desde su propio filesystem. Para un origen `freeform`, lee `doc/tracking/items.json`
del cliente con `Read` y pásalo como `source_content`. Si el preview reporta 0 items o
un conteo inesperado (p.ej. el del engine), **PARA** — el source no es el tuyo.

## Uso

```
/switch-backend
/switch-backend a:native           # destino sugerido
/switch-backend de:freeform a:trello
```

---

## Paso 1 — Detectar backend actual

Llama a `detect_project_backend(project_path=".")`. Reporta al usuario el backend
actual y de qué fuente se infirió (settings / items.json / app_spec / default).

Con el MCP alojado (UC-8604) añade `files_content` con `.claude/settings.local.json`,
`doc/tracking/items.json` y `doc/app/app_spec.md` (los que existan): el servidor no lee tu
repositorio.

```
Backend actual: {freeform|trello|plane|native} (fuente: {...})
```

Si el detector no encuentra ningún backend → el proyecto no está onboardeado;
sugiere `/app-init` u `onboard_project` antes de migrar.

---

## Paso 2 — Elegir destino + credenciales (de forma segura)

Pregunta el backend **destino** entre los 3 restantes (nunca el actual). Una vez
elegido, solicita las credenciales del destino **según su tipo**, con esta tabla:

| Destino | Qué necesita | Cómo se aporta |
|---------|--------------|----------------|
| **FreeForm** | `root_path` absoluto del repo cliente | El skill lo resuelve vía `git rev-parse --show-toplevel` + `/doc/tracking`. No se pide por chat. |
| **Trello** | `api_key` + `token` | `set_migration_target` con las credenciales de Trello. |
| **Plane** | `base_url` + `api_key` + `workspace_slug` | `set_migration_target` con las credenciales de Plane. |
| **Native** | `project_id` + acceso a Postgres | El DSN se lee **exclusivamente** de la env var `SPECBOX_NATIVE_DSN` (Frontier 2). **NUNCA pidas el DSN por chat** ni lo escribas en ningún archivo. Si la env var no está, avisa al usuario de que la exporte y para. |

> **Regla Frontier 2 (inviolable)**: el DSN de Native jamás se solicita por chat,
> ni se persiste en sesión, ni se escribe en `meta.json` / `settings.local.json` /
> reportes. Solo vive en `SPECBOX_NATIVE_DSN`.

Para destinos Trello/Plane, llama a `set_migration_target` con las credenciales del
destino antes del preview.

### Gate de prerequisitos Native (BLOQUEANTE — UC-709)

> Solo cuando **destino == native**. Verifica los 3 gates **ANTES de leer o
> transformar el source** — descubrirlos a mitad fue el "medio día perdido" del
> dogfooding. Para en el primero que falle con su mensaje accionable.

1. **Tenant provisionado.** El `project_id` native debe existir ya en SpecBox
   Cloud. El MCP **no** provisiona tenants — es acción del panel
   (`cloud.specbox.build`). Si no tienes un `project_id`, para y pide al usuario
   que provisione el proyecto en el panel y mintee un `dev_token`.
2. **Identidad / dev_token.** Llama `set_auth_token(backend_type="native",
   project_id={pid}, token={dev_token})` y luego `whoami`. Si `whoami` devuelve
   `NOT_NATIVE_SESSION` o error → el token es inválido/ausente; para y pídelo.
   Un `whoami` con `developer_id` resuelto prueba **a la vez** identidad válida y
   que el server tiene el DSN (el pool consultó Postgres).
3. **DSN en el server.** No lo verifiques en tu shell local (`printenv` del
   cliente es irrelevante — el DSN lo necesita el proceso del MCP server, no el
   tuyo). El éxito de `whoami` en el paso 2 ya lo confirma. Si `whoami` falla con
   un error de conexión a Postgres → el server no tiene `SPECBOX_NATIVE_DSN`;
   para y avisa (es config del despliegue del MCP, no algo que el usuario exporte
   en su terminal).

```
Gate native: tenant {pid} ✓ | whoami {developer_id} ✓ | DSN(server) ✓
```

Solo con los 3 en verde sigue al Paso 3.

---

## Paso 3 — Leer el source del cliente (content-passing)

> Solo para origen `freeform`. Para `trello`/`plane`/`native` el source vive detrás
> de una API/DB que el server lee directamente — sáltate la lectura local.

Si el origen es `freeform`:

1. Resuelve el path absoluto del cliente: `git rev-parse --show-toplevel` + `/doc/tracking/`.
2. **Pre-flight de formato (UC-709) — hazlo ANTES del preview/auth/chunks.** El
   FreeForm tiene **dos dialectos** y la migración solo consume uno:

   | Dialecto | Marcador en disco | ¿Lo consume la migración? |
   |---|---|---|
   | **Flat** | `doc/tracking/items.json` (array JSON `[{id,labels,parent_id,...}]`) | ✅ directo |
   | **Exploded** | `doc/tracking/index.json` (anidado `{"user_stories":[...]}`) + `us/*.md` + `uc/*.md` con AC en checkboxes `- [x]` | ❌ hay que normalizar |

   Mira qué archivo existe:
   - **`items.json` presente** → léelo con `Read`; es el `source_content`. Sigue.
   - **`index.json` presente (sin `items.json`)** → es el dialecto *exploded*.
     Pasarlo tal cual al preview falla con `items_content ... got dict`. Normalízalo
     primero (paso 3a). **NO** sigas con auth/provisioning hasta haberlo convertido
     y validado su conteo — así el fallo de formato sale aquí, no en el Paso 4.
   - **Ninguno** → el tracking está vacío o en otra ruta; para y pregunta.

3. Tendrás el `source_content` (flat) en memoria para el preview / la ingesta por lotes.

### Paso 3a — Normalizar FreeForm *exploded* → items.json (UC-709)

Cuando el origen es el dialecto exploded:

1. Lee `index.json` (el maestro anidado) y mide su conteo real
   (`US`, `UC`, y `AC` = suma de `ac_total`). Ese conteo es tu **guard rail**.
2. **AC — decide el modo y dilo al usuario:**
   - *Faithful*: extrae los textos de AC de los checkboxes `- [x]`/`- [ ]` de los
     `us/*.md` + `uc/*.md` y arma un mapa `{uc_id: [{text, done}]}`. Los AC migran
     con su texto y estado reales.
   - *Degradado*: si no extraes los textos, la conversión sintetiza `ac_total` AC
     placeholder por UC (con `ac_done` marcados `done`). **Los conteos cuadran**
     (el guard rail pasa) pero los textos son placeholders. Avisa explícitamente.
3. Convierte con el normalizador del engine
   (`server.migration.freeform_normalize.normalize_source_content(source_content,
   ac_texts=...)`). Devuelve `{items_content, counts, converted, ac_degraded}`.
   Usa `items_content` (array flat) como `source_content` desde aquí.
4. Verifica que `counts` == el conteo del paso 1. Si difiere → para (source mal
   formado o extracción incompleta). Si `ac_degraded=True`, recuérdalo en el
   reporte final.

> El tamaño que decide ruta normal vs. por lotes (Paso 3b) es el del
> **`items_content` normalizado**, no el del `index.json` original.

---

## Paso 3b — Source grande hacia Native: ingesta por lotes (US-NATIVE-BATCH-INGEST)

> Solo aplica cuando **destino = native** y el `items.json` del cliente es grande
> (> ~64 KB). Un `items.json` real (133 KB / cientos de ítems) NO cabe fiablemente
> en un único parámetro `source_content` — pasarlo de golpe arriesga truncado/corrupción
> silenciosa, y el skill debe **negarse** a pasar un blob no verificable. En su lugar,
> usa la **ingesta por lotes**: troceo verificable del transporte + escritura atómica.

Decisión de ruta:

```
¿destino == native Y len(items.json en bytes) > 64 KB?
├── NO  → ruta estándar: pasa source_content de una pieza (Paso 4 / Paso 5).
└── SÍ  → ruta por lotes (este paso):
    1. Calcula sha256 del items.json completo y el nº de ítems (US+UC+AC) que vas a migrar.
    2. Trocea el items.json en chunks de ≤16 KB. Calcula sha256 de cada chunk.
    3. Presenta al usuario el PLAN DE TRANSPORTE antes de ejecutar:
```

```
📦 Plan de transporte por lotes — items.json grande → Native
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Tamaño:  {N} KB        Ítems declarados: {US+UC+AC}
Chunks:  {M} × ≤16 KB  SHA-256 source:   {hash[:16]}…
La escritura sigue siendo atómica (commit diferido en 1 transacción, rollback total).
```

```
    4. start_migration_session(target_project_id={pid native}, source_type="freeform",
         declared_items={N}, declared_bytes={bytes}, source_sha256={hash},
         chunk_count={M}, dev_token={token del panel})
       → session_id. (NO escribe nada en Postgres todavía.)
    5. Por cada chunk i en orden:
         append_migration_chunk(session_id, chunk_index=i, chunk_data={chunk_i},
           chunk_sha256={hash_i}, dev_token={token})
       Si alguno devuelve CHUNK_HASH_MISMATCH → el transporte corrompió ese chunk;
       reenvíalo. No sigas al commit con un chunk dudoso.
    6. Confirma el conteo con el usuario (mismo guard rail del Paso 4) y ejecuta el
       switch atómico pasando batch_session_id en vez de source_content (Paso 5).
```

> El commit (dentro del switch o vía `commit_migration_session`) verifica que llegaron
> los M chunks, que el SHA-256 reensamblado == el declarado, y que el conteo parseado ==
> el confirmado, **antes** de escribir un solo INSERT. Si algo falla → no escribe nada.
> **NUNCA** le pidas al usuario que pegue el blob de 133 KB a mano.

---

## Paso 4 — Preview obligatorio (dry-run) + confirmación de conteo

**BLOQUEANTE**: antes de ejecutar nada, llama a la tool atómica en modo preview:

```
switch_project_backend(
  project_slug="{slug}",
  source_type="{actual}",
  target_type="{destino}",
  source_content={contenido de items.json leído en Paso 3, o None si no es freeform},
  target_id={id destino o None para crear},
  project_path=".",
  dev_token={token del panel si destino/origen es native, "" si no},
  dry_run=True
)
```

El preview devuelve `read_counts` (`us`/`uc`/`ac`) **leídos del cliente**, las
degradaciones de estado, la `collision` si el destino native ya tiene items, y el
`discarded_native_state` si el origen es native. Preséntalo:

```
📋 Preview de migración — {actual} → {destino}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Leídas DEL CLIENTE:  {read_counts.us} US / {read_counts.uc} UC / {read_counts.ac} AC

⚠️ Degradaciones de estado (si las hay):
  {Si el destino es Plane: UCs 'review' → 'in_progress', US 'user_stories' → 'backlog'.
   Si no: "ninguna".}

{Si el destino native ya existe (collision): item_count actual + pide on_collision.}
{Si el origen es Native: lista de reservas / developers / branches que NO se migran.}
```

### Guard rail de conteo (BLOQUEANTE — US-BACKEND-SWITCH-NATIVE)

Si `read_counts.us + read_counts.uc == 0`, o el conteo **no coincide** con lo que
esperas de tu repo (p.ej. lee 22/112 = el engine, en vez de tus 11/88) → **PARA**.
El source no es el tuyo (típico bug de path en MCP remoto). No ejecutes el real.

### Confirmación literal del conteo (BLOQUEANTE)

```
Voy a migrar {read_counts.us} US / {read_counts.uc} UC leídas de TU repo, de
{actual} a {destino}, y cambiar el backend activo. El origen NO se borra y la
operación es atómica (todo-o-nada). ¿Confirmas ese conteo? (escribe "migrar")
```

Si el usuario no confirma → PARAR.

---

## Paso 5 — Ejecutar el switch atómico + write-back

Tras la confirmación, llama a la MISMA tool con `dry_run=False` y el conteo confirmado:

```
switch_project_backend(
  project_slug="{slug}",
  source_type="{actual}",
  target_type="{destino}",
  source_content={mismo contenido del Paso 3, o None si usaste la ruta por lotes},
  batch_session_id={session_id del Paso 3b, o "" si pasaste source_content},
  target_id={id destino o None},
  project_path=".",
  dev_token={token si native},
  on_collision={"reuse"|"skip"|"fail" si hubo collision},
  dry_run=False,
  confirmed_count={"us": read_counts.us, "uc": read_counts.uc}
)
```

> **Ruta por lotes (Paso 3b)**: pasa `batch_session_id` (no `source_content`). El paso de
> escritura del switch ingesta los chunks acumulados de forma atómica; el resto de la
> operación (config de los 3 lugares) es idéntico y sigue siendo todo-o-nada — la config
> solo cambia si la ingesta tuvo éxito.

Internamente, **en una sola llamada todo-o-nada**: verifica el conteo, (target native)
exige dev_token, crea el proyecto destino, copia US/UC/AC **preservando estados**,
asocia el developer, (source native) recopila el estado descartado, y conmuta los **3
lugares de verdad** (registry / app_spec / settings). Si cualquier paso falla → rollback
total (incluida la migración de datos), y devuelve `rolled_back=true` con `failing_step`.

**Si la respuesta trae `rolled_back: true`** → reporta el fallo al usuario y NO continúes
al reporte de éxito; el proyecto quedó en su backend original.

**Write-back de los 3 lugares (cliente)**: con el MCP remoto el servidor solo escribe el
registro y devuelve `client_writes` (en `switch_result.client_writes` de
`switch_project_backend`, o en `client_writes` de `switch_backend`, `enable_mirror` y
`disable_mirror`) para que lo apliques tú en el repo (UC-8604):

- `client_writes.settings` → `.claude/settings.local.json`: aplica cada clave de `set`
  (con puntos = anidada: `specbox.backend_type`, `specbox.mirror`) y borra las de `unset`.
  Crea el fichero con `{}` si no existe. Lee, modifica y escribe con `Read` + `Write`.
- `client_writes.app_spec` → `doc/app/app_spec.md`: sustituye el cuerpo de la zona
  `tracking_backend` (entre sus marcadores `<!-- @specbox:zone … id="tracking_backend" -->`)
  por `body`. Si el fichero no existe, sáltalo.

Con el MCP local la respuesta no trae `client_writes`: el servidor ya escribió los tres.

Verifica la consistencia con `detect_project_backend` (con el MCP remoto, enviando
`files_content` con los ficheros ya escritos, como en el Paso 1): debe devolver el nuevo backend.

---

## Paso 6 — Reporte final (4 secciones)

Presenta al usuario un reporte con **exactamente estas 4 secciones**:

```
✅ Cambio de backend completado — {actual} → {destino}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

1. SWITCH + CONSISTENCIA
   - Items migrados: {N} US / {N} UC / {N} AC / {N} comments (skipped: {N})
   - 3 lugares de verdad actualizados: projects.json ✓ · app_spec.md ✓ · settings.local.json ✓
   - detect_project_backend confirma: {destino}

2. EVIDENCIA INTACTA
   - La evidencia de acceptance (.quality/evidence/) NO se ha movido ni alterado.
     Sigue indexada por uc_id lógico, así que el vínculo evidencia↔UC se mantiene.
   - ⚠️ Riesgo: la evidencia puede quedar STALE respecto al código si éste
     evolucionó. Considera regenerarla (sección 4).

3. ESTADO NATIVE DESCARTADO {solo si el origen era Native}
   - Claims activos descartados: {N} {listar uc_id + developer_id}
   - Identidad de developers no migrada: {N}
   - Branches registradas no migradas: {N}
   - Nota: los backends single-user no tienen el concepto multi-developer.
   {Si el origen NO era Native, omitir esta sección.}

4. REGENERACIÓN DE EVIDENCIAS (opcional)
   - Para refrescar la evidencia contra el código actual:
     ¿Quieres ejecutar regenerate_evidence ahora? (s/n)
   - Si SÍ → llamar regenerate_evidence(project_path=".") y mostrar el progreso
     por UC + el reporte en doc/migrations/.
   - Si NO → recordar que puede ejecutarse después con la tool regenerate_evidence.
```

---

## Anti-patterns

- **NUNCA** pidas el DSN de Native por chat ni lo escribas en disco (Frontier 2).
- **NUNCA** ejecutes `switch_project_backend(dry_run=False)` sin preview + confirmación del conteo.
- **NUNCA** ejecutes el real si el preview leyó 0 items o un conteo que no es el de tu repo.
- **NUNCA** dejes que el server resuelva un source `freeform` desde su propio filesystem en MCP remoto — pásalo por `source_content` (source pequeño) o por la ingesta por lotes del Paso 3b (source grande). Nunca pegues un blob grande no verificable en `source_content`.
- **NUNCA** borres ni modifiques el backend origen — la migración es aditiva.
- **NUNCA** continúes al reporte de éxito si la respuesta trae `rolled_back: true`.
- **NUNCA** muevas o alteres `.quality/evidence/` — solo se regenera vía `regenerate_evidence`.

## Tools MCP usadas

| Tool | Paso | Rol |
|------|------|-----|
| `detect_project_backend` | 1, 5 | Detectar backend actual / verificar consistencia |
| `Read` (cliente) | 3 | Leer `doc/tracking/items.json` del cliente (content-passing) |
| `set_migration_target` | 2 | Credenciales del destino (Trello/Plane) |
| `switch_project_backend` | 4, 5 | **Operación atómica**: preview (dry_run) + migrate+seed+switch+exit todo-o-nada con rollback |
| `start_migration_session` | 3b | Abrir sesión de ingesta por lotes (source freeform grande → Native) |
| `append_migration_chunk` | 3b | Subir un chunk hash-verificado del `items.json` |
| `commit_migration_session` | 3b/5 | Verificar integridad global + ingesta atómica (vía el switch o standalone) |
| `Write` (cliente) | 5 | Write-back de `app_spec.md` + `settings.local.json` en el repo del cliente |
| `regenerate_evidence` | 6 | Reejecutar acceptance por UC (opt-in) |

> `migrate_backend` y `switch_backend` siguen disponibles como tools sueltas, pero
> este skill usa `switch_project_backend` (atómica) — no las encadena a mano.


---
Source attribution: EmbedBuild
Original project: https://github.com/EmbedBuild/specbox-engine/blob/0d7a27c30d728b068215b9486667272160ed0d4e/.claude/skills/switch-backend/SKILL.md
License: Apache-2.0
License information: https://www.apache.org/licenses/LICENSE-2.0
Original upstream instructions; downloaded from Mega AI Hub.

## Upstream license notice

Business Source License 1.1

Parameters

Licensor:             Jesus Perez Sanchez (Embed Build)
Licensed Work:        SpecBox Engine v5.11.0
                      Copyright (c) 2026 Jesus Perez Sanchez
Additional Use Grant: You may use the Licensed Work for non-production
                      purposes, including personal evaluation, academic
                      research, educational use, and contributing
                      modifications back to the Licensed Work.
                      Production use is permitted only in projects
                      developed jointly with Embed Build under a
                      separate written agreement.
Change Date:          2030-03-26
Change License:       Apache License, Version 2.0

License text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved.
"Business Source License" is a trademark of MariaDB Corporation Ab.

-----------------------------------------------------------------------------

Terms

The Licensor hereby grants you the right to copy, modify, create derivative
works, redistribute, and make non-production use of the Licensed Work. The
Licensor may make an Additional Use Grant, above, permitting limited
production use.

Effective on the Change Date, or the fourth anniversary of the first publicly
available distribution of a specific version of the Licensed Work under this
License, whichever comes first, the Licensor hereby grants you rights under
the terms of the Change License, and the rights granted in the paragraph
above terminate.

If your use of the Licensed Work does not comply with the requirements
currently in effect as described in this License, you must purchase a
commercial license from the Licensor, its affiliated entities, or authorized
resellers, or you must refrain from using the Licensed Work.

All copies of the original and modified Licensed Work, and derivative works
of the Licensed Work, are subject to this License. This License applies
separately for each version of the Licensed Work and the Change Date may vary
for each version of the Licensed Work released by Licensor.

You must conspicuously display this License on each original or modified copy
of the Licensed Work. If you receive the Licensed Work in original or
modified form from a third party, the terms and conditions set forth in this
License apply to your use of that work.

Any use of the Licensed Work in violation of this License will automatically
terminate your rights under this License for the current and all other
versions of the Licensed Work.

This License does not grant you any right in any trademark or logo of
Licensor or its affiliates (provided that you may use a trademark or logo of
Licensor as expressly required by this License).

TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
TITLE.

MariaDB hereby grants you permission to use this License's text to license
your works, and to refer to it using the trademark "Business Source License",
as long as you comply with the Covenants of Licensor below.

Covenants of Licensor

In consideration of the right to use this License's text and the "Business
Source License" name and trademark, Licensor covenants to MariaDB, and to all
other recipients of the licensed work to be provided by Licensor:

1. To specify as the Change License the GPL Version 2.0 or any later version,
   or a license that is compatible with GPL Version 2.0 or a later version,
   where "compatible" means that software provided under the Change License can
   be included in a program with software provided under GPL Version 2.0 or a
   later version. Licensor may specify additional Change Licenses without
   limitation.

2. To either: (a) specify an additional grant of rights to use that does not
   impose any additional restriction on the right granted in this License, as
   the Additional Use Grant; or (b) insert the text "None".

3. To specify a Change Date.

4. Not to modify this License in any other way.



# Mega AI Hub resource metadata

Creator / publisher: EmbedBuild
Original project: https://github.com/EmbedBuild/specbox-engine/blob/0d7a27c30d728b068215b9486667272160ed0d4e/.claude/skills/switch-backend/SKILL.md
Attribution basis: Source repository record

Fields of work: Development & Engineering, Project & Program Management
Roles: Backend Developer, DevOps / SRE, Project Manager
Specialties: Backend
Tools: Mcp
Country applicability: Country not specified
Classification basis: Source catalog labels
Instruction SHA-256: d8b22c863340f1ea0bc0379575759f529f4643767d241c6929c9b38c697b7c71
