Herramientas de usuario

Herramientas del sitio


clase:daw:daw-proyecto:tema04

Diferencias

Muestra las diferencias entre dos versiones de la página.

Enlace a la vista de comparación

Ambos lados, revisión anteriorRevisión previa
Próxima revisión
Revisión previa
clase:daw:daw-proyecto:tema04 [2026/10/14 11:49] – Gramática única de comandos: /git-flow start|commit|update|finish|publish, app.sh, /sdd spec create|impl, /test create|run|repair, /developer review code|test|ci; un solo camino con spec; revisión que no bloquea claudeclase:daw:daw-proyecto:tema04 [2026/10/14 14:08] (actual) – Tema nuevo «2. IA y agentes»: los temas 2 a 7 pasan a ser 3 a 8, con sus referencias cruzadas renumeradas claude
Línea 1: Línea 1:
-====== 4. IA en el proyecto ======+====== 4. Docker en el proyecto ======
  
-Objetivo del tema: usar bien al agente para programar. No se enseña a programar; se enseña a **pedir** el trabajo como una especificación y a comprobar que ha salido bien. Al acabar, el proyecto tiene su primer CRUD completo hecho así y la arquitectura del proyecto escrita donde el agente la lee antes de diseñar nada.+Objetivo del tema: cada worktree tiene **su propia aplicación completa funcionando** (base de datos, backend y frontend), sin chocar con las de los demás worktrees. Al acabar, ''/git-flow start'' deja el worktree con sus puertos asignados y ''./app.sh start'' levanta la aplicación del issue con datos.
  
