claudeos-core 2.4.0 → 2.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.es.md CHANGED
@@ -7,7 +7,11 @@
7
7
  [![license](https://img.shields.io/npm/l/claudeos-core.svg?color=blue)](LICENSE)
8
8
  [![downloads](https://img.shields.io/npm/dm/claudeos-core.svg?logo=npm&color=blue&label=downloads)](https://www.npmjs.com/package/claudeos-core)
9
9
 
10
- **Genera automáticamente la documentación de Claude Code a partir de tu código fuente real.** Una herramienta CLI que analiza estáticamente tu proyecto y luego ejecuta una pipeline de Claude de 4 pasadas para generar `.claude/rules/`, standards, skills y guías para que Claude Code siga las convenciones de tu proyecto, no las genéricas.
10
+ **Haz que Claude Code siga las convenciones de TU proyecto al primer intentono los valores predeterminados genéricos.**
11
+
12
+ Un scanner determinístico de Node.js lee tu código primero; luego una pipeline de Claude de 4 pasadas escribe el conjunto completo — `CLAUDE.md` + `.claude/rules/` auto-cargado + standards + skills + L4 memory. 10 idiomas de salida, 5 validators post-generación, y un allowlist explícito de rutas que impide al LLM inventar archivos o frameworks que no están en tu código.
13
+
14
+ Funciona en [**12 stacks**](#supported-stacks) (monorepos incluidos) — un solo comando `npx`, sin configuración, resume-safe, idempotente.
11
15
 
12
16
  ```bash
13
17
  npx claudeos-core init
@@ -19,18 +23,28 @@ npx claudeos-core init
19
23
 
20
24
  ## ¿Qué es esto?
21
25
 
22
- Usas Claude Code. Es inteligente, pero no conoce **las convenciones de tu proyecto**:
23
- - Tu equipo usa MyBatis, pero Claude genera código JPA.
24
- - Tu wrapper es `ApiResponse.ok()`, pero Claude escribe `ResponseEntity.success()`.
25
- - Tus paquetes son `controller/order/`, pero Claude crea `order/controller/`.
26
+ Usas Claude Code. Es potente, pero cada sesión empieza desde cero — no tiene memoria de cómo está estructurado _tu_ proyecto. Así que recurre a valores predeterminados "generalmente buenos" que rara vez coinciden con lo que tu equipo realmente hace:
27
+
28
+ - Tu equipo usa **MyBatis**, pero Claude genera repositorios JPA.
29
+ - Tu wrapper de respuesta es `ApiResponse.ok()`, pero Claude escribe `ResponseEntity.success()`.
30
+ - Tus paquetes son layer-first (`controller/order/`), pero Claude crea domain-first (`order/controller/`).
31
+ - Tus errores pasan por middleware centralizado, pero Claude esparce `try/catch` por cada endpoint.
32
+
33
+ Te gustaría tener un set de `.claude/rules/` por proyecto — Claude Code lo carga automáticamente cada sesión — pero escribir esas reglas a mano para cada repo nuevo lleva horas, y se desactualizan a medida que el código evoluciona.
34
+
35
+ **ClaudeOS-Core las escribe por ti, desde tu código fuente real.** Un scanner determinístico de Node.js lee tu proyecto primero (stack, ORM, layout de paquetes, convenciones, rutas de archivos). Luego una pipeline de Claude de 4 pasadas convierte los hechos extraídos en un conjunto de documentación completo:
26
36
 
27
- Así que pasas mucho tiempo arreglando cada archivo generado.
37
+ - **`CLAUDE.md`** — el índice del proyecto que Claude lee en cada sesión
38
+ - **`.claude/rules/`** — reglas auto-cargadas por categoría (`00.core` / `10.backend` / `20.frontend` / `30.security-db` / `40.infra` / `60.memory` / `70.domains` / `80.verification`)
39
+ - **`claudeos-core/standard/`** — documentos de referencia (el "por qué" detrás de cada regla)
40
+ - **`claudeos-core/skills/`** — patrones reutilizables (CRUD scaffolding, plantillas de página)
41
+ - **`claudeos-core/memory/`** — decision log + failure patterns que crecen con el proyecto
28
42
 
29
- **ClaudeOS-Core resuelve esto.** Escanea tu código fuente real, descubre tus convenciones y escribe un conjunto completo de reglas en `.claude/rules/` el directorio que Claude Code lee automáticamente. La próxima vez que digas *"Crea un CRUD para órdenes"*, Claude seguirá tus convenciones al primer intento.
43
+ Como el scanner le entrega a Claude un allowlist explícito de rutas, el LLM **no puede inventar archivos o frameworks que no estén en tu código**. Cinco validators post-generación (`claude-md-validator`, `content-validator`, `pass-json-validator`, `plan-validator`, `sync-checker`) verifican la salida antes de que se envíe — language-invariant, así que las mismas reglas se aplican generes en inglés, español, o cualquiera de los otros 8 idiomas.
30
44
 
31
45
  ```
32
- Antes: Tú → Claude Code → código "generalmente bueno" → corrección manual
33
- Después: Tú → Claude Code → código que coincide con tu proyecto → listo para enviar
46
+ Antes: Tú → Claude Code → código "generalmente bueno" → corrección manual cada vez
47
+ Después: Tú → Claude Code → código que coincide con TU proyecto → listo para enviar
34
48
  ```
35
49
 
36
50
  ---
@@ -119,37 +133,45 @@ Ejecutado en [`spring-boot-realworld-example-app`](https://github.com/gothinkste
119
133
  </details>
120
134
 
121
135
  <details>
122
- <summary><strong>📄 Lo que termina en tu <code>CLAUDE.md</code> (extracto real)</strong></summary>
136
+ <summary><strong>📄 Lo que termina en tu <code>CLAUDE.md</code> (extracto real — Section 1 + 2)</strong></summary>
123
137
 
124
138
  ```markdown
125
- ## 4. Core Architecture
126
-
127
- ### Core Patterns
128
-
129
- - **Hexagonal ports & adapters**: domain ports live in `io.spring.core.{aggregate}`
130
- and are implemented by `io.spring.infrastructure.repository.MyBatis{Aggregate}Repository`.
131
- The domain layer has zero MyBatis imports.
132
- - **CQRS-lite read/write split (same DB)**: write side goes through repository ports
133
- + entities; read side is a separate `readservice` package whose `@Mapper`
134
- interfaces return `*Data` DTOs directly (no entity hydration).
135
- - **No aggregator/orchestrator layer**: multi-source orchestration happens inside
136
- application services (e.g., `ArticleQueryService`); there is no `*Aggregator`
137
- class in the codebase.
138
- - **Application-supplied UUIDs**: entity constructors assign their own UUID; PK is
139
- passed via `#{user.id}` on INSERT. The global
140
- `mybatis.configuration.use-generated-keys=true` flag is dead config
141
- (auto-increment is unused).
142
- - **JWT HS512 authentication**: `io.spring.infrastructure.service.DefaultJwtService`
143
- is the sole token subject in/out; `io.spring.api.security.JwtTokenFilter`
144
- extracts the token at the servlet layer.
139
+ # CLAUDE.md spring-boot-realworld-example-app
140
+
141
+ > Reference implementation of the RealWorld backend specification on
142
+ > Java 11 + Spring Boot 2.6, exposing both REST and GraphQL endpoints
143
+ > over a hexagonal MyBatis persistence layer.
144
+
145
+ ## 1. Role Definition
146
+
147
+ As the senior developer for this repository, you are responsible for
148
+ writing, modifying, and reviewing code. Responses must be written in English.
149
+ A Java Spring Boot REST + GraphQL API server organized around a hexagonal
150
+ (ports & adapters) architecture, with a CQRS-lite read/write split inside
151
+ an XML-driven MyBatis persistence layer and JWT-based authentication.
152
+
153
+ ## 2. Project Overview
154
+
155
+ | Item | Value |
156
+ |---|---|
157
+ | Language | Java 11 |
158
+ | Framework | Spring Boot 2.6.3 |
159
+ | Build Tool | Gradle (Groovy DSL) |
160
+ | Persistence | MyBatis 3 via `mybatis-spring-boot-starter:2.2.2` (no JPA) |
161
+ | Database | SQLite (`org.xerial:sqlite-jdbc:3.36.0.3`) — `dev.db` (default), `:memory:` (test) |
162
+ | Migration | Flyway — single baseline `V1__create_tables.sql` |
163
+ | API Style | REST (`io.spring.api.*`) + GraphQL via Netflix DGS `:4.9.21` |
164
+ | Authentication | JWT HS512 (`jjwt-api:0.11.2`) + Spring Security `PasswordEncoder` |
165
+ | Server Port | 8080 (default) |
166
+ | Test Stack | JUnit Jupiter 5, Mockito, AssertJ, rest-assured, spring-mock-mvc |
145
167
  ```
146
168
 
147
- Nota: cada afirmación anterior está fundamentada en el código fuente real nombres de clases, rutas de paquetes, claves de configuración y la marca de configuración muerta son todas extraídas por el scanner antes de que Claude escriba el archivo.
169
+ Cada valor de arriba coordenadas exactas de dependencias, el nombre de archivo `dev.db`, el nombre de la migración `V1__create_tables.sql`, "no JPA" es extraído por el scanner desde `build.gradle` / `application.properties` / el árbol de fuentes antes de que Claude escriba el archivo. Nada se adivina.
148
170
 
149
171
  </details>
150
172
 
151
173
  <details>
152
- <summary><strong>🛡️ Una regla auto-cargada real (<code>.claude/rules/10.backend/03.data-access-rules.md</code>)</strong></summary>
174
+ <summary><strong>🛡️ Una regla auto-cargada real (<code>.claude/rules/10.backend/01.controller-rules.md</code>)</strong></summary>
153
175
 
154
176
  ````markdown
155
177
  ---
@@ -157,42 +179,56 @@ paths:
157
179
  - "**/*"
158
180
  ---
159
181
 
160
- # Data Access Rules
182
+ # Controller Rules
183
+
184
+ ## REST (`io.spring.api.*`)
161
185
 
162
- ## XML-only SQL
163
- - Every SQL statement lives in `src/main/resources/mapper/*.xml`.
164
- NO `@Select` / `@Insert` / `@Update` / `@Delete` annotations on `@Mapper` methods.
165
- - Each `@Mapper` interface has exactly one XML file at
166
- `src/main/resources/mapper/{InterfaceName}.xml`.
167
- - `<mapper namespace="...">` MUST be the fully qualified Java interface name.
168
- The single existing exception is `TransferData.xml` (free-form `transfer.data`).
186
+ - Controllers are the SOLE response wrapper for HTTP — no aggregator/facade above them.
187
+ Return `ResponseEntity<?>` or a body Spring serializes via `JacksonCustomizations`.
188
+ - Each controller method calls exactly ONE application service method. Multi-source
189
+ composition lives in the application service.
190
+ - Controllers MUST NOT import `io.spring.infrastructure.*`. No direct `@Mapper` access.
191
+ - Validate command-param arguments with `@Valid`. Custom JSR-303 constraints live under
192
+ `io.spring.application.{aggregate}.*`.
193
+ - Resolve the current user via `@AuthenticationPrincipal User`.
194
+ - Let exceptions propagate to `io.spring.api.exception.CustomizeExceptionHandler`
195
+ (`@ControllerAdvice`). Do NOT `try/catch` business exceptions inside the controller.
169
196
 
170
- ## Dynamic SQL
171
- - `<if>` predicates MUST guard both null and empty:
172
- `<if test="X != null and X != ''">`. Empty-only is the existing HIGH-severity bug pattern.
173
- - Prefer `LIMIT n OFFSET m` over MySQL-style `LIMIT m, n`.
197
+ ## GraphQL (`io.spring.graphql.*`)
198
+
199
+ - DGS components (`@DgsComponent`) are the sole GraphQL response wrappers.
200
+ Use `@DgsQuery` / `@DgsData` / `@DgsMutation`.
201
+ - Resolve the current user via `io.spring.graphql.SecurityUtil.getCurrentUser()`.
174
202
 
175
203
  ## Examples
176
204
 
177
205
  ✅ Correct:
178
- ```xml
179
- <update id="update">
180
- UPDATE articles
181
- <set>
182
- <if test="article.title != null and article.title != ''">title = #{article.title},</if>
183
- updated_at = #{article.updatedAt}
184
- </set>
185
- WHERE id = #{article.id}
186
- </update>
206
+ ```java
207
+ @PostMapping
208
+ public ResponseEntity<?> createArticle(@AuthenticationPrincipal User user,
209
+ @Valid @RequestBody NewArticleParam param) {
210
+ Article article = articleCommandService.createArticle(param, user);
211
+ ArticleData data = articleQueryService.findById(article.getId(), user)
212
+ .orElseThrow(ResourceNotFoundException::new);
213
+ return ResponseEntity.ok(Map.of("article", data));
214
+ }
187
215
  ```
188
216
 
189
217
  ❌ Incorrect:
190
- ```xml
191
- <mapper namespace="article.mapper"> <!-- NO — namespace MUST be FQCN -->
218
+ ```java
219
+ @PostMapping
220
+ public ResponseEntity<?> create(@RequestBody NewArticleParam p) {
221
+ try {
222
+ articleCommandService.createArticle(p, currentUser);
223
+ } catch (Exception e) { // NO — let CustomizeExceptionHandler handle it
224
+ return ResponseEntity.status(500).body(e.getMessage()); // NO — leaks raw message
225
+ }
226
+ return ResponseEntity.ok().build();
227
+ }
192
228
  ```
193
229
  ````
194
230
 
195
- El glob `paths: ["**/*"]` significa que Claude Code carga automáticamente esta regla cada vez que editas cualquier archivo del proyecto. Los ejemplos ✅/❌ vienen directamente de las convenciones reales y los patrones de bugs existentes en este código.
231
+ El glob `paths: ["**/*"]` significa que Claude Code carga automáticamente esta regla cada vez que editas cualquier archivo del proyecto. Cada nombre de clase, ruta de paquete, y exception handler en la regla viene directamente del código fuente escaneado — incluyendo el `CustomizeExceptionHandler` y `JacksonCustomizations` reales del proyecto.
196
232
 
197
233
  </details>
198
234
 
@@ -200,25 +236,26 @@ El glob `paths: ["**/*"]` significa que Claude Code carga automáticamente esta
200
236
  <summary><strong>🧠 Un seed auto-generado de <code>decision-log.md</code> (extracto real)</strong></summary>
201
237
 
202
238
  ```markdown
203
- ## 2026-04-26 — CQRS-lite read/write split inside the persistence layer
204
-
205
- - **Context:** Writes go through `core.*Repository` port → `MyBatis*Repository`
206
- adapter → `io.spring.infrastructure.mybatis.mapper.{Aggregate}Mapper`.
207
- Reads bypass the domain port: application service →
208
- `io.spring.infrastructure.mybatis.readservice.{Concept}ReadService` directly,
209
- returning flat `*Data` DTOs from `io.spring.application.data.*`.
210
- - **Options considered:** Single repository surface returning hydrated entities
211
- for both reads and writes.
212
- - **Decision:** Same database, two `@Mapper` packages — `mapper/` (write side,
213
- operates on core entities) and `readservice/` (read side, returns `*Data` DTOs).
214
- Read DTOs avoid entity hydration overhead.
215
- - **Consequences:** Reads are NOT routed through the domain port this is
216
- intentional, not a bug. Application services may inject both a `*Repository`
217
- (writes) and one or more `*ReadService` interfaces (reads) at the same time.
218
- Do NOT add hydrate-then-map glue in the read path.
239
+ ## 2026-04-26 — Hexagonal ports & adapters with MyBatis-only persistence
240
+
241
+ - **Context:** `io.spring.core.*` exposes `*Repository` ports (e.g.,
242
+ `io.spring.core.article.ArticleRepository`) implemented by
243
+ `io.spring.infrastructure.repository.MyBatis*Repository` adapters.
244
+ The domain layer has zero `org.springframework.*` /
245
+ `org.apache.ibatis.*` / `io.spring.infrastructure.*` imports.
246
+ - **Options considered:** JPA/Hibernate, Spring Data, MyBatis-Plus
247
+ `BaseMapper`. None adopted.
248
+ - **Decision:** MyBatis 3 (`mybatis-spring-boot-starter:2.2.2`) with
249
+ hand-written XML statements under `src/main/resources/mapper/*.xml`.
250
+ Hexagonal port/adapter wiring keeps the domain framework-free.
251
+ - **Consequences:** Every SQL lives in XML`@Select`/`@Insert`/`@Update`/`@Delete`
252
+ annotations are forbidden. New aggregates require both a
253
+ `core.{aggregate}.{Aggregate}Repository` port AND a
254
+ `MyBatis{Aggregate}Repository` adapter; introducing a JPA repository would
255
+ split the persistence model.
219
256
  ```
220
257
 
221
- Pass 4 inicializa `decision-log.md` con las decisiones arquitectónicas extraídas de `pass2-merged.json`, así las sesiones futuras recuerdan *por qué* el código se ve como se ve — no solo *cómo* se ve.
258
+ Pass 4 inicializa `decision-log.md` con las decisiones arquitectónicas extraídas de `pass2-merged.json`, así las sesiones futuras recuerdan *por qué* el código se ve como se ve — no solo *cómo* se ve. Cada opción ("JPA/Hibernate", "MyBatis-Plus") y cada consecuencia están fundamentadas en el bloque real de dependencias de `build.gradle`.
222
259
 
223
260
  </details>
224
261
 
@@ -277,13 +314,17 @@ Las categorías que comparten el mismo prefijo numérico entre `rules/` y `stand
277
314
 
278
315
  ## ¿Para quién es esto?
279
316
 
280
- | Eres... | Esto te ayuda a... |
317
+ | Eres... | El dolor que esto elimina |
281
318
  |---|---|
282
- | **Un dev en solitario** empezando un proyecto nuevo con Claude Code | Saltarte por completo la fase de "enseñarle a Claude mis convenciones" |
283
- | **Un team lead** que mantiene estándares compartidos | Automatizar la parte tediosa de mantener `.claude/rules/` actualizado |
284
- | **Ya usas Claude Code** pero estás cansado de corregir el código generado | Hacer que Claude siga TUS patrones, no patrones "generalmente buenos" |
319
+ | **Un dev en solitario** empezando un proyecto nuevo con Claude Code | "Enseñarle a Claude mis convenciones cada sesión" desaparece. `CLAUDE.md` + `.claude/rules/` de 8 categorías generados en una sola pasada. |
320
+ | **Un team lead** manteniendo estándares compartidos entre repos | El `.claude/rules/` se desincroniza cuando la gente renombra paquetes, cambia de ORM, o modifica wrappers de respuesta. ClaudeOS-Core re-sincroniza determinísticamente — misma entrada, salida byte-idéntica, sin ruido en los diffs. |
321
+ | **Ya usas Claude Code** pero estás cansado de corregir el código generado | Wrapper de respuesta incorrecto, layout de paquetes incorrecto, JPA cuando usas MyBatis, `try/catch` esparcido cuando tu proyecto usa middleware centralizado. El scanner extrae tus convenciones reales; cada pasada de Claude se ejecuta contra un allowlist explícito de rutas. |
322
+ | **Haciendo onboarding a un nuevo repo** (proyecto existente, uniéndote a un equipo) | Ejecuta `init` en el repo, obtén un mapa de arquitectura vivo: tabla de stack en CLAUDE.md, reglas por capa con ejemplos ✅/❌, decision log seedeado con el "por qué" detrás de las decisiones principales (JPA vs MyBatis, REST vs GraphQL, etc.). Leer 5 archivos le gana a leer 5,000 archivos fuente. |
323
+ | **Trabajando en español / coreano / japonés / chino / 6 idiomas más** | La mayoría de los generadores de reglas de Claude Code son solo en inglés. ClaudeOS-Core escribe el conjunto completo en **10 idiomas** (`en/ko/ja/zh-CN/es/vi/hi/ru/fr/de`) con **validación estructural byte-idéntica** — el mismo veredicto de `claude-md-validator` independientemente del idioma de salida. |
324
+ | **Trabajando con un monorepo** (Turborepo, pnpm/yarn workspaces, Lerna) | Dominios backend + frontend analizados en una sola ejecución con prompts separados; `apps/*/` y `packages/*/` recorridos automáticamente; reglas por stack emitidas bajo `70.domains/{type}/`. |
325
+ | **Contribuyendo a OSS o experimentando** | La salida es gitignore-friendly — `claudeos-core/` es tu directorio de trabajo local, solo `CLAUDE.md` + `.claude/` necesitan enviarse. Resume-safe si se interrumpe; idempotente al re-ejecutar (tus ediciones manuales en las reglas sobreviven sin `--force`). |
285
326
 
286
- **No es para ti si:** quieres un bundle preset de tipo "talla única" con agentes/skills/rules que funcione el primer día sin paso de escaneo (ver [docs/es/comparison.md](docs/es/comparison.md) para entender qué encaja en cada caso), o tu proyecto aún no encaja en uno de los [stacks soportados](#supported-stacks).
327
+ **No es para ti si:** quieres un bundle preset de tipo "talla única" con agentes/skills/rules que funcione el primer día sin paso de escaneo (ver [docs/es/comparison.md](docs/es/comparison.md) para entender qué encaja en cada caso), tu proyecto aún no encaja en uno de los [stacks soportados](#supported-stacks), o solo necesitas un único `CLAUDE.md` (el `claude /init` integrado es suficiente — no hace falta instalar otra herramienta).
287
328
 
288
329
  ---
289
330
 
@@ -296,9 +337,28 @@ Habitual: Tú describes el proyecto → Claude adivina tu stack → Claude es
296
337
  Aquí: El código lee tu stack → El código pasa hechos confirmados a Claude → Claude escribe docs a partir de hechos
297
338
  ```
298
339
 
299
- La idea clave: **un scanner Node.js lee tu código fuente primero** (determinístico, sin IA), y luego una pipeline de Claude de 4 pasadas escribe la documentación, restringida por lo que el scanner encontró. Claude no puede inventar rutas o frameworks que no estén realmente en tu código.
340
+ La pipeline corre en **tres etapas**, con código a ambos lados de la llamada al LLM:
341
+
342
+ **1. Step A — Scanner (determinístico, sin LLM).** Un scanner Node.js recorre la raíz de tu proyecto, lee `package.json` / `build.gradle` / `pom.xml` / `pyproject.toml`, parsea archivos `.env*` (con redacción de variables sensibles para `PASSWORD/SECRET/TOKEN/JWT_SECRET/...`), clasifica tu patrón de arquitectura (los 5 patrones de Java A/B/C/D/E, Kotlin CQRS / multi-módulo, Next.js App vs. Pages Router, FSD, components-pattern), descubre dominios, y construye un allowlist explícito de cada ruta de archivo fuente que existe. Salida: `project-analysis.json` — la única fuente de verdad para lo que sigue.
343
+
344
+ **2. Step B — Pipeline de Claude de 4 pasadas (restringida por los hechos del Step A).**
345
+ - **Pass 1** lee archivos representativos por grupo de dominio y extrae ~50–100 convenciones por dominio — wrappers de respuesta, librerías de logging, manejo de errores, convenciones de naming, patrones de testing. Se ejecuta una vez por grupo de dominio (`máx 4 dominios, 40 archivos por grupo`) para que el contexto nunca se desborde.
346
+ - **Pass 2** fusiona todo el análisis por dominio en una imagen a nivel de proyecto y resuelve los desacuerdos eligiendo la convención dominante.
347
+ - **Pass 3** escribe `CLAUDE.md` + `.claude/rules/` + `claudeos-core/standard/` + skills + guides — dividido en stages (`3a` facts → `3b-core/3b-N` rules+standards → `3c-core/3c-N` skills+guides → `3d-aux` database+mcp-guide) para que el prompt de cada stage quepa en la ventana de contexto del LLM incluso cuando `pass2-merged.json` es grande. Sub-divide 3b/3c en batches de ≤15 dominios para proyectos con ≥16 dominios.
348
+ - **Pass 4** seedea la capa de memoria L4 (`decision-log.md`, `failure-patterns.md`, `compaction.md`, `auto-rule-update.md`) y agrega reglas universales de scaffold. Pass 4 tiene **prohibido modificar `CLAUDE.md`** — la Section 8 de Pass 3 es autoritativa.
349
+
350
+ **3. Step C — Verification (determinístico, sin LLM).** Cinco validators verifican la salida:
351
+ - `claude-md-validator` — 25 checks estructurales en `CLAUDE.md` (8 secciones, conteos H3/H4, unicidad de archivos de memoria, invariante T1 de heading canónico). Language-invariant: mismo veredicto independientemente de `--lang`.
352
+ - `content-validator` — 10 checks de contenido incluyendo verificación de path-claim (`STALE_PATH` atrapa referencias `src/...` fabricadas) y detección de drift en MANIFEST.
353
+ - `pass-json-validator` — well-formedness JSON de Pass 1/2/3/4 + conteo de secciones consciente del stack.
354
+ - `plan-validator` — consistencia plan ↔ disco (legacy, mayormente no-op desde v2.1.0).
355
+ - `sync-checker` — consistencia de registro disco ↔ `sync-map.json` a través de 7 directorios rastreados.
356
+
357
+ Tres niveles de severidad (`fail` / `warn` / `advisory`) para que las warnings nunca bloqueen CI por hallucinations del LLM que el usuario puede arreglar manualmente.
358
+
359
+ El invariante que ata todo: **Claude solo puede citar rutas que realmente existen en tu código**, porque el Step A le entrega un allowlist finito. Si el LLM aun así intenta inventar algo (raro pero ocurre con ciertos seeds), el Step C lo atrapa antes de que los docs se envíen.
300
360
 
301
- Para la arquitectura completa, ver [docs/es/architecture.md](docs/es/architecture.md).
361
+ Para detalles por pasada, resume basado en marker, el workaround de staged-rules para el block de path sensible `.claude/` de Claude Code, e internals de detección de stack, ver [docs/es/architecture.md](docs/es/architecture.md).
302
362
 
303
363
  ---
304
364