Saltearse al contenido

Configuración de Claude Code para Mi Plante

El pair programmer fantasma — colaboración con IA

Este es el manual de programación en pareja con IA para el código de Mi Plante usando Claude Code (la CLI oficial de Anthropic). Es opinado, específico al código y construido sobre el conocimiento validado del conjunto de auditoría (docs/audit/01 hasta docs/audit/16).

Si eres dev nuevo en Mi Plante, lee esto una vez antes de tu primera sesión. Luego mantenlo abierto como referencia.


1. Por qué Claude Code para Mi Plante

Llegaste en frío. El equipo de devs anterior ya no está. El código está en producción. Los clientes lo están usando para comprar a crédito. Tienes una semana para volverte productivo.

Claude Code es la salida más rápida de ese hueco porque:

  • El código usa nomenclatura de dominio en español. Venta, Cuota, Cupo, Aliado, Empresa, Sucursal, Pagare, Estrato, Mora, HDC. Si haces grep a ciegas con términos en inglés, no encuentras nada. Claude Code lee CLAUDE.md automáticamente y aprende el diccionario en cada sesión.
  • La lógica de negocio no está documentada en el código. Un Cliente debe completar un pipeline de aprobación crediticia de 7 fases que toca 4 APIs financieras colombianas. Nada de eso está en los comentarios del controller. El conjunto de auditoría en docs/audit/ sí. Claude puede leer ambos a la vez.
  • Las particularidades de integración no son obvias. DataCrédito tiene un proxy mock en el entorno local. Certicámara antepone test_ a los nombres de documento en local. Experian se escribe EXPIRIAN en las variables de entorno. Todo esto es fácil de equivocar y Claude lo tiene escrito.
  • Hay varios landmines sin arreglar en producción. El flujo del pagaré de Certicámara genera cuotas dos veces, GenerarCreditoDeVenta no tiene handler failed(), el handler de excepciones de la API no retorna su respuesta, y el composable de la wishlist está roto. Necesitas saber esto ANTES de editar cualquier cosa. docs/onboarding/04-the-landmines/ los documenta y Claude lo lee.

Claude Code no reemplaza tu criterio. Acelera la carga de contexto, ejecuta ediciones mecánicas, corre las mismas búsquedas que tú correrías y te ahorra releer el mismo documento de auditoría diecisiete veces. Trátalo como un pair programmer, no como un oráculo.


2. Instalación

Claude Code se distribuye vía npm. Necesitas Node 18+.

Ventana de terminal
npm install -g @anthropic-ai/claude-code

Después de instalar, corre claude --version para confirmar. La primera vez que inicies claude en cualquier directorio, te pedirá autenticarte. Usa las credenciales de Anthropic que te dé Martin o el líder técnico.

La documentación completa de referencia vive en https://docs.claude.com/en/docs/claude-code.

Si tu organización restringe los globales de npm, instala por proyecto con npm install --save-dev @anthropic-ai/claude-code e invoca vía npx claude.


3. Checklist de primera ejecución para un dev de Mi Plante

Recorre esto exactamente la primera vez que abras el repo.

Ventana de terminal
cd ~/code/ext-miplante # o donde lo hayas clonado
claude # inicia la sesión en este directorio

Al arrancar, Claude Code imprime los archivos CLAUDE.md cargados arriba de la sesión. Como mínimo deberías ver:

  • CLAUDE.md raíz (resumen del proyecto, diccionario español, invariantes de arquitectura, landmines conocidos)
  • app/Services/CLAUDE.md si haces cd a ese directorio o si lo carga una búsqueda recursiva

Si no ves el resumen del proyecto en el output inicial, no estás en la raíz del repo. Detente, haz cd al lugar correcto y reinicia.

Luego corre estos tres prompts de prueba en orden:

Prueba 1 — “¿Qué es Mi Plante?”

¿Qué es Mi Plante en un párrafo? Usa el CLAUDE.md de la raíz del repo como fuente.

