# Frontend PR Workflow — cd-frontend-dev → cd-system

> Última actualización: 2026-05-04 (v2 — staging-frontend + branch fija acumulada)
> Estado: **Live en producción** con flujo de 3 niveles (`staging-frontend` → `main` → `cd-system`)
> Owner: founder + maintainer del repo `cd-system`
> Workflow vigente: `cd-frontend-dev/.github/workflows/sync-to-main.yml` commit `5a8c2ed`

Cómo entran cambios de **vistas Blade** al monorepo `cd-system` desde un repo separado pensado para devs frontend que no necesitan PHP/Composer/MySQL local. Este doc cubre la arquitectura, los workflows de GitHub Actions involucrados, el ciclo de vida de un PR, los comandos del día a día y las buenas prácticas. **También está pensado para pegarse como contexto a un prompt de IA** (ver §10).

---

## Índice

1. [TL;DR](#1-tldr)
2. [Arquitectura de los dos repos](#2-arquitectura-de-los-dos-repos)
3. [Workflows de GitHub Actions involucrados](#3-workflows-de-github-actions-involucrados)
4. [Ciclo de vida de un cambio frontend](#4-ciclo-de-vida-de-un-cambio-frontend)
5. [Convenciones del PR](#5-convenciones-del-pr)
6. [Comandos del maintainer (cd-system)](#6-comandos-del-maintainer-cd-system)
7. [Comandos del dev frontend (cd-frontend-dev)](#7-comandos-del-dev-frontend-cd-frontend-dev)
8. [Buenas prácticas](#8-buenas-prácticas)
9. [Troubleshooting](#9-troubleshooting)
10. [Bloque para pegar como contexto IA](#10-bloque-para-pegar-como-contexto-ia)
11. [Cambios v1 → v2 (2026-05-04)](#11-cambios-v1--v2-2026-05-04)

---

## 1. TL;DR

```
dev edita .blade.php          GH Action acumula commits    maintainer    promote a main    promote a cd-system
en cd-frontend-dev   ──push──►  en PR contra            ──►  mergea  ──►  (PR manual)  ──►  (deploy a VPS1
                              staging-frontend              a staging                       arranca)
                              (branch fija: pending)
```

**Tres niveles de branch en cd-system, cada uno gate-eable por separado**:

| Branch | Quién la actualiza | Qué dispara |
|--------|---------------------|-------------|
| `staging-frontend` | PR auto desde cd-frontend-dev | Nada — solo es zona de revisión |
| `main` | PR manual desde `staging-frontend` | Build de imagen Docker (si tocan paths del filter) |
| `cd-system` | PR manual o fast-forward desde `main` | **Deploy a producción VPS1** |

> Nota: el deploy a `bewpro.com` solo se dispara con push a `cd-system`. Mergear a `main` rebuildea la imagen de devs frontend pero NO toca producción.

| Repo | URL | Rol | Quién accede |
|------|-----|-----|--------------|
| `LACOMPANIADIGITAL/cd-system` | privado | Backend Laravel + vistas master + provisión + billing | Founder + senior devs |
| `LACOMPANIADIGITAL/cd-frontend-dev` | privado | Solo vistas Blade (volumen Docker) + workflow de sync | Devs frontend (no necesitan PHP local) |

> **Por qué el split**: bajar la barrera de entrada al frontend. Un dev nuevo solo necesita Docker + git, no Composer/PHP/MySQL/keys de Airtable/Stripe. Y el PR queda como **gate de revisión** antes de tocar el monorepo de producción.

---

## 2. Arquitectura de los dos repos

```
┌──────────────────────────────────────┐         ┌──────────────────────────────────────┐
│  cd-system (PRIVADO — backend)       │         │  cd-frontend-dev (PRIVADO — frontend)│
│                                      │         │                                      │
│  Dockerfile                          │         │  docker-compose.yml                  │
│  .docker/                            │         │  .env.docker                         │
│  ├── apache.conf                     │         │  Makefile                            │
│  ├── entrypoint.sh                   │         │                                      │
│  └── switch-demo.sh                  │         │  resources/views/modules/            │
│                                      │         │  └── cd-base/frontend/demos/         │
│  app/        config/                 │         │      ├── demo-restaurant/            │
│  database/   resources/  routes/     │         │      ├── demo-law-firm-2/            │
│                                      │         │      └── ... (18 demos)              │
│  .github/workflows/                  │         │                                      │
│  ├── build-image.yml ──── push ────► │         │  .github/workflows/                  │
│  ├── build-runtime-image.yml         │         │  └── sync-to-main.yml ─── PR ──┐     │
│  └── deploy.yml                      │ ◄───────┼─────────────────────────────────┘     │
│                                      │         │                                      │
└──────────────────┬───────────────────┘         └──────────────────────────────────────┘
                   │ build & push
                   ▼
        ┌──────────────────────────────────┐
        │  GHCR — ghcr.io/                 │
        │  lacompaniadigital/cd-system     │ ◄── docker compose pull desde cd-frontend-dev
        │  :latest, :sha-<commit>           │
        │  (pública)                       │
        └──────────────────────────────────┘
```

### Roles claros

- **`cd-system`** es el "monorepo verdad". Toda lógica de negocio, migraciones, módulos, providers, y la **versión canónica** de las vistas Blade viven acá. Lo que se mergee acá es lo que va a producción.
- **`cd-frontend-dev`** es un **espejo parcial**: solo `resources/**` y `public/**`. Los devs frontend **nunca** ven `app/`, `config/`, `database/`. Levantan un container Docker con la imagen pre-buildeada y editan vistas con hot-reload (volumen).

### Qué se sincroniza, qué no

| Path | ¿Sync de cd-frontend-dev → cd-system? |
|------|----------------------------------------|
| `resources/views/**` | ✅ |
| `resources/css/**`, `resources/js/**` | ✅ |
| `public/**` (assets) | ✅ |
| `Makefile`, `docker-compose.yml`, `.env.docker` | ❌ (vive solo en cd-frontend-dev) |
| `.github/workflows/sync-to-main.yml` | ❌ (vive solo en cd-frontend-dev) |
| Cualquier cosa fuera de `resources/` y `public/` | ❌ (filtrado en el workflow) |

---

## 3. Workflows de GitHub Actions involucrados

Hay **cuatro workflows en juego**, tres en cd-system y uno en cd-frontend-dev:

### 3.1 `cd-system/.github/workflows/build-image.yml`

**Qué hace**: builda la imagen Docker `ghcr.io/lacompaniadigital/cd-system:latest` y la pushea a GHCR.

**Trigger**:
- `push` a `main` o `cd-system`, **si** los cambios tocan: `Dockerfile`, `.docker/**`, `app/**`, `bootstrap/**`, `config/**`, `database/**`, `resources/**`, `routes/**`, `composer.json`, `composer.lock`, o el propio workflow.
- `workflow_dispatch` (botón manual).

**Tags**: `latest` + `sha-<commit-corto>`.

**Por qué importa**: cuando se mergea un PR de frontend, este workflow corre y actualiza la imagen. Los devs frontend **no ven el cambio** en su entorno local hasta que hagan `docker compose pull`.

### 3.2 `cd-system/.github/workflows/deploy.yml`

**Qué hace**: SSH a VPS1 (Hostinger, `72.61.45.136`) y deploya `bewpro.com` (Laravel app del user `bewpro22`). Hace `git pull`, `composer install`, `migrate`, clear caches, smoke test HTTP.

**Trigger**: `push` a `cd-system` (branch productiva) + `workflow_dispatch`.

**Secrets requeridos**: `VPS1_SSH_HOST`, `VPS1_SSH_KEY` (ed25519 dedicada). Setup: ver [02-github-ci.md](../roadmap/02-github-ci.md) — fix completo del 2026-05-02.

> ⚠️ **No deployea contenedores Docker** — deployea el código directamente sobre el VPS productivo (cPanel + Apache). El path Docker es solo para **dev frontend local**, no para producción de bewpro.com.

### 3.3 `cd-frontend-dev/.github/workflows/sync-to-main.yml`

**Qué hace**: cuando un dev hace push a `main` con cambios en `resources/**` o `public/**`, el workflow:
1. Detecta archivos modificados con `git diff` (HEAD~1..HEAD).
2. Hace checkout de **`cd-system` rama `staging-frontend`** usando `CD_SYSTEM_PAT`.
3. Copia los archivos al mismo path.
4. Crea/actualiza PR via `peter-evans/create-pull-request@v6` con:
   - **Base**: `staging-frontend` (NO `main`, NO `cd-system`)
   - **Branch (fija)**: `frontend-sync/pending` — N pushes consecutivos actualizan el mismo PR
   - **Título (fijo)**: `[Frontend] cambios pendientes desde cd-frontend-dev`
   - **Label**: `frontend`
   - **Body**: metadatos del último push + link al run de Actions.

**Comportamiento de la branch fija**:
- Push #1 (PR no existe) → crea branch `frontend-sync/pending` + abre PR.
- Push #2, #3, #N (PR sigue abierto) → empuja commits adicionales a la **misma branch** → el PR se actualiza, no se duplica.
- Maintainer mergea → `delete-branch: true` borra `frontend-sync/pending`.
- Push #N+1 → ciclo arranca de nuevo.

> **Por qué fija**: evita el ruido de N pushes = N PRs. El maintainer ve un solo "inbox" en cd-system. Trade-off: si el PR está abierto y el dev pushea algo malo, contamina el PR. Solución: el maintainer cierra y avisa.

**Anti-loop**: el workflow skipea commits cuyo mensaje arranca con `chore: sync desde cd-system` (commits creados por el sync inverso §3.4) — sino, cada propagación maestro→dev dispararía este workflow, creando un bucle infinito.

### 3.4 `cd-system/.github/workflows/sync-to-frontend-dev.yml` (sync inverso, desde 2026-05-04)

**Qué hace**: cuando un push a `cd-system` (la branch productiva) toca paths sincronizables, propaga los cambios directo a `cd-frontend-dev/main`.

**Trigger**: `push` a **`cd-system`** (no `main` — `main` está abandonada con histories no relacionadas), **si** los cambios tocan:
- `resources/views/modules/cd-base/frontend/**`
- `resources/views/layout/front/headers/**`
- `resources/views/layout/front/footers/**`
- `resources/views/layout/front/partials/**`
- `public/template/css/demos/**`
- `public/template/css/skins/**`
- `public/template/js/demos/**`

**Acción**:
1. Detecta archivos modificados con `git diff`, filtrados a paths sincronizables.
2. Hace checkout de `cd-frontend-dev/main` con `CD_FRONTEND_DEV_PAT`.
3. Copia los archivos (incluyendo deletes con `rm`).
4. Commit con mensaje `chore: sync desde cd-system @ <sha>` y **push directo a `main`** (sin PR).

**Push directo (no PR)**: el cambio ya pasó revisión humana en cd-system vía PR de la etapa 3a (staging-frontend → main). Doble revisión sería ruido sin valor.

**Anti-loop**: skipea commits cuyo mensaje arranca con `feat(frontend): sync desde cd-frontend-dev` (creados por el sync directo §3.3).

**Por qué importa**: sin sync inverso, si el maintainer edita un archivo sincronizable directo en cd-system, el dev frontend tendría una versión vieja en su laptop y al pushear sobrescribiría el cambio del maintainer (drift silencioso). El sync inverso elimina ese riesgo.

---

### Secrets requeridos para los syncs

**En `cd-frontend-dev` (para el sync directo §3.3)**: `CD_SYSTEM_PAT`.

**Cómo crearlo / rotarlo**:
1. GitHub → avatar → Settings → Developer settings → Personal access tokens → Tokens (classic) → New token.
2. Scope: `repo`. Expiration: 12 meses (recordatorio en calendar).
3. `cd-frontend-dev` → Settings → Secrets and variables → Actions → New secret → `CD_SYSTEM_PAT`.

> Si el PAT expira, los pushes siguen funcionando pero el workflow falla en el paso "Checkout cd-system". Ver §9.

**En `cd-system` (para el sync inverso §3.4)**: `CD_FRONTEND_DEV_PAT`.

**Cómo crearlo / rotarlo**:
1. GitHub → avatar → Settings → Developer settings → Personal access tokens → Tokens (classic) → New token.
2. Scope: `repo`. Expiration: 12 meses.
3. Copiar el token y setear:
   ```bash
   gh secret set CD_FRONTEND_DEV_PAT --repo LACOMPANIADIGITAL/cd-system < /tmp/pat.txt
   rm /tmp/pat.txt
   ```
   O via UI: `cd-system` → Settings → Secrets and variables → Actions → New secret.

> Si el PAT expira, los pushes a cd-system siguen funcionando pero el workflow falla en el paso "Checkout cd-frontend-dev". Sin afectar producción.

---

## 4. Ciclo de vida de un cambio frontend

### Tres etapas explícitas

```
─────────────── ETAPA 1: dev pushea, PR se actualiza ───────────────

T+0    Dev edita resources/views/modules/cd-base/frontend/demos/demo-X/welcome.blade.php
T+1    git commit -m "feat(demo-X): nuevo hero con video"
T+2    git push origin main  (cd-frontend-dev)
       ↓
T+3    GH Action "Sync to cd-system"
       ├── git diff HEAD~1 HEAD → detecta archivos
       ├── checkout cd-system @ staging-frontend (con CD_SYSTEM_PAT)
       ├── cp archivos a cd-system-repo/
       └── peter-evans/create-pull-request@v6
            ├── Si PR sobre frontend-sync/pending NO existe → crea
            └── Si YA existe → empuja commit adicional al mismo PR
       ↓
T+4    PR abierto/actualizado en cd-system contra staging-frontend

─────────────── ETAPA 2: maintainer mergea a staging-frontend ───────────────

T+5    Maintainer revisa diff acumulado en el PR. Valida:
       - No hex colors hardcodeados (usar CSS vars del skin)
       - No rompe site.theme.fonts / brand_defaults
       - El demo correcto fue editado
       - Tests visuales OK (opcional: pull la branch local con gh pr checkout)
       ↓
T+6    gh pr merge --squash --delete-branch
       → cambios viven en staging-frontend
       → branch frontend-sync/pending borrada (próximo push abre PR fresco)

─────────────── ETAPA 3: promote a main → cd-system ───────────────

T+7    Maintainer abre PR manual: staging-frontend → main
       gh pr create --base main --head staging-frontend
       ↓
T+8    Merge → push a main → dispara build-image.yml
       └── ghcr.io/lacompaniadigital/cd-system:latest (~3-5 min)
       ↓
T+9    Maintainer abre PR (o fast-forward) main → cd-system
       gh pr create --base cd-system --head main
       ↓
T+10   Merge → push a cd-system → dispara deploy.yml
       └── SSH a VPS1 → git pull → migrate → cache clear → smoke test (~1m11s)
       ↓
T+11   bewpro.com sirve el cambio. Devs frontend pullean nueva imagen Docker:
       docker compose pull && docker compose down && docker compose up -d
```

### Resumen visual de los gates

```
cd-frontend-dev/main            cd-system/staging-frontend          cd-system/main           cd-system/cd-system
        │                                  │                              │                          │
        │  Etapa 1 (auto, ~30s)            │  Etapa 2 (manual, review)   │  Etapa 3a (manual, deps) │  Etapa 3b (manual, deploy)
        ├────────── PR ──────────────────► │ ────── PR manual ──────────► │ ──── PR manual ────────► │
        │  base: staging-frontend          │  base: main                  │  base: cd-system         │
        │  branch: frontend-sync/pending   │  head: staging-frontend      │  head: main              │
        │  (acumulativo)                   │                              │                          │
        ▼                                  ▼                              ▼                          ▼
                                    sin side effects               build-image.yml             deploy.yml
                                                                   (rebuild Docker)            (SSH a VPS1)
```

### Tiempos típicos

| Etapa | Duración | Notas |
|-------|----------|-------|
| 1. Sync auto | ~30s | Medido en runs 25324824318 / 25328278941 |
| 2. Review + merge a staging | variable (humano) | Cuello de botella natural — bien |
| 3a. Promote a main + build | ~3-5 min | Solo build Docker, no deploy |
| 3b. Promote a cd-system + deploy | ~1m11s | Disponible en bewpro.com |

**Push-to-prod si todo se mergea inmediato**: ~6-8 min. Lo normal son días/semanas porque la etapa 2 espera revisión humana.

---

## 5. Convenciones del PR

### 5.1 Título del PR auto-generado (Etapa 1)

**Fijo**: `[Frontend] cambios pendientes desde cd-frontend-dev`.

El título ya no usa el commit message del dev (cambio v2). El motivo: como el PR acumula N pushes, el commit message del último push no es representativo del contenido del PR. La info detallada vive en el body del PR.

### 5.2 Naming de branches

| Branch | Generada por | Persistencia |
|--------|--------------|--------------|
| `frontend-sync/pending` | Workflow auto, fija | Vive mientras hay PR abierto. Borrada al merge. Re-creada al próximo push. |
| `staging-frontend` | Manual, una vez | Permanente. Recibe merges del PR auto. Promovida a `main` por PR manual. |
| `main` | Manual, vía promote | Permanente. Branch trunk. |
| `cd-system` | Manual, vía promote | Permanente. **Branch productiva — push a esta dispara deploy a VPS1.** |

> ⚠️ **No renombrar `frontend-sync/pending`** — el workflow tiene el nombre hardcodeado. Si la renombrás y querés otro push, peter-evans no la va a encontrar y abrirá una branch nueva con el nombre original.

### 5.3 Merge strategy por etapa

| Etapa | Merge type | Por qué |
|-------|------------|---------|
| 1→2: PR auto → `staging-frontend` | **Squash** | El PR ya viene como bloque lógico, history del dev es sandbox |
| 2→3a: `staging-frontend` → `main` | **Merge commit** o squash | Si squasheaste en etapa 1, da igual. Merge commit deja claro de dónde vino |
| 3a→3b: `main` → `cd-system` | **Fast-forward** preferido (o merge commit) | Esta es la promoción final a producción — no introducir commits adicionales |

```bash
# Etapa 1→2 (squash)
gh pr merge <N> --repo LACOMPANIADIGITAL/cd-system --squash --delete-branch

# Etapa 2→3a (PR manual)
gh pr create --repo LACOMPANIADIGITAL/cd-system \
  --base main --head staging-frontend \
  --title "Promote frontend cambios → main" \
  --body "Mergeando staging-frontend tras validación visual."
gh pr merge <N> --repo LACOMPANIADIGITAL/cd-system --merge

# Etapa 3a→3b (PR manual a producción)
gh pr create --repo LACOMPANIADIGITAL/cd-system \
  --base cd-system --head main \
  --title "Deploy main → cd-system" \
  --body "Promoting tested main to production."
gh pr merge <N> --repo LACOMPANIADIGITAL/cd-system --merge
```

### 5.4 Label

Todos los PRs llevan label `frontend` automáticamente. Útil para filtrar:

```bash
gh pr list --repo LACOMPANIADIGITAL/cd-system --label frontend --state all
```

### 5.5 Quién aprueba

Hoy: founder o maintainer designado. Sin reglas de branch protection forzadas. **Pendiente** (ver [04-risks-and-priorities.md](04-risks-and-priorities.md)): exigir 1 review approval antes de merge en `main`/`cd-system`.

### 5.6 Cómo probar un PR antes de mergear (sin afectar nada)

Tres formas, de menos a más impacto:

```bash
# A. Local: traer la branch del PR a tu laptop sin tocar remoto
gh pr checkout <N> --repo LACOMPANIADIGITAL/cd-system
# levantás Apache local, validás visualmente, después:
git checkout cd-system

# B. Mergear a staging-frontend (cero side effects — no hay workflows que lo escuchen)
gh pr merge <N> --repo LACOMPANIADIGITAL/cd-system --squash --delete-branch
# si validás OK, promote a main y a cd-system

# C. Si tenés un VPS de staging activo, configurar para que pullee de staging-frontend
# (no implementado al 2026-05-04 — TODO en infrastructure roadmap)
```

---

## 6. Comandos del maintainer (cd-system)

Asume `gh auth status` ya autenticado como `LACOMPANIADIGITAL` con scopes `repo` + `workflow`.

### 6.1 Inspección del estado

```bash
# PRs abiertos del flujo frontend (debería ser 0 o 1)
gh pr list --repo LACOMPANIADIGITAL/cd-system --label frontend --state open

# Ver el PR pendiente (siempre frontend-sync/pending)
gh pr view <N> --repo LACOMPANIADIGITAL/cd-system
gh pr diff <N> --repo LACOMPANIADIGITAL/cd-system

# Detalles JSON (mergeable, archivos, base)
gh pr view <N> --repo LACOMPANIADIGITAL/cd-system \
  --json number,title,state,baseRefName,mergeable,mergeStateStatus,files,additions,deletions

# Diff entre main y cd-system (qué falta promover a producción)
gh api repos/LACOMPANIADIGITAL/cd-system/compare/cd-system...main \
  -q '.files[].filename'

# Diff entre staging-frontend y main (qué hay listo para promover)
gh api repos/LACOMPANIADIGITAL/cd-system/compare/main...staging-frontend \
  -q '.files[].filename'
```

### 6.2 Review (Etapa 2)

```bash
# Probar local sin mergear
gh pr checkout <N> --repo LACOMPANIADIGITAL/cd-system
# ... validá ...
git checkout cd-system

# Aprobar y mergear a staging-frontend (squash + delete-branch)
gh pr review <N> --repo LACOMPANIADIGITAL/cd-system --approve
gh pr merge <N> --repo LACOMPANIADIGITAL/cd-system --squash --delete-branch

# Pedir cambios (el dev frontend va a ver el comentario en cd-system pero
# tiene que fixearlo en cd-frontend-dev y volver a pushear)
gh pr review <N> --repo LACOMPANIADIGITAL/cd-system --request-changes \
  --body "Hardcodeaste #ff5722 en .hero-cta — usá var(--brand-primary)."
```

### 6.3 Promote staging-frontend → main (Etapa 3a)

```bash
gh pr create --repo LACOMPANIADIGITAL/cd-system \
  --base main --head staging-frontend \
  --title "Promote frontend → main ($(date +%Y-%m-%d))" \
  --body "Mergeando cambios validados en staging-frontend."

# Después del merge, build-image.yml corre solo
gh run watch --repo LACOMPANIADIGITAL/cd-system  # opcional
```

### 6.4 Promote main → cd-system (Etapa 3b — DEPLOY A PRODUCCIÓN)

```bash
gh pr create --repo LACOMPANIADIGITAL/cd-system \
  --base cd-system --head main \
  --title "Deploy → cd-system ($(date +%Y-%m-%d))" \
  --body "Promote tested main to production. Triggers deploy.yml."

# Tras mergear, ver el deploy
gh run list --repo LACOMPANIADIGITAL/cd-system --workflow=deploy.yml --limit 1
gh run watch --repo LACOMPANIADIGITAL/cd-system
```

### 6.5 Workflows y runs

```bash
# Runs recientes del workflow de sync (en cd-frontend-dev)
gh run list --repo LACOMPANIADIGITAL/cd-frontend-dev --workflow=sync-to-main.yml --limit 5

# Runs recientes del build de imagen (en cd-system)
gh run list --repo LACOMPANIADIGITAL/cd-system --workflow=build-image.yml --limit 5

# Log completo de un run específico
gh run view <run-id> --repo LACOMPANIADIGITAL/cd-frontend-dev --log
gh run view <run-id> --repo LACOMPANIADIGITAL/cd-frontend-dev --log-failed   # solo steps fallidos

# Forzar rebuild de imagen Docker manualmente
gh workflow run build-image.yml --repo LACOMPANIADIGITAL/cd-system

# Forzar deploy manualmente (con o sin cambios)
gh workflow run deploy.yml --repo LACOMPANIADIGITAL/cd-system --ref cd-system
```

### 6.6 Secrets

```bash
# Listar secrets del repo cd-frontend-dev (no muestra valores, solo nombres + fecha)
gh api repos/LACOMPANIADIGITAL/cd-frontend-dev/actions/secrets \
  -q '.secrets[] | {name, updated_at}'

# Listar secrets del repo cd-system (idem)
gh api repos/LACOMPANIADIGITAL/cd-system/actions/secrets \
  -q '.secrets[] | {name, updated_at}'

# Rotar el PAT (después de generar uno nuevo en GitHub Settings)
gh secret set CD_SYSTEM_PAT --repo LACOMPANIADIGITAL/cd-frontend-dev < /tmp/new_pat.txt
rm /tmp/new_pat.txt
```

---

## 7. Comandos del dev frontend (cd-frontend-dev)

Cubierto en detalle en [`../repo-frontend-dev/guia-de-uso.md`](../repo-frontend-dev/guia-de-uso.md). Resumen:

```bash
# Setup (primera vez)
git clone https://github.com/LACOMPANIADIGITAL/cd-frontend-dev.git
cd cd-frontend-dev
docker compose pull        # imagen pre-buildeada de GHCR
docker compose up -d       # levanta Laravel+Apache+MySQL local

# Día a día
docker compose up -d
# editar resources/views/modules/cd-base/frontend/demos/<demo>/...
# (hot reload via volumen — solo recargar browser)
docker compose down

# Cambiar demo activo
docker compose exec app switch-demo demo-law-firm-2
# o con make:
make demo DEMO=demo-law-firm-2

# Pullear nueva imagen tras un merge en cd-system
docker compose pull
docker compose down && docker compose up -d
```

---

## 8. Buenas prácticas

### 8.1 Para el dev frontend

1. **Un push = un cambio lógico**. No acumules 5 features en un solo push, porque el PR auto-generado va a ser difícil de revisar.
2. **Subject de commit en una sola línea**: usá `-m "subject"` + `-m "body"` separados. El body **no** entra al título del PR.
3. **Probá el cambio en el demo correcto antes de pushear**. `switch-demo` te lo cambia en segundos.
4. **No edites archivos fuera de `resources/`** — no se sincronizan.
5. **No hardcodees colores ni fonts**. Usar CSS variables del sistema de skin (`var(--brand-primary)`, `var(--brand-font-heading)`). Validable con el agente `demo-branding-auditor`.

### 8.2 Para el maintainer

1. **Revisá el diff antes de mergear**. El workflow no valida nada — solo copia archivos. Un dev puede meter `<script>alert(1)</script>` y el bot lo va a abrir tranquilamente.
2. **Squash en etapa 1→2** (PR auto a `staging-frontend`). Merge commit / fast-forward en etapas 3a y 3b.
3. **Mergeá la etapa 2 rápido**. Mientras `frontend-sync/pending` está abierto, todo nuevo push del dev se acumula. Si pasan días sin mergear, el PR se vuelve un blob de cambios sin contexto.
4. **Etapa 3a y 3b son independientes**. Podés mergear muchos cambios a `staging-frontend` y `main` sin deployar a producción. Cuando deployás (3b), pensalo como release: agrupá cambios coherentes.
5. **Después de mergear a `main`, verificá que `build-image.yml` corra**. Sin él, los devs frontend levantarán imagen vieja.
6. **Después de mergear a `cd-system`, verificá que `deploy.yml` haya pasado verde**. Sin él, bewpro.com sigue mostrando lo anterior.
7. **Anotá en `MEMORY.md` o memory project** si descubrís un patrón que se repite (ej. devs olvidando newline al final del archivo).

### 8.3 Auditoría periódica (mensual)

```bash
# 1. Verificar que el PAT no esté próximo a expirar
gh api repos/LACOMPANIADIGITAL/cd-frontend-dev/actions/secrets/CD_SYSTEM_PAT \
  -q '{name, updated_at}'
# Si updated_at > 10 meses → rotar

# 2. Verificar runs en falla del último mes
gh run list --repo LACOMPANIADIGITAL/cd-frontend-dev --status=failure --limit 20

# 3. Verificar PRs frontend stale (>7 días sin actividad)
gh pr list --repo LACOMPANIADIGITAL/cd-system --label frontend --state open \
  --json number,title,updatedAt,createdAt
```

### 8.4 Decisiones tomadas y por qué

- **PAT classic vs fine-grained**: classic con scope `repo`. Fine-grained no soporta `peter-evans/create-pull-request` consistentemente al momento del setup.
- **Branch productiva en cd-system es `cd-system`, no `main`**: legacy del repo, `main` es trunk pero `cd-system` es lo que VPS1 pullea (deploy.yml escucha solo a `cd-system`).
- **3 niveles de branch (staging-frontend / main / cd-system)** en lugar de 1: separar "zona de revisión" de "trunk listo para release" de "producción" hace cada merge un gate consciente. Aceptamos más fricción a cambio de menos accidentes.
- **Branch fija `frontend-sync/pending`** en lugar de `frontend-sync/<run-id>`: evita ruido de N pushes = N PRs. Trade-off: 2 devs en simultáneo contaminan el mismo PR.
- **No usamos `peter-evans/create-pull-request@v7`**: v6 funciona. Bumpear cuando GitHub fuerce Node 24 (deadline 2026-06-02).

---

## 9. Troubleshooting

### "El PR no apareció en cd-system tras pushear a cd-frontend-dev"

```bash
# 1. ¿El push tocó archivos en resources/ o public/?
gh api repos/LACOMPANIADIGITAL/cd-frontend-dev/commits/<sha> \
  -q '.files[].filename'

# 2. ¿El workflow corrió?
gh run list --repo LACOMPANIADIGITAL/cd-frontend-dev --workflow=sync-to-main.yml --limit 3

# 3. Si falló: ver log
gh run view <run-id> --repo LACOMPANIADIGITAL/cd-frontend-dev --log-failed
```

Causas comunes:
- Push solo tocó `Makefile`/`.env.docker`/`docker-compose.yml` → workflow no se dispara (filter `paths`).
- Archivo idéntico al que ya está en cd-system → `peter-evans` no abre PR si no hay diff real.

### "El workflow falla en 'Checkout cd-system' con 'Bad credentials'"

→ El PAT expiró o fue revocado. Ver §3.3 para recrearlo.

```bash
# Regenerar y subir
gh secret set CD_SYSTEM_PAT --repo LACOMPANIADIGITAL/cd-frontend-dev < /tmp/new_pat.txt
# Re-disparar el workflow del último commit
gh workflow run sync-to-main.yml --repo LACOMPANIADIGITAL/cd-frontend-dev --ref main
```

### "Mergeé el PR pero los devs frontend no ven el cambio"

→ La imagen Docker no se rebuildeó o no la pullearon.

```bash
# 1. Verificar que build-image.yml corrió tras el merge
gh run list --repo LACOMPANIADIGITAL/cd-system --workflow=build-image.yml --limit 3

# 2. Si no corrió: el merge no tocó paths que disparan el workflow.
#    Verificar el filter en .github/workflows/build-image.yml
#    (resources/** debería estar incluido y lo está)

# 3. Forzar rebuild manual
gh workflow run build-image.yml --repo LACOMPANIADIGITAL/cd-system

# 4. Avisar a los devs que pulleen
# En su laptop:
docker compose pull && docker compose down && docker compose up -d
```

### "El workflow falla con 'Reference does not exist' en checkout staging-frontend"

→ La branch `staging-frontend` no existe en cd-system. Probable causa: alguien la borró por error.

```bash
# Recrearla desde main
SHA=$(gh api repos/LACOMPANIADIGITAL/cd-system/git/refs/heads/main -q '.object.sha')
gh api repos/LACOMPANIADIGITAL/cd-system/git/refs -X POST \
  -f ref="refs/heads/staging-frontend" -f sha="$SHA"

# Re-disparar el workflow del último push
gh workflow run sync-to-main.yml --repo LACOMPANIADIGITAL/cd-frontend-dev --ref main
```

### "El PR `frontend-sync/pending` se abre con dos devs mezclados"

→ Esperado con la configuración actual. Si pasa frecuentemente, considerar:
- Convención: 1 dev frontend en vuelo (Slack/coordinación humana)
- Pasar a branches por-dev (modificar workflow para usar `frontend-sync/${{ github.actor }}`)
- Mergear más seguido para reducir la ventana donde 2 pueden chocar

### "El sync abrió un PR pero el diff está vacío"

→ Bug raro de `peter-evans/create-pull-request` cuando el `cp` no copia nada (paths inexistentes). Cerrar el PR y abrir issue en cd-frontend-dev para revisar el step "Detectar archivos modificados".

---

## 10. Bloque para pegar como contexto IA

Copiá lo siguiente al inicio de un prompt cuando le pidas a Claude/GPT/etc. ayuda con esto:

```
Contexto: Sistema BewPro (codebase técnico llamado cd-system, Laravel 9 multitenant).

Hay 2 repos GitHub bajo LACOMPANIADIGITAL (privados):

1. cd-system (monorepo backend completo) — 4 branches relevantes:
   - cd-system    → branch PRODUCTIVA. Push a esta dispara deploy.yml (SSH a VPS1 Hostinger
                    72.61.45.136 → bewpro.com). Secrets: VPS1_SSH_HOST, VPS1_SSH_KEY.
   - main         → branch trunk. Push a esta dispara build-image.yml (rebuild de
                    ghcr.io/lacompaniadigital/cd-system:latest).
   - staging-frontend → branch zona de revisión para cambios frontend. Sin workflows
                        que la escuchen — sin side effects al mergear ahí.
   - frontend-sync/pending → branch efímera, fija. Vive solo cuando hay PR frontend
                              abierto. Borrada al mergear. Re-creada al próximo push.

2. cd-frontend-dev (espejo parcial)
   - resources/views/modules/cd-base/frontend/, resources/views/layout/front/{headers,footers,partials}/,
     public/template/{css/{demos,skins},js/demos}/ + docker-compose.yml + docs/arquitectura.md
   - Devs frontend: Docker + Git (no necesitan PHP/Composer/MySQL)
   - .github/workflows/sync-to-main.yml → en push a main con cambios en resources/**
     o public/**, hace checkout de cd-system @ staging-frontend, copia archivos,
     y crea/actualiza PR via peter-evans/create-pull-request@v6:
       base: staging-frontend
       branch: frontend-sync/pending  (FIJA — N pushes acumulan en mismo PR)
       title: "[Frontend] cambios pendientes desde cd-frontend-dev"
       label: frontend
       (skipea commits "chore: sync desde cd-system" para evitar bucle)
   - Secret: CD_SYSTEM_PAT (PAT classic, scope repo, sobre cd-system)

Sincronización bidireccional desde 2026-05-04 — además del workflow anterior:
   cd-system tiene .github/workflows/sync-to-frontend-dev.yml que detecta pushes
   a main tocando paths sincronizables (los mismos 7 paths del repo cd-frontend-dev)
   y los pushea directo (sin PR) a cd-frontend-dev/main con commit
   "chore: sync desde cd-system @ <sha>". Skipea commits del sync inverso.
   Secret: CD_FRONTEND_DEV_PAT (PAT classic, scope repo, sobre cd-frontend-dev).
   Resultado: el dev frontend siempre tiene la última versión maestra al hacer git pull.

Flujo end-to-end (3 etapas):
  Etapa 1 (auto, ~30s): dev push cd-frontend-dev/main → PR auto
                        en cd-system contra staging-frontend (acumulativo).
  Etapa 2 (humano):     maintainer revisa, mergea PR auto (squash) → cambios
                        en staging-frontend. Cero workflows disparados.
  Etapa 3a (manual):    PR manual staging-frontend → main → mergea →
                        build-image.yml rebuildea Docker (~3-5 min) Y
                        sync-to-frontend-dev.yml propaga a cd-frontend-dev/main.
  Etapa 3b (manual):    PR manual main → cd-system → mergea → deploy.yml
                        SSH a VPS1 → bewpro.com servido (~1m11s).

Convenciones:
  - El PR frontend tiene título FIJO: "[Frontend] cambios pendientes desde cd-frontend-dev".
    El detalle de qué cambió vive en el body (acumulado por los N pushes).
  - Squash en etapa 1→2. Merge commit o fast-forward en 3a y 3b.
  - No hardcodear hex colors en blades (usar CSS vars del sistema de skin).
  - Para probar un PR sin afectar nada: gh pr checkout <N> --repo LACOMPANIADIGITAL/cd-system

CLI: gh CLI autenticado como LACOMPANIADIGITAL con scopes repo + workflow.
Doc maestro: docs/bewpro2.0/infrastructure/06-frontend-pr-workflow.md
Doc dev frontend: docs/bewpro2.0/repo-frontend-dev/{guia-de-uso, anatomia-de-un-demo, sync-architecture}.md

[Tu pregunta acá ↓]
```

> Mantener este bloque corto y autoexplicativo es lo que permite obtener buenas respuestas sin tener que explicar todo el sistema cada vez. Si actualizás el flujo, **actualizá este bloque** primero — es el que vas a copiar 100 veces.

---

## 11. Cambios v1 → v2 (2026-05-04)

| Aspecto | v1 (hasta 2026-05-04) | v2 (ahora) |
|---------|----------------------|------------|
| `base` del PR auto | `main` (default branch) | **`staging-frontend`** |
| Nombre de la branch del PR auto | `frontend-sync/<run-id>` (única por push) | **`frontend-sync/pending`** (fija, acumulativa) |
| ¿1 push = 1 PR? | Sí | **No — N pushes actualizan el mismo PR** |
| Título del PR | `[Frontend] <commit subject>` | `[Frontend] cambios pendientes desde cd-frontend-dev` (fijo) |
| Etapas hasta producción | 1 merge (a main) + manual fast-forward a cd-system | **3 merges explícitos** (staging-frontend → main → cd-system) |
| Aislamiento para testing | Solo `gh pr checkout` local | `gh pr checkout` + branch staging-frontend dedicada sin side effects |
| Riesgo de PR ruidosos | Alto si dev pushea seguido | Bajo — un solo PR vivo a la vez |
| **Sincronización** | Unidireccional (dev → maestro) — drift silencioso si maintainer edita directo en cd-system | **Bidireccional** — sync inverso propaga cambios maestros a cd-frontend-dev/main automáticamente |
| **Workflows** | 3 (build-image, deploy, sync-to-main) | **4** (+ sync-to-frontend-dev) |
| **Anti-loop** | N/A | Filter por mensaje de commit en ambos workflows |

**Migración**:
- PR #7 (que existía en v1 contra `main`, branch `frontend-sync/25328278941`) NO se autoadapta. Si se mergea o se cierra, los próximos pushes ya usarán el flujo v2.
- Se creó la branch `staging-frontend` desde el HEAD de `main` el 2026-05-04 (commit `2ad8a00e`).
- Workflow actualizado en `cd-frontend-dev` commit `5a8c2ed`.

**Razones del cambio** (decisión registrada):
- Mejor aislamiento: ahora hay un buffer (`staging-frontend`) entre el bot y `main`/`cd-system` (productiva).
- Menos ruido: 1 PR vivo a la vez en lugar de N=cantidad-de-pushes.
- Etapas más explícitas: separar "validar cambios" de "promover a main" de "deployar a producción" hace cada gate ¬más claro.

**Trade-off aceptado**:
- Más merges manuales en estado normal (3 en lugar de 1-2). Aceptable porque cada uno es un gate explícito y rápido (`gh pr merge`).
- Si dos devs frontend pushean en paralelo, contaminan el mismo PR. Mitigación: convención de "1 dev frontend a la vez en flight" o crear branch por-dev en futuras versiones.

---

## Referencias cruzadas

- [`../repo-frontend-dev/guia-de-uso.md`](../repo-frontend-dev/guia-de-uso.md) — guía operativa del dev frontend (setup, comandos, troubleshooting)
- [`../repo-frontend-dev/anatomia-de-un-demo.md`](../repo-frontend-dev/anatomia-de-un-demo.md) — qué archivos componen un demo en cd-frontend-dev
- [`../repo-frontend-dev/sync-architecture.md`](../repo-frontend-dev/sync-architecture.md) — las 3 capas (repo / volumes / workflow)
- [docs/bewpro2.0/roadmap/02-github-ci.md](../roadmap/02-github-ci.md) — fix histórico del deploy.yml (2026-05-02)
- [docs/bewpro2.0/infrastructure/01-secrets-inventory.md](01-secrets-inventory.md) — inventario completo de secrets
- [docs/bewpro2.0/infrastructure/03-runbook-recovery.md](03-runbook-recovery.md) — qué hacer cuando algo se rompe
