From 0d42e462ecb47408fe7721eb27f54015580157dc Mon Sep 17 00:00:00 2001 From: lacasoft Date: Tue, 3 Nov 2026 23:45:37 +0100 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20skill=20architecture-tests,=20de=20?= =?UTF-8?q?la=20intenci=C3=B3n=20a=20la=20restricci=C3=B3n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Los límites de CLAUDE.project.md (Responsabilidades y límites, Contextos y propiedad) eran solo intención. La skill los convierte en reglas del lint o de los tests que fallan si alguien los cruza: dependencias, interfaz pública, propiedad de los datos y ciclos, con una herramienta libre por stack o con un test sin dependencias. Antes de dar una regla por buena provoca una violación; una violación heredada queda como excepción explícita con su motivo. project-context deja los límites como "intención: sin automatizar", la baseline §7 dice que una carpeta o un módulo del framework no aíslan nada, ship-gate y reviewer los revisan, y los overlays de NestJS y Django lo advierten. --- README.md | 9 ++- shared/.claude/CLAUDE.base.md | 2 +- shared/.claude/agents/reviewer.md | 2 +- .../skills/architecture-tests/SKILL.md | 61 +++++++++++++++++++ .../.claude/skills/project-context/SKILL.md | 1 + .../skills/project-context/desde-design.md | 1 + shared/.claude/skills/ship-gate/SKILL.md | 2 +- shared/templates/PROJECT.template.md | 4 +- stacks/backend/django/.claude/CLAUDE.md | 1 + stacks/backend/nestjs/.claude/CLAUDE.md | 1 + 10 files changed, 78 insertions(+), 6 deletions(-) create mode 100644 shared/.claude/skills/architecture-tests/SKILL.md diff --git a/README.md b/README.md index 7a3f482..702e905 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Configuración `.claude` unificada para backend, frontend, mobile y blockchain. │ ├── CLAUDE.base.md # baseline universal (comportamiento, agentes, memoria, seguridad, calidad) │ ├── settings.json # hooks + permisos + statusLine │ ├── agents/ # 14 agentes (5 core + 9 especialistas) — ver "Agentes incluidos" -│ ├── skills/ # 19 skills (flujo + contexto + ingeniería + seguridad) — ver "Skills incluidas" +│ ├── skills/ # 20 skills (flujo + contexto + ingeniería + seguridad) — ver "Skills incluidas" │ ├── commands/ # /swarm, /kit:status │ ├── helpers/ # claude-imports, claude-permissions, claude-rules, hook-handler, loop, statusline │ └── templates/ # PROJECT.template.md (definición del proyecto) @@ -125,7 +125,7 @@ Además, el kit instala por stack agentes/skills externos curados (`frontend-dev **Flujo de trabajo**: `tdd` · `diagnose` · `grill-me` · `handoff` · `swarm-orchestration` · `verification` · `code-review-response` · `git-worktrees` · `loop-task` **Contexto y memoria**: `project-context` (rellena `CLAUDE.project.md` a partir del repo real) · `adr` (registro de decisiones en `docs/adr/`) -**Ingeniería/entrega**: `ci-cd` · `performance-profiling` · `migration` · `monorepo` · `release` · `ship-gate` · `iac` +**Ingeniería/entrega**: `ci-cd` · `performance-profiling` · `migration` · `monorepo` · `release` · `ship-gate` · `iac` · `architecture-tests` (convierte los límites de `CLAUDE.project.md` en reglas que fallan solas) **Seguridad**: `owasp-review` (revisión contra OWASP Top 10:2025, ASVS 5.0 y CWE; LLM y agentes 2026) Las de ingeniería se destilaron (en español, a nuestra profundidad) del catálogo MIT @@ -172,6 +172,11 @@ En Pi Agent (instalado con `--pi`) no hace falta `--add-dir`: `/skill:project-co | Decisiones clave y tradeoffs | **Un ADR por decisión** en `docs/adr/`, con la plantilla de la skill `adr` | | Supuestos sin confirmar | *Pendiente de confirmar* | +Los límites llegan como **intención**: el diseño los nombra, pero nada los comprueba todavía. La skill +`architecture-tests` los convierte en reglas del lint o de los tests que fallan si alguien los cruza +(dependency-cruiser, import-linter, Spring Modulith o ArchUnit, ArchUnitNET, deptrac; todas libres), +y anota en *Testing → Restricciones de arquitectura* cuál está automatizada y cuál sigue como intención. + Lo que el repo sí sabe (stack real, comandos, estructura) sigue saliendo del repo. El archivo anota de qué diseño salió y su fecha, así que si el diseño cambia, volver a correrlo actualiza solo lo que vino de ahí; un ADR aceptado no se edita, se reemplaza por otro. diff --git a/shared/.claude/CLAUDE.base.md b/shared/.claude/CLAUDE.base.md index 67c222a..4d75e99 100644 --- a/shared/.claude/CLAUDE.base.md +++ b/shared/.claude/CLAUDE.base.md @@ -143,7 +143,7 @@ Esto no es solo documentación: el hook `pre-bash` (`helpers/hook-handler.cjs`) - Nada de código muerto ni comentado "por si acaso". - Manejo de errores explícito; nunca tragar excepciones en silencio. - Tests para la nueva funcionalidad y para cada regresión corregida. -- **Respeta los límites** de *Arquitectura → Responsabilidades y límites* en `CLAUDE.project.md`: cada regla de negocio va en su dueño (no en el controlador, el adaptador ni el job), y ningún módulo escribe el estado de otro. Si una tarea exige cruzar un límite, dilo antes de hacerlo. +- **Respeta los límites** de *Arquitectura → Responsabilidades y límites* en `CLAUDE.project.md`: cada regla de negocio va en su dueño (no en el controlador, el adaptador ni el job), y ningún módulo escribe el estado de otro. Si una tarea exige cruzar un límite, dilo antes de hacerlo. Un límite solo está garantizado si una regla lo comprueba (*Testing → Restricciones de arquitectura*, skill `architecture-tests`): una carpeta o un módulo del framework no aíslan nada. - TypeScript/tipado **strict**; evita `any` (usa `unknown` + narrowing). - **Gate de código nuevo** (*clean as you code*): lo que cambia no introduce issues nuevos de lint, tipos ni seguridad; **cobertura ≥ 80 % de las líneas nuevas o modificadas** donde se mida; duplicación nueva ≤ 3 %. La deuda heredada no bloquea el cambio, pero no se amplía. - En lógica crítica (dinero, permisos, cálculos), la cobertura no basta: **mutation testing** (Stryker, mutmut, PIT) o **property-based testing** demuestran que los tests fallan cuando el código se rompe. diff --git a/shared/.claude/agents/reviewer.md b/shared/.claude/agents/reviewer.md index f81c115..11924e8 100644 --- a/shared/.claude/agents/reviewer.md +++ b/shared/.claude/agents/reviewer.md @@ -45,7 +45,7 @@ Asertan comportamiento, no implementación. Edge cases cubiertos: inputs vacíos N+1 (queries dentro de loops). Colecciones grandes paginadas/streamed, no cargadas enteras en memoria. Índices en columnas usadas en `JOIN`/`WHERE`. Trabajo pesado fuera del hilo crítico. ### Diseño -SOLID, DRY con criterio, niveles de abstracción coherentes, dirección de dependencias, acoplamiento/cohesión, diseño de interfaces, extensibilidad. Funciones con complejidad cognitiva > 15 se marcan con su alternativa. +SOLID, DRY con criterio, niveles de abstracción coherentes, dirección de dependencias, acoplamiento/cohesión, diseño de interfaces, extensibilidad. Funciones con complejidad cognitiva > 15 se marcan con su alternativa. Si `CLAUDE.project.md` tiene *Responsabilidades y límites*, marca el cambio que los cruza: una regla de negocio fuera de su dueño, un módulo que escribe el estado de otro, o una excepción nueva en las restricciones de arquitectura sin motivo escrito. ### Gate de código nuevo El cambio no introduce issues nuevos (lint, tipos, seguridad); cobertura ≥ 80 % de las líneas nuevas o modificadas si el proyecto la mide; duplicación nueva ≤ 3 %. La deuda heredada no bloquea, pero el diff no la amplía. Si el proyecto no mide algo de esto, dilo en vez de suponerlo. diff --git a/shared/.claude/skills/architecture-tests/SKILL.md b/shared/.claude/skills/architecture-tests/SKILL.md new file mode 100644 index 0000000..7c3bb05 --- /dev/null +++ b/shared/.claude/skills/architecture-tests/SKILL.md @@ -0,0 +1,61 @@ +--- +name: architecture-tests +description: Úsalo para convertir los límites de CLAUDE.project.md (Responsabilidades y límites, Contextos y propiedad) en reglas que fallen solas en el lint o los tests (quién importa a quién, quién escribe qué datos, qué expone cada módulo, sin ciclos), o cuando alguien dé por aislado un módulo solo porque tiene su carpeta o es un módulo del framework. +--- + +# Tests de arquitectura: de la intención a la restricción + +Las rutas que nombra esta skill (`CLAUDE.project.md`, `.claude/…`) son relativas a la raíz del proyecto, donde está `CLAUDE.md`; no a la carpeta de la skill. + +Un límite escrito en `CLAUDE.project.md` es una **intención**: se cumple mientras todos lo recuerdan. Se vuelve **restricción** cuando una regla lo comprueba en cada corrida del lint o de los tests y falla si alguien lo cruza. Las carpetas, los routers y los módulos del framework (módulos de NestJS o de Angular, apps de Django) no impiden ningún import: no des un módulo por aislado solo por eso. + +## Qué se puede comprobar + +| Límite | Regla | Ejemplo | +|---|---|---| +| Dependencias | A no importa de B, o las capas solo dependen hacia adentro | `solicitudes` no importa nada de `nomina` | +| Interfaz pública | Desde fuera, un módulo solo se usa por su entrada pública. Sus repositorios y modelos del ORM no salen de él | Otros módulos importan `saldos/index.ts`, nunca `saldos/saldo.repository.ts` | +| Propiedad de los datos | Solo el dueño importa el modelo o el repositorio de sus tablas | Solo `saldos` importa `SaldoEntity` | +| Ciclos | Ningún ciclo entre módulos | `solicitudes → aprobaciones → solicitudes` falla | +| Contratos, invariantes y recuperación | No son reglas de dependencias: son tests, y los escribe la skill `tdd` | Aprobar una solicitud no modifica el saldo | + +La regla de imports protege la propiedad de los datos en el código. Si el riesgo lo pide (dinero, datos personales, varios servicios sobre la misma base), la protección fuerte está en la base: un esquema y un usuario por módulo, con permisos solo sobre lo suyo. Propónla; no la impongas. + +## Herramientas por stack + +Todas son libres y no piden cuenta ni clave. Revisa en su documentación lo que trae la versión que instales. + +| Stack | Herramienta (licencia) | Qué usar | +|---|---|---| +| NestJS, Express, React, Next.js, Angular, React Native | dependency-cruiser (MIT) | Reglas `forbidden` con `from` y `to`; interfaz pública con `pathNot`; ciclos con `circular: true`. En un monorepo Nx, `@nx/enforce-module-boundaries` (MIT) | +| FastAPI, Django | import-linter (BSD-2-Clause) | Contratos `forbidden`, `protected` (solo una lista de módulos puede importar), `layers`, `independence` y `acyclic_siblings` | +| Spring | Spring Modulith (Apache-2.0) para un monolito modular, o ArchUnit (Apache-2.0) para reglas a la medida | `ApplicationModules.of(App.class).verify()` comprueba la interfaz pública y los ciclos entre módulos. En ArchUnit, `noClasses()…` y `slices().matching(…).should().beFreeOfCycles()` | +| .NET | ArchUnitNET (Apache-2.0) | Reglas de dependencia, y `Slices().Matching(…).Should().BeFreeOfCycles()` | +| PHP | deptrac (MIT) | Capas y las dependencias permitidas entre ellas: todo lo que no se permite, falla | +| Flutter | lakos (MIT) | `--no-cycles-allowed` para los ciclos. Para las demás reglas, el test sin dependencias (abajo) | +| Solidity | No aplica | Los límites entre contratos se protegen con control de acceso y tests de invariantes (Foundry) | + +**Sin dependencias nuevas.** Si la persona no quiere agregar una herramienta, o el stack no tiene una, escribe un test con el framework de tests del proyecto que lea los archivos fuente y falle ante un import prohibido. Es más débil: no resuelve alias, reexportaciones ni imports dinámicos. Dilo en la línea de esa restricción. + +## Proceso +1. **Lee los límites** en `CLAUDE.project.md`: *Arquitectura → Responsabilidades y límites*, *Modelo / dominio → Contextos y propiedad* y las reglas de límite de *Reglas de negocio*. Si no hay ninguno, no los inventes: propón correr `project-context`, o pregunta cuáles son los módulos y quién es dueño de qué. +2. **Ubica cada módulo en el código.** Si un límite no corresponde a ninguna carpeta o paquete, dilo antes de seguir: primero hay que decidir dónde vive. +3. **Traduce cada límite a una regla**, una por límite, con un mensaje que lo nombre (por ejemplo, "Solo Saldos escribe el saldo; ver CLAUDE.project.md"). Lo que no se pueda expresar como regla queda como intención, con el motivo. +4. **Elige la herramienta** del stack. Agregarla es instalar una dependencia de desarrollo: pide confirmación y sigue la baseline §6.1 (versión exacta, publicada hace 7 días o más y licencia revisada en su archivo LICENSE). +5. **Prueba que cada regla falla.** Introduce a propósito una violación (un import prohibido en un archivo temporal), corre la regla, confirma que falla con su mensaje y deshaz el cambio. Una regla que nunca falló no protege nada, igual que un test que nunca estuvo en rojo. +6. **Hazla correr sola.** Que forme parte del comando de lint o de tests que ya corren la CI, la skill `ship-gate` y el verificador de `loop-task`, no de un comando aparte que nadie recuerda. +7. **Si el código ya viola un límite**, no lo arregles en el mismo cambio. Deja la excepción explícita en la regla, con el archivo y el motivo, y anota la deuda en *Notas / gotchas*. La excepción no se amplía. +8. **Anota el resultado** en `CLAUDE.project.md` → *Testing* → "Restricciones de arquitectura": una línea por límite, con dónde se comprueba o "intención" y el motivo. + +```markdown +### Restricciones de arquitectura +- Solo Saldos escribe el saldo → automatizada: `.dependency-cruiser.cjs` (regla `solo-saldos-escribe-saldo`), en `npm run lint` +- Sin ciclos entre módulos → automatizada: `.dependency-cruiser.cjs` (regla `no-circular`), en `npm run lint` +- Aprobaciones no decide el saldo → intención: es comportamiento, no dependencia; lo cubre el test `aprobar-no-modifica-saldo` +``` + +## Reglas +- No digas que un módulo está "aislado", "desacoplado" o es "independiente" sin una regla que lo compruebe. Si no la hay, di "por convención". +- Las reglas reflejan los límites decididos; no inventes límites nuevos. Si ves uno que falta, propónlo. +- Una excepción sin motivo escrito es un límite borrado. +- En un monorepo, la dirección entre paquetes la dan sus dependencias declaradas (skill `monorepo`); estas reglas cuidan los límites dentro de cada paquete. diff --git a/shared/.claude/skills/project-context/SKILL.md b/shared/.claude/skills/project-context/SKILL.md index c046bea..94463b8 100644 --- a/shared/.claude/skills/project-context/SKILL.md +++ b/shared/.claude/skills/project-context/SKILL.md @@ -36,6 +36,7 @@ Si partiste de un `design.md`, pregunta solo lo que el diseño no responde. Si n ## 5. Escribe y cierra - Elimina el comentario de plantilla y las secciones que no aplican; deja las preguntas abiertas en una sección final **Pendiente de confirmar**. - Si encontraste decisiones de arquitectura importantes sin registrar, propón ADRs (skill `adr`) en vez de narrarlas en este archivo. +- Si trasladaste límites (*Responsabilidades y límites*, *Contextos y propiedad*), quedan como intención: propón la skill `architecture-tests` para convertirlos en reglas que fallen solas. - Lo que aprendiste del repo que no cabe aquí pero conviene recordar (dónde vive algo, trampas) va a la memoria del agente `researcher`. - Resume a la persona qué se confirmó, qué se infirió y qué quedó pendiente. diff --git a/shared/.claude/skills/project-context/desde-design.md b/shared/.claude/skills/project-context/desde-design.md index 6771779..d64167c 100644 --- a/shared/.claude/skills/project-context/desde-design.md +++ b/shared/.claude/skills/project-context/desde-design.md @@ -26,6 +26,7 @@ reescriba requisitos ni decisiones: el diseño se **traslada**, resumido y con l | `architecture` | **Arquitectura** | Componentes y flujo principal, síncrono y asíncrono; enlaza el diagrama del diseño en vez de copiarlo | | `architecture` (*Ficha de responsabilidades*, si existe) | **Arquitectura** → "Responsabilidades y límites" | La tabla tal cual: componente, responsable de, no le toca, estado del que es dueño y ejecución. Son los límites que el código respeta: ahí se ve dónde va cada regla | | `architecture` y `api-data-model` (dueños) | **Reglas de negocio (invariantes)** | Las de límite, en una línea cada una: qué componente decide qué, y que ningún contexto escribe los datos de otro | +| `architecture` y `api-data-model` (límites) | **Testing** → "Restricciones de arquitectura" | Una línea por límite, marcada "intención: sin automatizar". La skill `architecture-tests` las convierte en reglas que fallan solas y cambia la marca. No digas que un límite está garantizado porque el diseño lo nombra | | `failures` | **Arquitectura** → "Fallas previstas" y **Testing** | Una línea por escenario (escenario → mitigación). En *Testing*, los que merecen test de regresión | | `failures` (*Flujos críticos*, si existen) | **Testing** → "Tests de recuperación" | Uno por cada paso con efecto **desconocido** o con recuperación (duplicado, timeout, concurrencia, reinicio): qué se simula y qué evidencia de cierre se comprueba. No los escribas ahora: quedan como lista para la skill `tdd` | | `requirements` (no funcionales) + `scale` | **Atributos de calidad** | Los umbrales con número (latencia, disponibilidad, consistencia) y el orden de magnitud de carga | diff --git a/shared/.claude/skills/ship-gate/SKILL.md b/shared/.claude/skills/ship-gate/SKILL.md index 58857f2..dc89fd0 100644 --- a/shared/.claude/skills/ship-gate/SKILL.md +++ b/shared/.claude/skills/ship-gate/SKILL.md @@ -17,7 +17,7 @@ Cuando detectes intención de desplegar ("deploy", "push to prod", "go live", "r - **CI/CD (GitHub Actions)**: sin **script injection** — `${{ github.event.* }}` (título/cuerpo de issue/PR, comentarios, `head_ref`) interpolado en un bloque `run:` es RCE; pásalo por `env:` y referencia `$VAR`. Sin `pull_request_target` que haga checkout del código del PR. `permissions:` mínimas a nivel top (`contents: read`), nunca `write-all`. Actions de terceros **pinneadas por SHA** (no `@v1`/`@main`, refs mutables). Secretos nunca en `echo`/`env`/CLI args (quedan en logs); usa OIDC en vez de claves estáticas. 2. **Datos**: migraciones reversibles y aplicadas, sin pérdida de datos, backups verificados, índices en columnas calientes. 3. **Despliegue**: variables de entorno de prod presentes (fail-fast), health/readiness checks, graceful shutdown, rollback definido. -4. **Calidad**: lint y tests en verde, cobertura de los flujos críticos, sin `TODO`/`FIXME` bloqueantes, sin código muerto. +4. **Calidad**: lint y tests en verde, cobertura de los flujos críticos, sin `TODO`/`FIXME` bloqueantes, sin código muerto. Las restricciones de arquitectura de `CLAUDE.project.md` corren en ese lint o esos tests; las que siguen como intención son **manual**: revisa a mano que lo que cambia no las cruce. 5. **Dependencias** (A03:2025): lockfile presente y coherente, sin paquetes abandonados/typosquatting ni dependency confusion, scripts de ciclo de vida (`postinstall`) revisados o deshabilitados por defecto, ninguna versión nueva publicada hace menos de 7 días sin justificación, `npm ci` (no `npm install`) en CI, licencias compatibles, **SBOM** (CycloneDX o SPDX) generado para la release si el producto se distribuye a terceros. 6. **Frontend** (si aplica): bundle dentro de presupuesto, sin claves privadas en cliente, estados de error/carga, accesibilidad mínima. 7. **Observabilidad**: logging estructurado sin secretos/PII, métricas y alertas de los flujos críticos, trazas. diff --git a/shared/templates/PROJECT.template.md b/shared/templates/PROJECT.template.md index 5e9d26b..50ac6bd 100644 --- a/shared/templates/PROJECT.template.md +++ b/shared/templates/PROJECT.template.md @@ -66,7 +66,9 @@ ## Testing - + ## Decisiones de arquitectura