Esbozo esperado: Claude responde con “marketplace fintech colombiano de crédito operando en Cali”, menciona Cliente, Aliado, cupo, factura de servicios EMCALI. Si dice “una plataforma de e-commerce” sin ninguno de esos términos en español, algo anda mal con la carga de CLAUDE.md.

Prueba 2 — “¿Cuáles son los landmines conocidos en app/Services/VentaService.php?”

Según el CLAUDE.md raíz y docs/onboarding/04-the-landmines, ¿cuáles son los landmines conocidos en app/Services/VentaService.php? Lista cada uno con número de línea.

Esbozo esperado: Claude lista como mínimo registrarEnCerticamara() siempre retornando false en :499, generación de cuotas leyendo created_at en :582 / :657 / :704 (la columna no existe en Venta), doble generación de cuotas en :293, y el bug de doble multiplicación de cantidad en procesarCarrito(). Si responde “este servicio está bien testeado y no tiene problemas conocidos”, no estás en el repo correcto o CLAUDE.md no se está cargando.

Prueba 3 — “¿Cómo está estructurado el pipeline de aprobación crediticia?”

Camíname las 7 fases del pipeline de aprobación crediticia. Referencia docs/audit/12 y los controllers bajo app/Http/Controllers/AprobarCliente/.

Esbozo esperado: Fases 0 (completar registro vía EMCALI), 1 (legal check de TransUnion), 2 (identidad Experian), 3 (generación OTP), 4 (verificación OTP), 5a/5b (preguntas basadas en conocimiento, condicionales), 6 (validación HDC de DataCrédito), 7 (aprobar y extender). Debe mencionar que la aprobación requiere >= 5 ProcessTypes con FINISH_SUCCESS este mes, no una secuencia estricta.

Si las tres pruebas responden correctamente, tu contexto está cargado. Continúa.


4. La caja de herramientas de slash commands

Mi Plante incluye slash commands personalizados de Claude Code bajo .claude/commands/. Codifican los flujos más comunes del equipo. Los vas a usar constantemente.

/onboarding-day-1

Camina por el plan del Día 1: contexto de negocio, puntos de entrada al tour del código, checklist de setup de dev y las primeras lecturas asignadas en docs/audit/.

> /onboarding-day-1

Esbozo del output:

Plan del Día 1 para un dev nuevo en Mi Plante:
1. Leer docs/audit/README.md y el CLAUDE.md raíz (30 min)
2. Hojear docs/audit/06-architecture-diagram.md y 12-credit-approval-workflow-diagram.md (45 min)
3. Correr composer dev localmente y confirmar que la home carga (1 hr)
4. Leer docs/onboarding/04-the-landmines/01-known-bugs.md de principio a fin (1 hr)
5. Hacer pairing con el líder técnico para un recorrido de una compra de cliente vía /checkout (30 min)
...

/explain-this-service [NombreServicio]

Inmersión profunda en un servicio: métodos públicos, qué hace cada uno, los sistemas externos que toca, las escrituras a base de datos que realiza y cualquier landmine conocido.

> /explain-this-service VentaService

Esbozo:

app/Services/VentaService.php — el servicio más grande del proyecto, dueño de la creación de ventas y la matemática de cuotas.
Métodos públicos:
- obtenerVentas(array $filtros) — listado paginado con filtros por rango de fechas, usuario, sucursal, estado
- crearVenta(CrearVentaDTO) — envuelve en DB::transaction, despacha GenerarCreditoDeVenta si el pagaré está firmado
- generarCuotas(Venta) — matemática de cuotas, llamado en la creación de venta
- generarCuotasPorOrden(...) — variante para órdenes multi-empresa
- registrarEnCerticamara(Cliente, CrearVentaDTO) — LANDMINE: siempre retorna false en :499
Sistemas externos tocados: Certicámara (vía CerticamaraService), job GenerarCreditoDeVenta (que llama a SHIVAM)
Escrituras a BD: orden_compras, ventas, venta_detalles, cuotas, beneficiario, precios.inventario (decremento)
Landmines conocidos:
- :293 + ProcesarPagareDigital generación duplicada de cuotas
- :582/:657/:704 las cuotas leen $venta->created_at, pero Venta extiende Modelo que renombra a creado_en — usa now() silenciosamente
- :499 retorna false incondicionalmente
...