-Lo que ya está contado en el [[cursos:sdd:start|curso de programación con IA]] y aquí no se repite: qué es el [[cursos:sdd:10-spec-driven-development|Spec-driven development]] y por qué encaja con los agentes, [[cursos:sdd:11-kiro|cc-sdd (Kiro)]] con sus fases y su memoria del proyecto, y la [[cursos:sdd:04-ingenieria#ingenieria_de_contexto|ingeniería de contexto]].+Lo que ya sabéis y aquí no se repite: imágenes y contenedores ([[clase:daw:daw:1eval:tema03|Despliegue 3]]), Dockerfile ([[clase:daw:daw:1eval:tema04|Despliegue 4]]), Docker Compose ([[clase:daw:daw:1eval:tema04.b|Despliegue 4.b]]) y redes y nginx-proxy ([[clase:daw:daw:1eval:tema04.c|Despliegue 4.c]]). Automatizar Maven y Node: [[clase:daw:proyecto:1eval:tema03|Proyecto 3]].
  
-===== Un solo camino: la spec =====+===== Tres contenedores =====
  
-En este proyecto todo issue se hace igual, sea un CRUD o un cambio pequeño: primero una **especificación** en ficheros (requisitos, diseño, tareas), revisada por el alumno, y después la implementación con el contexto limpio. No hay un segundo camino: un camino es una gramática que aprender; dos son dos, y una regla para elegir cada vez.+La aplicación son tres contenedores, uno por caja del diagrama C4 de contenedores:
  
-La spec la genera **cc-sdd** (la versión libre del flujo de Kiro), pero el alumno no escribe sus comandos: los envuelve una skill del proyecto, ''/sdd'', con dos subcomandos. Así el vocabulario es el del tema 1 y no el de la herramienta. cc-sdd se instala una vez en la raíz del repositorio:+^ Servicio ^ Imagen ^ Qué hace ^ 
 +| ''db'' | ''mariadb'' | La base de datos. Solo accesible desde el backend | 
 +| ''backend'' | Construida: Maven → JRE | El JAR de Spring Boot en el puerto 8080 | 
 +| ''frontend'' | Construida: Angular → Nginx | Sirve el Angular compilado y hace de proxy de ''/api'' al backend |
  
-<code> +El navegador solo habla con ''frontend''. Todo lo que empieza por ''/api'' lo reenvía Nginx al backend, así el frontend no necesita saber en qué puerto está el backend y no hay problemas de CORS.
-npx cc-sdd@latest --claude-skills --lang es      (OpenCode: --opencode-skills) +
-</code>+
  
-Ese es el flujo de cada issue:+Los dos Dockerfile son **multi-stage** y los escribe la IA. Lo único que hay que vigilar:
  
-<uml> +<sxh bash;title:backend/Dockerfile> 
-start +FROM maven:3.9-eclipse-temurin-25 AS build 
-:/git-flow start N +WORKDIR /app 
-y ./app.sh start; +COPY pom.xml . 
-:/sdd spec create +RUN --mount=type=cache,target=/root/.m2 mvn -q dependency:go-offline 
-requisitos, diseño y tareas +COPY src ./src 
-en .kiro/specs/N-slug/; +RUN --mount=type=cache,target=/root/.m2 mvn -q -DskipTests package
-repeat +
-  :Leer y corregir los tres ficheros; +
-repeat while (¿hay algo que cambiar?) is (sí: /sdd spec create otra vez) +
-:/clear; +
-:/sdd spec impl +
-una tarea tras otra, +
-tests unitarios y de arquitectura +
-después de cada una; +
-:Probar en el navegador; +
-stop +
-</uml>+
  
-==== /sdd spec create ====+FROM eclipse-temurin:25-jre 
 +RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/* 
 +COPY --from=build /app/target/*.jar /app.jar 
 +ENTRYPOINT ["java", "-jar", "/app.jar"] 
 +</sxh>
  
-Se lanza en el worktree del issue, sin argumentos: el número del issue está en el nombre de la rama. Lo que hace, por dentro, son las cuatro fases de cc-sdd encadenadas:+El ''curl'' es para el ''healthcheck'' de abajo. El ''--mount=type=cache'' sobre ''.m2'' es lo que hace que reconstruir la imagen tarde segundos y no minutos: sin él, cada ''build'' vuelve a descargar todas las dependencias.
  
-  - ''/kiro-spec-init'' con el título y el cuerpo del issue (''gh issue view N''). +<sxh bash;title:frontend/Dockerfile> 
-  - ''/kiro-spec-requirements'': los requisitos y los **criterios de aceptación**. +FROM node:22 AS build 
-  - ''/kiro-spec-design -y'': el diseño, leyendo antes el //steering// con la arquitectura del proyecto (abajo). +WORKDIR /app 
-  - ''/kiro-spec-tasks -y'': la lista de tareas, en orden y pequeñas.+COPY package*.json . 
 +RUN npm ci 
 +COPY . . 
 +RUN npx ng build --configuration production
  
-Y se para. El resultado son tres ficheros en ''.kiro/specs/<N>-<slug>/'': ''requirements.md'', ''design.md'' y ''tasks.md''. Antes de generar nada, si el issue no deja claro algo (¿el NIF es único?), pregunta.+FROM nginx:alpine 
 +COPY nginx.conf /etc/nginx/conf.d/default.conf 
 +COPY --from=build /app/dist/frontend/browser /usr/share/nginx/html 
 +</sxh>
  
-**Revisar esos ficheros es el trabajo humano del flujo.** Es mucho más barato corregir una frase de la spec que corregir el código que sale de ella. Qué mirar en cada uno:+<sxh plain;title:frontend/nginx.conf> 
 +server { 
 +    listen 80; 
 +    root /usr/share/nginx/html;
  
-^ Fichero ^ Qué tiene que tener ^ +    location /api/ { 
-| ''requirements.md'' | Los criterios de aceptación en frases **comprobables** («al guardar un cliente sin NIF, el formulario muestra el error y no se crea»), y entre ellos **los casos de error**: campo vacío, valor repetido, id que no existe. En el tema 5 cada frase se convierte en un test | +        proxy_pass http://backend:8080/api/; 
-| ''design.md'' | Que es **igual** que el CRUD de referencia del proyecto: las mismas capas, los mismos nombres, migración y datos de prueba. Si no lo es, lo que falla no es el diseño sino el steering (abajo): se corrige el steering y se vuelve a generar | +    }
-| ''tasks.md'' | Tareas pequeñas y en orden: migración y datos, backend, frontend. Cada una se puede hacer y probar sola |+
  
-Si se cambia algo en ''requirements.md'', se vuelve a lanzar ''/sdd spec create'': rehace el diseño y las tareas a partir de los requisitos corregidos. Se repite hasta que los tres ficheros digan lo que se quiere.+    location / { 
 +        try_files $uri $uri/ /index.html; 
 +    } 
 +} 
 +</sxh>
  
-==== /sdd spec impl ====+El ''try_files'' es obligatorio en una aplicación Angular: sin él, recargar el navegador en ''/clientes/12'' da un 404 de Nginx.
  
-Conversación nueva (''/clear'') y una sola orden:+===== Producción y desarrollo con los mismos ficheros =====
  
-<code> +Docker Compose carga ''compose.yaml'' y, si existe, ''compose.override.yaml'' encima. Eso se usa así:
-/sdd spec impl +
-</code>+
  
-Por dentro es ''/kiro-impl'' sobre las tareas pendientes de la spec del worktree, una detrás de otra. Después de cada tarea pasa los tests unitarios y de arquitectura (''/test run unit'', tema 5) y la marca como hecha en ''tasks.md''. Si un test falla por un error del código, se para en esa tarea y lo dice, en vez de retocar el test para que pase. Cuando termina, el alumno prueba la aplicación en la URL del worktree y, si algo no está bien, se le pide en la misma conversación.+  * ''compose.yaml'' **es producción**: imágenes construidas, la base de datos en una red interna sin puertos publicados, el frontend en la red de nginx-proxy con su ''VIRTUAL_HOST''. 
 +  * ''compose.override.yaml'' **cambia lo de desarrollo**. Está en el repositorio y Compose lo carga solo. En el servidor, el workflow de despliegue (tema 7) fija ''COMPOSE_FILE=compose.yaml'' y el override no se aplica.
  
-El mismo comando sirve más adelante para las tareas que añade la revisión (tema 5): ''/sdd spec impl'' hace lo que quede pendiente en ''tasks.md'', venga de donde venga.+<sxh yaml;title:compose.yaml> 
 +services: 
 +  db: 
 +    image: mariadb:11 
 +    environment: 
 +      MARIADB_DATABASE: proyecto 
 +      MARIADB_USER: proyecto 
 +      MARIADB_PASSWORD: ${DB_PASSWORD} 
 +      MARIADB_ROOT_PASSWORD: ${DB_PASSWORD} 
 +    volumes: 
 +      - db-data:/var/lib/mysql 
 +    networks: 
 +      - interna 
 +    healthcheck: 
 +      test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] 
 +      interval: 5s 
 +      retries: 20
  
-Por qué el ''/clear'' entre los dos: la conversación de especificar arrastra todo lo que el agente ha leído y descartado mientras pensaba. Ese ruido empeora el resultado y encarece cada petición. Con la spec en ficheros, esa conversación es desechable.+  backend: 
 +    build: ./backend 
 +    environment: 
 +      SPRING_DATASOURCE_URL: jdbc:mariadb://db:3306/proyecto 
 +      SPRING_DATASOURCE_USERNAME: proyecto 
 +      SPRING_DATASOURCE_PASSWORD: ${DB_PASSWORD} 
 +    depends_on: 
 +      db: 
 +        condition: service_healthy 
 +    networks: 
 +      - interna 
 +    healthcheck: 
 +      test: ["CMD", "curl", "-f", "http://localhost:8080/api/hola"] 
 +      interval: 5s 
 +      retries: 20
  
-==== La carpeta de la spec ====+  frontend: 
 +    build: ./frontend 
 +    environment: 
 +      VIRTUAL_HOST: <alumno>.proyectosdaw.cip.fpmislata.com 
 +    depends_on: 
 +      backend: 
 +        condition: service_healthy 
 +    networks: 
 +      - interna 
 +      - proxy
  
-''.kiro/specs/'' se sube a git: la spec viaja con la rama, entra en ''develop'' en el mismo commit que el código, y las skills del tema 5 la usan para generar los tests y para revisar que el código hace lo que se pidió.+volumes: 
 +  db-data:
  
-Es un **registro** de lo que se pidió, no documentación: cuando el código cambie después, la spec no se actualiza. Para que el agente no se confunda con specs viejas, ''AGENTS.md'' lleva esta línea:+networks: 
 +  interna: 
 +  proxy: 
 +    external: true 
 +    name: nginx-proxy-network 
 +</sxh>
  
-<code> +Los ''healthcheck'' con ''depends_on: condition: service_healthy'' hacen que el backend no arranque hasta que MariaDB acepte conexiones, y el frontend hasta que el backend responda. Sin eso, el backend muere al arrancar porque la base de datos aún no está lista.
-.kiro/specs/ es histórico: la fuente de verdad es el código. No leas specs de otros issues salvo que te lo pidan. +
-</code>+
  
-==== La skill /sdd ====+<sxh yaml;title:compose.override.yaml> 
 +services: 
 +  db: 
 +    ports: 
 +      - "${DB_PORT:-3306}:3306"
  
-Es corta, porque el trabajo lo hacen los comandos de cc-sdd. Recibe el subcomando en ''$ARGUMENTS'' y hace una cosa u otra:+  backend: 
 +    ports: 
 +      - "${BACKEND_PORT:-8080}:8080" 
 +    environment: 
 +      SPRING_PROFILES_ACTIVE: ${PERFIL:-datos-prueba}
  
-<sxh yaml;title:.claude/skills/sdd/SKILL.md (cabecera)> +  frontend: 
---- +    build: 
-name: sdd +      target: build 
-description: Especificar e implementar el issue del worktree actual con cc-sdd. Subcomandos: "spec create" (requisitos, diseño y tareas en .kiro/specs/N-slug/) y "spec impl" (implementa las tareas pendientes). +    command: npx ng serve --host 0.0.0.0 --proxy-config proxy.conf.json 
-allowed-tools: Bash(gh issue view:*), Bash(git branch:*), Bash(mvn:*), Read, Write, Edit, Skill +    volumes: 
----+      - ./frontend:/app 
 +      - /app/node_modules 
 +    ports: 
 +      - "${FRONTEND_PORT:-4200}:4200"
 </sxh> </sxh>
  
-Todas las skills de tarea del proyecto son así: ''/<familia> <verbo> <objeto>'', con el subcomando en los argumentos. Es lo que hace que ''/test run e2e'' y ''/test create e2e'' sean la misma skill con dos subcomandos.+Lo que cambia en desarrollo:
  
-===== El primer CRUD =====+  * **Se publican los puertos**, para entrar desde el navegador y para que Maven y Playwright lleguen a la base de datos y al frontend desde el host. 
 +  * **El frontend pasa a ''ng serve''** con la carpeta local ''frontend/'' montada dentro: al guardar un fichero, el navegador se recarga. ''proxy.conf.json'' manda ''/api'' a ''%%http://backend:8080%%''. El volumen anónimo sobre ''node_modules'' evita que los módulos instalados en la imagen se tapen con los del host. 
 +  * **El backend no tiene recarga en caliente**: cada cambio en Java es ''docker compose up --build backend''. Con la caché de Maven son unos segundos. Y no pasa nada. 
 +  * **Datos de prueba** activados (abajo).
  
-La práctica central del tema es el primer CRUD del proyecto, hecho con ''/sdd''. Pero hay un problema de huevo y gallina: el diseño tiene que salir como la arquitectura del proyecto, y la arquitectura todavía no está escrita en ningún sitio que el agente lea.+<note important>La red ''nginx-proxy-network'' es externa: en el servidor la crea nginx-proxy, pero en el portátil hay que crearla una vez, ''docker network create nginx-proxy-network'', o Compose se niega a arrancar. ''app.sh start'' lo hace si no existe.</note>
  
-Se resuelve con el primer CRUD y solo con él: se genera la spec, y en ''design.md'' el alumno **corrige a mano** todo lo que no sea la arquitectura que ya conocéis de Programación y DWS (capas ''controller'' / ''domain'' / ''persistence'', ''Repository'' definido en ''domain'' e implementado en ''persistence'', DTO ''XxxRequest'' / ''XxxResponse'', ''XxxJpaEntity'', MapStruct, ''ApiExceptionHandler''). Aquí no se inventa una arquitectura nueva. Después de implementarlo se lee **clase por clase** y se corrige hasta que esté como tiene que estar: es el código de referencia de todo lo que viene después, y de él sale el steering.+===== Un compose por worktree =====
  
-===== El steering: las dos arquitecturas =====+Dos worktrees con el mismo ''compose.yaml'' crearían contenedores con el mismo nombre y pedirían los mismos puertos. Lo resuelve un fichero ''.env'' **en cada worktree**, ignorado por git, que Compose lee solo:
  
-Para que el segundo CRUD salga igual que el primero **de verdad**, con las mismas capas, los mismos nombres y los mismos tests, no basta con pedirlo bien: el agente tiene que tener la arquitectura escrita y leerla antes de diseñar. Eso es el //steering// de cc-sdd, y ''/sdd spec create'' lo carga en la fase de diseño. Cómo funciona está en [[cursos:sdd:11-kiro#la_memoria_del_proyecto|la memoria del proyecto]]; aquí solo lo que es de este proyecto: que tiene **dos arquitecturas**, la del cliente y la del servidor, y cómo se le cuentan.+<sxh bash;title:.env> 
 +COMPOSE_PROJECT_NAME=mi-proyecto-12-alta-clientes 
 +DB_PORT=33112 
 +BACKEND_PORT=8112 
 +FRONTEND_PORT=4212 
 +DB_PASSWORD=dev 
 +</sxh>
  
-==== Cuándo se genera ====+  * ''COMPOSE_PROJECT_NAME'' es el prefijo de contenedores, redes y volúmenes de ese worktree. Dos proyectos distintos no se tocan. 
 +  * Los puertos los busca ''/git-flow start'' al crear el worktree: prueba a partir de una base y se queda con los libres. 
 +  * Las variables llevan valor por defecto, ''${BACKEND_PORT:-8080}'', para cuando no hay ''.env'': en CI (tema 7).
  
-''/kiro-steering'' describe lo que ve en el código. Por eso se lanza **después del primer CRUD**, ya revisado, y no antes: con el esqueleto vacío describiría nada, y con un CRUD mal hecho convertiría el error en norma. Se lanza una vez, a mano, en la raíz del repositorio:+''/git-flow finish'' termina con ''docker compose down -v'': se van los contenedores **y el volumen** de la base de datos de ese worktree. No queda nada.
  
-<code> +===== La primera tabla =====
-/kiro-steering +
-</code>+
  
-Los tres ficheros que genera, ''product.md'', ''tech.md'' y ''structure.md'', entran en el contexto **siempre**, en todas las fases de todas las specs. Así que se revisan y se dejan cortos, con solo lo común: qué es el proyecto, que es Spring Boot más Angular más MariaDB, el reparto ''backend/'' y ''frontend/'', los comandos de arrancar y probar y las dos ramas.+Hasta ahora el backend no tiene base de datos. Se añaden ahora, con un prompt directo al agente (el flujo de trabajo de verdad llega en el tema 5; esto es solo para tener algo que ver):
  
-==== Dos arquitecturas, dos ficheros ====+  * Dependencias ''spring-boot-starter-data-jpa'', ''mariadb-java-client'' y ''flyway-mysql''. 
 +  * Una migración ''%%backend/src/main/resources/db/migration/V1__clientes.sql%%'' con una tabla. 
 +  * Un ''GET /api/clientes'' que la lista y una página de Angular que la muestra.
  
-Lo específico de cada parte no va en esos tres, sino en dos ficheros de steering **condicionales**, que solo entran en el contexto cuando la spec toca esa carpeta. Se crean con ''/kiro-steering-custom'' y llevan en la cabecera el patrón de ficheros que los activa:+El esquema **siempre** va en migraciones de Flyway, ''%%V<n>__descripcion.sql%%'', y nunca con ''ddl-auto''. Así cada worktree, cada runner de CI y el servidor construyen la misma base de datos desde cero.
  
-<sxh plain;title:.kiro/steering/backend.md> +===== Datos de prueba =====
---- +
-inclusion: fileMatch +
-fileMatchPattern: "backend/**" +
---- +
-# Arquitectura del servidor +
-... +
-</sxh>+
  
-Y ''frontend.md'' igual, con ''%%frontend/**%%''. **Concretar las dos arquitecturas es trabajo del alumno**: ''/kiro-steering-custom'' pone la cabecera y un borrador; el contenido se escribe mirando el CRUD de referencia, y tiene que responder a esto:+Cada worktree arranca con la base de datos vacía: no hay nada que ver en la aplicación ni se pueden escribir tests E2E que dependan de que exista un cliente. Los datos de prueba se separan del esquema:
  
-^ ''backend.md'' ^ ''frontend.md'' ^ +<code> 
-| Las capas ''controller'' / ''domain'' / ''persistence'' y qué va en cada paquete | Carpeta por entidad y qué componentes tiene (listado y formulario) | +backend/src/main/resources/db/ 
-| Nombres: ''XxxController'', ''XxxService'', ''Xxx'' (dominio), ''XxxRepository'' en ''domain'' y ''XxxRepositoryImpl'' en ''persistence'', ''XxxJpaEntity'', ''XxxRequest'' / ''XxxResponse'', el mapper de MapStruct | Nombres de ficheros, de componentes y de rutas (''/clientes'', ''/clientes/nuevo'', ''/clientes/:id'') | +├── migration/            <- esquema: V1__clientes.sql, V2__pedidos.sql ... 
-| Dónde se valida (Bean Validation en el ''Request'') y cómo se devuelven los errores: ''ApiExceptionHandler'', códigos 400 / 404 / 409 y el formato del JSON de error | El servicio HTTP de cada entidad y cómo se llama a la API | +└── datos-prueba/         <- datos: R__clientes.sql, R__pedidos.sql ... 
-| Migraciones ''%%V<n>__%%'' y datos de prueba ''%%R__%%'', con un ejemplo | Formularios reactivos y cómo se muestran las validaciones | +</code>
-| Cómo se prueban los unitarios: JUnit 5 y Mockito, sin Spring | ''data-testid'' en todo lo que un test toca, nunca selectores por clase CSS |+
  
-La lección: **lo que el steering no dice, el agente lo inventa distinto cada vez.** Si el segundo CRUD sale distinto del primero, falta una línea en el steering, no un prompt mejor.+Los de ''datos-prueba/'' son migraciones **repetibles** de Flyway (''%%R__%%''): se ejecutan después de todas las versionadas, y como la base de datos de un worktree siempre nace vacía, se ejecutan una vez. Flyway solo mira esa carpeta cuando está activo el perfil ''datos-prueba'':
  
-==== Las skills de convenciones ====+<sxh plain;title:application-datos-prueba.properties> 
 +spring.flyway.locations=classpath:db/migration,classpath:db/datos-prueba 
 +</sxh>
  
-El steering solo lo leen los comandos de cc-sdd. Las demás skills del proyecto (''/test'', ''/developer review'') también necesitan saber cómo se escribe el código aquí. Para eso hay dos **skills de conocimiento** (no se invocan: el agente las carga solo cuando va a tocar backend o frontend) que no repiten nada: remiten al steering.+  * En desarrollo el perfil lo activa ''compose.override.yaml''. Para probar la aplicación vacía: ''PERFIL=default docker compose up -d --build backend''. 
 +  * En producción ''compose.yaml'' no activa el perfil: los datos de prueba **nunca llegan al servidor**. 
 +  * Para volver al estado inicial (antes de pasar los E2E, por ejemplo): ''docker compose down -v && docker compose up -d''.
  
-<sxh yaml;title:.claude/skills/convenciones-backend/SKILL.md> +Cada entidad nueva trae su ''%%R__<entidad>.sql%%'' con unos cuantos registros; el steering de cc-sdd (tema 6) obliga a que cada CRUD lo traiga.
---- +
-name: convenciones-backend +
-description: Cómo se escribe el código del backend en este proyecto (capas, nombres, DTO, migraciones, tests). Cargar siempre antes de crear, modificar o revisar cualquier clase Java, test de Java o fichero SQL. +
---- +
-Lee .kiro/steering/backend.md y aplícalo. Es la única fuente de la arquitectura del servidor. +
-</sxh>+
  
-Y ''convenciones-frontend'' igual, hacia ''frontend.md''. Una sola fuente: cuando cambia una convención, cambia en un sitio.+===== El script app.sh =====
  
-===== El contexto del agente =====+Operar la aplicación es siempre lo mismo y no necesita ninguna inteligencia, así que no es una skill: es un script de bash en la raíz del repositorio, con cuatro subcomandos y la gramática del tema 1. Bash está en los tres sistemas (en Windows lo trae Git, tema 3). Y es el mismo script que se usa en el servidor (tema 7): no hay una forma de operar en local y otra en producción.
  
-El contexto es todo lo que el modelo tiene delante en una conversación: ''AGENTS.md'', las skills cargadas, el steering, los ficheros que ha leído, la salida de los comandos que ha ejecutado y todo lo que se ha hablado. Cada mensaje lo reenvía entero. Cuanto más lleno, peor trabaja y más cuesta. Reglas:+<sxh bash;title:app.sh> 
 +#!/bin/bash 
 +# La aplicación de este worktree: start | stop [-v] | status | logs 
 +set -e 
 +cd "$(dirname "$0")" 
 +case "$1" in 
 +  start) 
 +    docker network inspect nginx-proxy-network >/dev/null 2>&1 || docker network create nginx-proxy-network 
 +    docker compose up -d --build --wait 
 +    [ -f .env ] && . ./.env 
 +    echo "Aplicación en http://localhost:${FRONTEND_PORT:-4200}" ;; 
 +  stop)   shift; docker compose down "$@" ;; 
 +  status) docker compose ps ;; 
 +  logs)   docker compose logs --tail=200 ;; 
 +  *)      echo "uso: $0 start | stop [-v] | status | logs"; exit 1 ;; 
 +esac 
 +</sxh>
  
-  * **Una conversación por tarea.** ''/clear'' entre tareas. Lo que tenga que sobrevivir, a un fichero. +  * ''./app.sh start'' vale para todo: la primera vez, después de cambiar el backend (reconstruye la imagen y la vuelve a levantar) y al día siguiente tras un ''stop''. ''--wait'' espera a los ''healthcheck'', así que cuando escribe la URL la aplicación ya responde. 
-  * **El modelo bueno para especificar, el barato para implementar** cosas claras. ''/model'' en Claude Code, ''/models'' en OpenCode. +  * ''./app.sh stop'' para los contenedores y conserva la base de datos. ''./app.sh stop -v'' la borra también: es lo que hace ''/git-flow finish'' al cerrar el issue, y lo que se hace para volver a los datos de prueba iniciales. 
-  * **No pegar logs enteros.** Solo el error y las líneas de alrededor. +  * ''./app.sh status'' y ''./app.sh logs'' son lo primero que se mira cuando algo no responde. 
-  * **Mirar el contador.** ''/context'' y ''/cost'' en Claude Code; OpenCode lo muestra junto a la caja de entrada. Cuando el contexto pasa de la mitad, o se cierra la tarea o se hace ''/compact''.+  * Trabaja sobre la carpeta en la que está y lee su ''.env'', así que cada worktree opera el suyo. En el servidor no hay ''.env'': valen los valores por defecto.
  
-===== MCP =====+<note>Los scripts se guardan con finales de línea de Linux o bash en Windows no los entiende. Una línea en ''.gitattributes'' lo garantiza para todos: ''*.sh text eol=lf''.</note>
  
-Dos servidores MCP y ninguno más (qué es un MCP: [[cursos:sdd:02-skills-agentes-mcp#mcp|MCP]]):+===== Dónde corren Maven y los tests =====
  
-  * **Context7**: documentación actualizada de Spring Boot y Angular. Sin él, el agente escribe código de la versión que recuerda, que no es la que usáis. +Las imágenes son de **ejecución**: el backend es un JRE sin Maven y el frontend es Nginx. Así que compilar y pasar tests se hace **en el host**, que para eso están instalados el JDK, Maven y Node:
-  * **Playwright MCP**: el agente abre la aplicación en un navegador y la ve. Sirve para que compruebe por sí mismo que una pantalla funciona, y en el tema 5 para generar los tests E2E.+
  
-Se configuran con ''claude mcp add'' en Claude Code y en ''opencode.json'' en OpenCode, siguiendo la página de cada servidor.+^ Qué ^ Dónde ^ Contra qué ^ 
 +| ''mvn test'' (unitarios y ArchUnit) | ''backend/'' del worktree | Nada: no necesitan la aplicación levantada | 
 +| ''npm run test:api'' (API) | ''frontend/'' del worktree | ''%%http://localhost:$FRONTEND_PORT/api%%'' | 
 +| ''npm run test:e2e'' (E2E) | ''frontend/'' del worktree | ''%%http://localhost:$FRONTEND_PORT%%'' |
  
-===== Permisos del agente =====+Son los tres comandos de tests del proyecto. Maven y npm funcionan igual en Linux, Windows y Mac, y son lo que ejecutan el alumno, el agente y CI (tema 7). ''mvn test'' forma parte del build y corre en cada compilación. Los de API y los E2E **no forman parte del build**: se lanzan aparte, cuando se quiere, con los otros dos comandos. Los dos son de Playwright y atacan la aplicación levantada del worktree (tema 6). En el tema 6 la skill ''/test'' los envuelve con la gramática del tema 1 (''/test api run'') y explica lo que sale en rojo; CI los ejecuta tal cual.
  
-En el tema 2 se permitió lo básico. Para programar, el agente tiene que poder compilar, pasar tests y levantar la aplicación sin preguntar en cada paso. La lista de ''.claude/settings.json'' queda así:+==== npm run ====
  
-  * **Permitido**: ''mvn'', ''npm'', ''npx'' (incluido ''npx playwright''), ''docker compose'', ''./app.sh'', ''gh issue'', ''gh run'' y las lecturas de git (''status'', ''diff'', ''log'', ''worktree list''). +''npm run <nombre>'' ejecuta un comando guardado en la sección ''scripts'' del ''package.json''. Es la forma de tener en el proyecto los comandos largos con un nombre corto, sin depender del sistema operativo: npm los ejecuta desde la carpeta del ''package.json'' y con los programas de ''node_modules/.bin'' en el PATH, así que ''playwright'' se escribe sin ''npx''.
-  * **Pregunta**: escribir ficheros fuera del worktree, ''docker'' a secas, cualquier ''curl'' a un sitio que no sea ''localhost''. +
-  * **Denegado**: ''git push --force'', ''rm -rf'', y cualquier ''git'' que cambie el repositorio (''commit'', ''merge'', ''push''), que solo puede ejecutar ''/git-flow'' por su ''allowed-tools''.+
  
-En OpenCode, lo mismo en la clave ''permission'' de ''opencode.json''.+<sxh json;title:frontend/package.json> 
 +"scripts": { 
 +  "start": "ng serve", 
 +  "build": "ng build", 
 +  "test:api": "docker compose down -v && docker compose up -d --wait && playwright test --project=api", 
 +  "test:e2e": "docker compose down -v && docker compose up -d --wait && playwright test --project=e2e" 
 +} 
 +</sxh> 
 + 
 +Los dos scripts resetean la base de datos del worktree, esperan a que la aplicación esté sana y pasan los tests de un proyecto de Playwright (''api'' o ''e2e'', tema 6). El ''&&'' lo entienden igual ''cmd'' y bash. ''npm run'' sin nombre lista los scripts que hay. ''start'' y ''build'' ya los trae Angular: son los que ejecutan ''npm start'' y ''npm run build''. 
 + 
 +Esto va en ''AGENTS.md'' tal cual, porque es lo que el agente ejecuta para comprobar su trabajo. Si no lo sabe, intentará ''mvn test'' dentro del contenedor y fallará, o ni probará.
  
 ===== Ejercicios ===== ===== Ejercicios =====
  
 ==== Ejercicio 1 ==== ==== Ejercicio 1 ====
-  * Configura Context7 y el MCP de Playwright en Claude Code (''claude mcp add'') y en OpenCode (''opencode.json''). +  * Crea un issue «Aplicación en Docker» e inícialo con ''/git-flow''. 
-  * Comprueba que funcionan: pide al agente la forma actual de declarar una ruta en Angular y que abra la aplicación del worktree y te diga qué ve. +  * Pide al agente los dos ''Dockerfile'', ''compose.yaml'', ''compose.override.yaml'', ''frontend/nginx.conf'' y ''frontend/proxy.conf.json'' siguiendo este tema. Revisa que el backend tiene la caché de ''.m2'', que ''db'' no publica puertos en ''compose.yaml'' y que ''nginx.conf'' tiene el ''try_files''. 
-  * Completa ''.claude/settings.json'' con los permisos de este tema y súbelo.+  * Añade ''.env'' al ''.gitignore'' y crea uno a mano en el worktree con ''COMPOSE_PROJECT_NAME'', los tres puertos y ''DB_PASSWORD''. 
 +  * ''docker network create nginx-proxy-network'' y ''docker compose up -d --build''. Comprueba con ''docker compose ps'' que los tres servicios están //healthy//. 
 +  * Entra en ''%%http://localhost:<FRONTEND_PORT>%%'' y comprueba que se ve el ''/api/hola''.
  
 ==== Ejercicio 2 ==== ==== Ejercicio 2 ====
-  * Crea un issue «Skill /sdd» e inícialo con ''/git-flow start''. +  * En el mismo worktree, pide al agente que añada JPA, MariaDB y Flyway, la migración ''%%V1__clientes.sql%%'', un ''GET /api/clientes'' y una página de Angular que liste los clientes. 
-  * Instala cc-sdd en la raíz del repositorio y comprueba que ''/kiro-spec-init'' aparece como skill. +  * Crea ''application-datos-prueba.properties'' y ''db/datos-prueba/%%R__clientes.sql%%'' con cinco clientes. 
-  * Escribe la skill ''/sdd'' con los dos subcomandos: ''spec create'' (lee el issue del nombre de la rama, pregunta lo que no esté claro y encadena las cuatro fases de cc-sdd, con ''-y'' en diseño y tareas) y ''spec impl'' (''/kiro-impl'' de las tareas pendientes, tests después de cada una, y parar si un test falla por el código). +  * ''docker compose up -d --build backend'' y comprueba que la lista muestra los cinco clientes. 
-  * Añade a ''AGENTS.md'' la línea sobre ''.kiro/specs/''. Finaliza el issue.+  * Arranca con ''PERFIL=default'' tras un ''down -v'' y comprueba que la lista sale vacía. Vuelve a dejarlo con datos. 
 +  * Cambia un texto de la página de Angular, guarda y comprueba que el navegador se recarga solo.
  
 ==== Ejercicio 3 ==== ==== Ejercicio 3 ====
-  * Crea un issue para la primera entidad de tu proyecto con un CRUD completo (por ejemplo «CRUD de productos») e inícialo. +  * Añade a ''application.properties'' la URL con ''${DB_PORT:3306}'' y ejecuta ''mvn test'' desde ''backend/''. Tiene que pasar sin nada levantado. 
-  * ''/sdd spec create''. Lee ''requirements.md'': comprueba que los criterios de aceptación son comprobables y que están los casos de error. Corrige lo que falte y vuelve a generar. +  * Escribe en ''AGENTS.md'': que la aplicación se opera con ''./app.sh start | stop | status | logs'', que los tests se lanzan en el host con ''mvn test'', ''npm run test:api'' y ''npm run test:e2e'', y que el esquema va siempre en migraciones. 
-  * Lee ''design.md'' y corrígelo a mano hasta que sea la arquitectura de Programación y DWS, clase por clase, con migración y datos de prueba. Lee ''tasks.md'' y comprueba que las tareas son pequeñas y van en orden. +  * ''/git-flow commit'' y ''/git-flow finish''.
-  * ''/clear'' y ''/sdd spec impl''. Comprueba que marca las tareas hechas y que pasa los tests después de cada una. +
-  * Prueba la aplicación en la URL del worktree: listado, alta, edición, borrado y las validaciones. Lo que no esté bien se pide en la misma conversación. +
-  * Lee el código generado clase por clase y corrígelo: es la referencia de todos los CRUD que vienen después.+
  
 ==== Ejercicio 4 ==== ==== Ejercicio 4 ====
-  * En el mismo worktree, lanza ''/kiro-steering''. Lee los tres ficheros y déjalos cortos, con solo lo común. +  * Escribe ''app.sh'' y el ''.gitattributes''. Pruébalos: arrancar, cambiar un texto del backend, volver a arrancar y ver el cambio; parar, arrancar y ver que los datos siguen; parar con ''-v'', arrancar y ver los datos de prueba de nuevo. 
-  * Con ''/kiro-steering-custom'' crea ''backend.md'' y ''frontend.md'' condicionales y rellénalos con la tabla del tema, mirando el CRUD que acabas de hacer. +  * Amplía ''/git-flow'': ''start'' escribe el ''.env'' del worktree buscando puertos libres y dice cómo arrancar; ''finish'' ejecuta ''./app.sh stop -v'' antes de borrar el worktree. 
-  * Escribe ''convenciones-backend'' y ''convenciones-frontend'' apuntando a ellos. Comprueba que la ''description'' dice cuándo cargarlas. +  * Finaliza el issue. 
-  * ''/clear'' y pide un cambio pequeño en el backend. Comprueba que el agente carga ''convenciones-backend'' sin que se lo digas. + 
-  * ''/git-flow commit'' y ''/git-flow finish''. Comprueba que la spec ha entrado en ''develop'' en el mismo commit que el código. +==== Ejercicio 5 ==== 
-  * Mira ''/context'' al final de la conversación de implementación y anota cuánto ha ocupado.+  * Crea dos issues cualesquiera, inícialos y arranca los dos con ''./app.sh start''. Comprueba que cada worktree tiene su ''.env'' con puertos distintos y que las dos aplicaciones están levantadas a la vez (''docker ps''). 
 +  * Cambia los datos de prueba en uno de los dos y comprueba que el otro no se entera. 
 +  * Finaliza los dos y comprueba con ''docker ps -a'' y ''docker volume ls'' que no queda nada de ellos.
  
clase/daw/daw-proyecto/tema04.1791971341.txt.gz · Última modificación: por claude