/find-landmines-near [archivo o dir]

Busca landmines conocidos que tocan un archivo o directorio dado. Úsalo ANTES de editar zonas riesgosas.

> /find-landmines-near app/Services/VentaService.php

Esbozo:

3 landmines críticos en este archivo:
- :499 — registrarEnCerticamara siempre retorna false; el camino real del código es vía el webhook de Certicámara -> ValidarPagareDigital -> ProcesarPagareDigital
- :293 — generarCuotas corre dos veces; ProcesarPagareDigital lo vuelve a correr en :80
- :582/:657/:704 — matemática de fechas contra created_at; la columna es creado_en
Landmines adyacentes (mismo flujo):
- app/Jobs/GenerarCreditoDeVenta.php — sin handler failed(), decremento no atómico de cupo (:93), marca OrdenCompra PROCESADA por-venta (:91)
- bootstrap/app.php:55 — el resultado de ApiExceptionHandler nunca se retorna

/trace-a-flow [nombre-flujo]

Traza completa de request para un flujo nombrado.

> /trace-a-flow checkout

Esbozo:

Flujo de checkout (Cliente compra con crédito):
1. GET /checkout — VentaController@checkoutIndex (middleware: auth, cliente_registro_completo, verificar_cliente_presenta_mora, consultar_cupo_cliente)
2. POST /mis-compras — VentaController@procesarCarrito (solo auth — LANDMINE: bypassa cliente_registro_completo + mora)
3. VentaService::crearVenta(CrearVentaDTO) dentro de DB::transaction
- Crea OrdenCompra
- registrarEnCerticamara() — crea pagaré, retorna false (LANDMINE), así que el dispatch sincrónico de GenerarCreditoDeVenta nunca se dispara
- Crea Venta(s), VentaDetalle, decrementa inventario
- generarCuotas() — genera el primer set de cuotas
4. El usuario firma el pagaré en el flujo web de Certicámara
5. Certicámara golpea el webhook POST /webhooks/certicamara/pagare-firmado — CerticamaraController@pagareFirmado
6. dispatch(new ValidarPagareDigital(uuid))->onQueue('pagares')
7. ValidarPagareDigital llama a CerticamaraService::verificarPagare(), confirma la firma
8. dispatch(new ProcesarPagareDigital(ordenCompra))->onQueue('pagares')
9. ProcesarPagareDigital — vuelve a correr generarCuotas() (LANDMINE: duplica) y despacha GenerarCreditoDeVenta por venta
10. GenerarCreditoDeVenta — llama a SHIVAM (CoreCreditoService), crea cliente+crédito, decrementa cupo (LANDMINE: no atómico en :93), marca OrdenCompra PROCESADA (LANDMINE: por-venta en :91)

/add-aliado-feature

Checklist para agregar una funcionalidad a la sección del aliado (portal del partner).

> /add-aliado-feature

Esbozo:

Checklist para una funcionalidad del portal aliado:
1. Ruta: agregar bajo routes/ally/web.php dentro del grupo middleware auth:app
2. Controller: bajo app/Http/Controllers/Aliado/ — validar, construir DTO, llamar al servicio
3. DTO: bajo app/DTOs/<Dominio>/ con fromArray() y toArray()
4. Servicio: bajo app/Services/ — nunca recibir arrays crudos del request
5. Página Vue: bajo resources/js/pages/ally/<seccion>/ — reusar AllyLayout
6. Nombre de ruta Inertia: aliado.<seccion>.<accion> (Ziggy genera el helper JS)
7. Props compartidas: empresa se expone vía HandleInertiaRequests para el guard app
8. Autorización: los aliados usan el string users.rol; chequear vía in_array($rol, ['aliado', 'empleado', 'administrador', 'administrador_comercial']) — Spatie Permission está instalado pero no cableado
9. Feature test: bajo tests/Feature/Aliado/ — usar RefreshDatabase, la cola corre sync

/safe-to-refactor [archivo o área]

Evaluación de riesgo de refactor para un archivo o directorio dado.

> /safe-to-refactor app/Services/VentaService.php

Esbozo:

Riesgo: ALTO. No refactorices sin alineación explícita.
- Dueño de la generación de cuotas (matemática financiera, visible al cliente)
- Toca Certicámara (pagaré legalmente vinculante)
- Tres landmines conocidos, dos de los cuales siguen sin arreglar
- Testeado en tests/Feature/Market/VentaTest.php (cobertura parcial)
- Listado en "Do Not Refactor Without Asking" del CLAUDE.md raíz
Recomendación: solo arreglos tácticos quirúrgicos. Sin rename estructural. Abre un plan en chat primero, consigue el visto bueno del líder técnico, luego cambia una cosa a la vez.

Estos comandos viven en .claude/commands/. Están en git. Si un comando se porta mal, edita el archivo .md correspondiente y mejóralo — así es como el equipo los curaduría.


5. Jerarquía de CLAUDE.md en este repo

Claude Code carga archivos CLAUDE.md desde múltiples ubicaciones y los mezcla en orden de prioridad. En Mi Plante deberías esperar ver:

ArchivoSe carga cuandoContiene
CLAUDE.md (raíz del repo)SiempreResumen del proyecto, diccionario en español, invariantes de arquitectura, landmines conocidos
app/Services/CLAUDE.mdCuando trabajas en app/Services/Patrón Service-DTO, catálogo de 29 servicios, plantilla de servicio para API externa, antipatrones
app/Jobs/CLAUDE.mdCuando trabajas en app/Jobs/Convenciones de colas, la cadena de jobs pagaré->crédito, patrón failed(), comportamiento de reintento
app/Http/Controllers/AprobarCliente/CLAUDE.mdCuando trabajas en los controllers de aprobación crediticiaFases del pipeline, estado en cache entre fases, cadena de middleware, límite diario de intentos
~/.claude/CLAUDE.md (tu directorio personal)SiempreTus preferencias personales, nunca se commitea

La mezcla es aditiva — Claude Code no reemplaza el contexto del proyecto con el del directorio. Ambos cargan. El contexto del directorio enfoca el alcance.

Si creas un CLAUDE.md por directorio, mantenlo pequeño y específico. El archivo raíz es el contexto general; los archivos por directorio son el contexto enfocado para zonas calientes.

Para revisar qué se cargó, pregúntale a Claude:

Lista cada archivo CLAUDE.md que se cargó en esta sesión y una oración de qué cubre cada uno.

6. Configuración de permisos

Por defecto Claude Code pregunta antes de cualquier tool call que pueda modificar estado. Para Mi Plante eso es demasiado ruidoso en operaciones de lectura y no lo suficientemente estricto en operaciones de escritura. Usa este settings.json para afinarlo.

Pon esto en ~/.claude/settings.json para tus preferencias personales, o en .claude/settings.json en el repo para defaults a nivel de equipo que se commitean.

{
"permissions": {
"allow": [
"Read(*)",
"Glob(*)",
"Grep(*)",
"Bash(git status)",
"Bash(git log:*)",
"Bash(git diff:*)",
"Bash(git branch:*)",
"Bash(git show:*)",
"Bash(ls:*)",
"Bash(php artisan route:list)",
"Bash(php artisan route:list:*)",
"Bash(php artisan tinker:*)",
"Bash(php artisan test:*)",
"Bash(composer test)",
"Bash(composer show:*)",
"Bash(npm run lint:*)",
"Bash(npm run format:check)"
],
"ask": [
"Edit(*)",
"Write(*)",
"Bash(php artisan migrate:*)",
"Bash(php artisan db:seed:*)",
"Bash(php artisan queue:*)",
"Bash(composer require:*)",
"Bash(npm install:*)",
"Bash(git checkout:*)",
"Bash(git commit:*)"
],
"deny": [
"Bash(git push:*)",
"Bash(git push --force:*)",
"Bash(php artisan migrate:fresh:*)",
"Bash(rm -rf:*)",
"Bash(php artisan db:wipe:*)"
]
}
}

Razonamiento:

  • Las operaciones de lectura (Read, Glob, Grep, Bash de solo lectura) siempre son seguras y no quieres un prompt de confirmación en cada una.
  • Edit y Write siempre deben confirmar para que puedas revisar el diff inline.
  • Las migraciones, seeds y comandos de cola cambian el estado de la BD. Confirma siempre.
  • git push está denegado. Nunca deberías hacer push desde dentro de una sesión de Claude — haz push manualmente después de revisar el commit. Lo mismo para composer require y npm install, ambos agregan dependencias y pueden introducir riesgo de cadena de suministro.

Si confías en ti mismo para saltarte la confirmación de Edit, mueve Edit(*) a allow. La mayoría de devs lo dejan en ask las primeras semanas.


7. El contexto de mock de integraciones

Mi Plante habla con cuatro APIs financieras colombianas. Cada una tiene una historia diferente de mock u override local. Claude necesita saber cuál es cuál cuando sugiere código o fixtures de test.

Tabla de referencia (también vive en el CLAUDE.md raíz):

ServicioClasePrefijo envMock local
TransUnion (legal check)LegalCheckServiceTRANSUNION_*Ninguno — debe pegarle a la API real o Http::fake() en tests
Experian CrossCore (Evidente)IdentityValidationServiceEXPIRIAN_* (nota: se escribe EXPIRIAN, no EXPERIAN)Ninguno — debe pegarle a la API real o Http::fake()
DataCrédito HDC PlusDataCreditoServiceDATACREDITO_*En entorno local, hace proxy a https://test.miplante.com/api/v1/datacredito/historial (ver DataCreditoService.php:153)
Certicámara (pagaré)CerticamaraServiceCERTICAMARA_*Nombre de documento con prefijo test_ en local
EMCALI MembresiasEmcaliMembresiaServiceEMCALI_MEMBRESIAS_API_URLCacheado 24h en éxito y en errores de cliente
SHIVAM Core CréditoCoreCreditoService, CreditoServiceCORE_CREDITO_*Ninguno — API SOAP/XML

Cuando le preguntas a Claude algo como “¿cómo testeo el flujo de validación HDC?”, debería responder con Http::fake(['*/cs/credit-history/*' => Http::response([...])]) contra el macro Http::datacredito(). Si sugiere escribir una clase DataCreditoMock, refuta — ese patrón no existe en este código.


8. Hooks recomendados

Claude Code soporta hooks de ciclo de vida. Para Mi Plante los dos hooks más valiosos son:

Hook de lint en pre-commit

Corre npm run lint y composer pint en los archivos staged antes de cualquier intento de commit desde dentro de una sesión de Claude.

En .claude/settings.json:

{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash(git commit:*)",
"hooks": [
{
"type": "command",
"command": "npm run lint && ./vendor/bin/pint --test"
}
]
}
]
}
}

Si no tienes Pint instalado (composer require --dev laravel/pint), suelta esa mitad y conserva la mitad de lint.

Notificación post-tarea

Cuando una tarea de larga duración termina — usualmente composer test o un build — obtén una notificación del sistema para poder hacer context-switch.

{
"hooks": {
"Stop": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude finished\" with title \"Mi Plante\"'"
}
]
}
]
}
}

En Linux reemplaza la línea de osascript con notify-send "Mi Plante" "Claude finished".


9. Errores comunes que los devs nuevos cometen con Claude Code

Errores de sesiones reales de onboarding en Mi Plante:

  1. No leer el output de CLAUDE.md al inicio. La sesión imprime los archivos cargados. Si te lo saltas, no sabes qué contexto tiene Claude realmente. Acostúmbrate a scrollear al inicio de cada sesión.

  2. Pedirle a Claude “arregla todos los bugs en este archivo”. Demasiado amplio. Con landmines que interactúan (p. ej. la doble generación de cuotas involucra VentaService::generarCuotas Y ProcesarPagareDigital::handle), un prompt de “arregla todo” producirá cambios inseguros que se pierden la relación. Sé siempre específico — nombra el archivo, la línea y el síntoma.

  3. Confiar en Claude con términos de dominio en español sin chequear el glosario. Claude a veces alucina una palabra en español que parece plausible pero no existe en el código. Siempre cruza referencia con docs/audit/15-glossary.md y con grep.

  4. Dejar que Claude refactorice código en zonas “do not refactor without asking”. El CLAUDE.md raíz lista seis zonas (pipeline de aprobación crediticia, flujo de Certicámara, generación de cuotas, decremento de cupo, clase base Modelo, useWishlist). Si Claude propone un cambio estructural en cualquiera de estas, detente y corre /safe-to-refactor antes de aceptar.

  5. No correr /find-landmines-near antes de editar zonas riesgosas. Esta es la forma #1 de commitear código que reintroduce o empeora un landmine existente. Hazlo un reflejo: abre el archivo, corre el slash command, lee el output, luego edita.

  6. Aceptar el código mockeado de test de Claude sin chequear el patrón real de mock. El proyecto usa Http::fake() contra los macros HTTP de Laravel. No hay clases *Mock hechas a mano. Si Claude genera un DataCreditoMock, recházalo y pide Http::fake() en su lugar.

  7. Hacer push sin revisar el diff. Incluso cuando confías en Claude para un cambio chico, scrollea por git diff antes de commitear. Claude a veces “amablemente” reformatea código cercano o reformula un comentario de español a inglés.

  8. Pedirle a Claude que “implemente” algo que los docs describen como no implementado. Los docs de auditoría describen varias rutas y funcionalidades que existen en el router pero no en el controller (p. ej. las rutas de escritura de lineas están registradas pero el controller no implementa los métodos). Si le pides a Claude que “arregle el error 500 en POST /lineas”, felizmente escribirá los métodos faltantes — lo cual no es lo que el equipo quiere. Chequea docs/audit/02-route-inventory.md primero.


10. El protocolo de pair programming de Mi Plante

Usa esto para cualquier cambio no trivial:

  1. Carga contexto. Corre /explain-this-service o /trace-a-flow en el área que vas a tocar. Lee el output.
  2. Encuentra landmines. Corre /find-landmines-near <archivo>. Anótalos.
  3. Planea en chat. Dile a Claude qué quieres hacer. Pregunta “¿qué debería tocar y qué podría romperse?” No lo dejes editar todavía.
  4. Ediciones pequeñas. Pídele a Claude que haga cambios un archivo a la vez. Revisa cada diff antes de guardar.
  5. Verifica. Corre composer test (o solo el archivo de test relevante), npm run lint, y si hay frontend involucrado, npm run dev y haz clic en el cambio en un navegador.
  6. Commitea. Escribe un mensaje claro en español o inglés (el equipo usa ambos). Referencia el archivo, el síntoma y la corrección.

Ejemplo trabajado: “Agregar un endpoint solo para admin para invalidar el cupo de un cliente específico”

Tarea: cuando un cliente es marcado por fraude, un admin debería poder hacer POST a un endpoint que ponga cupo_disponible = 0 y registre la acción.

Paso 1 — Cargar contexto.

> /explain-this-service ClienteService
> /trace-a-flow consultar-cupo

Aprendes que Cliente tiene columnas cupo_asignado, cupo_disponible, cupo_vence_en. El portal aliado ya tiene un prefijo de ruta para clientes bajo routes/ally/web.php. El patrón de chequeo de rol para admins es in_array($rol, ['administrador', 'administrador_comercial', 'administrador_financiero']).

Paso 2 — Encontrar landmines.

> /find-landmines-near app/Services/ClienteService.php
> /find-landmines-near routes/ally/web.php

El output te recuerda que Spatie Permission está instalado pero no cableado — no puedes usar Route::middleware('permission:...'). Debes hacer un chequeo inline de rol.

Paso 3 — Planear en chat.

Quiero agregar POST /aliados/clientes/{cliente}/invalidar-cupo. Debería:
- Estar en routes/ally/web.php bajo auth:app
- Restringido a los roles globales de admin (administrador, administrador_comercial, administrador_financiero)
- Poner cupo_disponible = 0 y escribir una entrada de log de auditoría
- Retornar Inertia back con un flash message
Camíname cada archivo que necesito tocar y qué podría romperse. No escribas código todavía.

La respuesta de planeación de Claude debería mencionar:

  • routes/ally/web.php — agregar la ruta
  • app/Http/Controllers/Aliado/ClienteController.php — agregar método invalidarCupo
  • app/Services/ClienteService.php — agregar método invalidarCupo(Cliente, User $admin) usando decrement o update directo (usa update(['cupo_disponible' => 0]) ya que estamos poniendo a un valor fijo, no decrementando)
  • Un log de auditoría — Mi Plante no tiene una tabla dedicada de audit-log. O escribe al canal de log database_backend_request vía Log::channel('database_backend_request')->info(...) o agrega una fila a aprobar_cupo_eventos (la tabla existente de event-sourcing; pero su esquema está atado al pipeline de aprobación, así que un canal de log personalizado es más limpio).
  • El cliente ya tiene cupo_vence_en — no lo toques; solo pon cupo_disponible en cero. El flujo de checkout existente rechazará compras porque el chequeo inline en VentaService::crearVenta línea ~157 ya lanza cuando cupo_disponible < subTotal.

Cosas que podrían romperse: nada inmediato. Pero como Spatie Permission no está cableado, debes implementar el chequeo de rol inline. Y como el string de rol vive en users.rol, un typo en el array se rompe silenciosamente.

Paso 4 — Ediciones pequeñas.

Pídele a Claude que escriba la línea de la ruta. Revisa. Guarda. Pídele a Claude que escriba el método del controller. Revisa. Guarda. Pídele a Claude que escriba el método del servicio. Revisa. Guarda.

Paso 5 — Verificar.

> /run composer test -- --filter ClienteTest

O escribe un nuevo archivo de test bajo tests/Feature/Aliado/ClienteInvalidarCupoTest.php primero (estilo TDD) y pídele a Claude que corra solo ese archivo.

También haz clic por el cambio en el navegador: ingresa como administrador, pega el endpoint con un cliente_id real, confirma que cupo_disponible es 0 en la BD, confirma que el admin obtiene un flash message.

Paso 6 — Commit.

git add routes/ally/web.php app/Http/Controllers/Aliado/ClienteController.php app/Services/ClienteService.php tests/Feature/Aliado/ClienteInvalidarCupoTest.php
git commit -m "Aliado: agregar endpoint para invalidar cupo de cliente (solo admins)"

No dejes que Claude haga push. Haz push manualmente después de una revisión de git diff @{u}.


Apéndice: referencia rápida de comandos

claude # Inicia una sesión en el dir actual
claude --version # Confirma instalación
claude --help # Ayuda completa de CLI
/help # Ayuda dentro de la sesión
/clear # Limpia historial de conversación (mantiene CLAUDE.md cargado)
/onboarding-day-1 # Plan del Día 1
/explain-this-service VentaService
/find-landmines-near app/Services/VentaService.php
/trace-a-flow checkout
/add-aliado-feature
/safe-to-refactor app/Services/VentaService.php

En la duda, pregúntale a Claude. La respuesta correcta usualmente está a un prompt bien formulado de distancia.