# Business Rules — Architecture

> Documento técnico que explica **cada pieza** del sistema de business rules y
> **cómo se relacionan entre sí**. Para uso operativo (cómo añadir reglas,
> cómo correr los comandos), ver el [`README.md`](./README.md). Para
> migración a otros repos, ver [`MIGRATION.md`](./MIGRATION.md).

---

## Visión general

```
┌─────────────────────────────────────────────────────────────────────────────┐
│                            ENTRADAS DEL SISTEMA                             │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  Developer/IA edita .md  ─┐                                                 │
│  /opsx:propose            ├──►  Catálogo (cliter/<bc>/business-rules/)      │
│  /business-rules:document ─┘                                                │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘
                                       │
                                       ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                       PROCESAMIENTO ESTRUCTURAL                             │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│   parse-catalog.ts                                                          │
│       └─► gray-matter (frontmatter)                                         │
│       └─► regex (per-rule body)                                             │
│                       │                                                     │
│                       ▼                                                     │
│   generate-index.ts ──► business-rules-index.json (consumed by tooling)     │
│                    └──► docs/business-rules/INDEX.md (consumed by humans)   │
│                                                                             │
│   validate-catalog.ts ──► Ajv schema check + IDs únicos + metadata válida  │
│   validate-citations.ts ──► @rule en código vs índice                      │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘
                                       │
                                       ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                            CONSUMIDORES                                     │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  Humanos ─► INDEX.md, ad-hoc lectura de los .md                             │
│  IA      ─► UserPromptSubmit hook inyecta reglas relevantes                 │
│             OpenSpec config.yaml inyecta business rules awareness en propose│
│  CI      ─► business-rules-check.yml (estructural en cada PR)               │
│  Audit   ─► audit.ts (estructural) + Claude continuación (semántico)        │
│  Pre-commit ─► husky pre-commit regenera índice + valida automáticamente    │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘
```

---

## Componentes — uno por uno

### 1. Catálogo (los `.md`)

**Ubicación**: `cliter/<bounded-context>/business-rules/<aggregate>.md`.

**Por qué ahí**: cohesión con `cliter/<bc>/CONTEXT.md` y los `*.aurora.yaml`.
Toda la "fuente de verdad del dominio" vive en `cliter/`; las reglas
pertenecen ahí. **Nunca** bajo `openspec/`, `docs/business-rules/<bc>/`, ni
`backend/`.

**Por qué NO un .md por regla**: navegación tediosa, frontmatter
duplicado. Una regla típica es lo bastante corta para vivir junto a sus
hermanas del mismo agregado.

**Contenido**: frontmatter file-level (BC, agregados, paths, keywords) +
body con secciones `## BR-<ID> — <título>`. Detalle exhaustivo del formato
en [`README.md`](./README.md) y en
[`.claude/skills/business-rules-guard/references/format.md`](../../.claude/skills/business-rules-guard/references/format.md).

---

### 2. Schema validador (`_schema.json`)

**Ubicación**: `docs/business-rules/_schema.json`.

**Qué valida**: el frontmatter file-level de cada `.md` del catálogo. Es un
JSON Schema draft-07 con campos `bounded_contexts`, `aggregates`,
`last_updated`, `keywords`, `paths` (todos requeridos, todos arrays excepto
`last_updated` que es string ISO date).

**Quién lo usa**: `validate-catalog.ts` lo carga vía Ajv y lo aplica a cada
archivo.

**Qué NO valida**: el cuerpo de cada regla (Estado, Severidad, Origen).
Eso lo verifica `validate-catalog.ts` con sets en código (los valores
enum legales están hardcodeados allí).

**Por qué dos sitios distintos para validación**: el frontmatter es YAML
estricto; un schema JSON es la mejor herramienta. El cuerpo es markdown
con secciones; un parser markdown + sets de validación es más natural.

---

### 3. Índice consolidado (`business-rules-index.json`)

**Ubicación**: `cliter/business-rules-index.json`.

**Generado por**: `scripts/business-rules/generate-index.ts`.

**Qué contiene**:

```jsonc
{
  "generatedAt": "2026-05-03T08:32:09.043Z",
  "totalFiles": 3,
  "totalRules": 5,
  "activeRules": 4,
  "derogatedRules": 0,
  "proposedRules": 1,

  "byId": {                       // ← lookup por ID
    "BR-PROD-HEADER-001": {
      "id": "BR-PROD-HEADER-001",
      "title": "Cascada de IN_PROGRESS al crear el primer evento",
      "filePath": "cliter/production-planning/business-rules/production-order-header.md",
      "bounded_contexts": ["production-planning"],
      "aggregates": ["production-planning/production-order-header", ...],
      "paths": ["backend/src/@app/...", "frontend/src/...", ...],
      "keywords": ["production order header", "cabecera de orden", ...],
      "type": "cascade",
      "state": "active",
      "severity": "blocking",
      "last_updated": "2026-05-03"
    },
    ...
  },
  "byBoundedContext": {           // ← lookup por BC (incluye cross-BC)
    "production-planning": ["BR-PROD-HEADER-001", "BR-PROD-HEADER-002", ...]
  },
  "byAggregate": { ... },         // ← lookup por agregado
  "byKeyword": { ... },           // ← lookup por keyword (lowercase)
  "byPath": [                     // ← lookup por glob de path
    { "pattern": "backend/src/@app/...", "ruleIds": [...] },
    ...
  ]
}
```

**Quién lo consume**:

- `audit.ts` (sección Salud + sección Drift).
- `check.ts` (los 4 modos).
- `promote.ts` (busca dónde vive la regla a derogar).
- `validate-citations.ts` (cruza `@rule` con `byId`).
- **Hook `inject-rules-context.ts`** (matching del prompt vs reglas).

**Cuándo se regenera**:

- Manualmente con `pnpm br:generate`.
- Automáticamente por la pre-commit hook si hay cambios staged en
  `cliter/<bc>/business-rules/*.md`.
- Por el workflow GHA `business-rules-check.yml` en cada PR (regenera y
  hace `git diff` para detectar olvidos).
- Por el slash command `/business-rules:audit`, que invoca `audit.ts` para
  la parte estructural y a continuación rellena la sección semántica.

**Claves en inglés deliberadamente**: el JSON es estructural (consumido por
tooling, no leído por humanos directamente). La mezcla de idiomas se aplica
solo al CONTENIDO de las reglas, no a las claves de los formatos
estructurales.

---

### 4. Índice humano (`INDEX.md`)

**Ubicación**: `docs/business-rules/INDEX.md`.

**Generado por**: `scripts/business-rules/generate-index.ts` (mismo script
que el JSON, en una pasada).

**Qué contiene**: tabla legible con todas las reglas (ID, título, BCs,
agregados, estado, severidad, last_updated, link al archivo). Ordenada
por ID.

**Quién lo consume**: humanos. Es la forma más rápida de tener una visión
global del catálogo sin abrir N archivos.

**No editar a mano**: se regenera automáticamente. Los cambios manuales se
sobrescriben.

---

### 5. Scripts TypeScript (`scripts/business-rules/`)

| Script | Responsabilidad | Invocado por |
|---|---|---|
| `types.ts` | Interfaces compartidas (`ParsedRule`, `IndexedRule`, `CatalogIndex`, etc.). | Todos los demás. |
| `parse-catalog.ts` | Walks `cliter/*/business-rules/*.md`, parsea frontmatter (gray-matter) y body (regex sobre `## <ID> — <título>` + tabla metadata). Coerciona `last_updated` a string ISO. | Todos los demás. |
| `generate-index.ts` | Construye `business-rules-index.json` + `INDEX.md` desde el output de parse-catalog. Falla en IDs duplicados o frontmatter inválido. | `pnpm br:generate`, pre-commit, CI. |
| `validate-catalog.ts` | Valida frontmatter contra `_schema.json` (Ajv), IDs únicos, metadata válida (Estado/Severidad ∈ sets legales), paths de `Implementa` (warnings si no existen). | `pnpm br:validate`, CI. |
| `validate-citations.ts` | Recorre `backend/src/` y `frontend/src/` con fast-glob buscando `@rule BR-...`, cruza con índice. Reporta huérfanas (cita inexistente) y zombis (regla activa >30d sin citas). | `pnpm br:validate:citations`, audit. |
| `audit.ts` | Reporte estructural en 4 secciones (A Salud, B Drift, C Asunciones, D Cross-BC pointers). Output humano + `--json`. Exit codes 0/2. **No expuesto** como `pnpm br:*`: el output útil es el integrado (estructural + sección E semántica) que solo el slash command produce al continuar la sesión. | `/business-rules:audit` (invoca `npx tsx scripts/business-rules/audit.ts` como Step 1, luego Claude continúa). |
| `promote.ts` | Lee `proposal.md` archivado, parsea `## Business rules affected`, aplica acciones (mantiene/deroga/extiende/Nuevas crean scaffold con `[VERIFICAR]` y state `proposed`). Regenera índice. Soporta `--dry-run`. **No expuesto** como `pnpm br:*`: el flujo del developer pasa por `/business-rules:promote`, que invoca este script como Step 1 y Claude continúa la sesión rellenando bodies de reglas nuevas (Step 2). | `/business-rules:promote` (invoca `npx tsx scripts/business-rules/promote.ts`). |
| `document.ts` | Bootstrap retroactivo. Valida agregado, lista fuentes a consultar, crea scaffold con frontmatter. **No genera reglas autónomamente**; las propone el LLM en sesión interactiva. **No expuesto** como `pnpm br:*` por la misma razón que `promote.ts` y `audit.ts`. | `/business-rules:document` (invoca `npx tsx scripts/business-rules/document.ts`). |
| `check.ts` | Validación dirigida. 4 modos: por agregado, por regla, por branch/PR (`gh`/git diff), por working tree (`git status --porcelain`). Glob matching contra `byPath`. | `pnpm br:check`. |

**Stack de dependencias**: `gray-matter` (frontmatter), `ajv` + `ajv-formats`
(JSON Schema), `fast-glob` (file walking), `tsx` (ejecutar TypeScript sin
compilar). Todas en `devDependencies` del root `package.json`.

---

### 6. Comandos pnpm

Declarados en `package.json` raíz. Cada uno es un wrapper sobre `tsx
scripts/business-rules/<script>.ts`:

```json
"br:generate": "tsx scripts/business-rules/generate-index.ts",
"br:validate": "tsx scripts/business-rules/validate-catalog.ts",
"br:validate:citations": "tsx scripts/business-rules/validate-citations.ts",
"br:check": "tsx scripts/business-rules/check.ts"
```

**Por qué prefijo `br:`**: distingue del resto de scripts (`back:*`,
`front:*`, `dev`). Convención del repo.

**Asimetría intencional — qué se expone y qué no**:

Los scripts `audit.ts`, `promote.ts` y `document.ts` **NO se exponen como
`pnpm br:*`**. Razón: los tres siguen el patrón **script + LLM
continuation** y su output útil es el integrado (script determinista +
Claude continúa la sesión rellenando lo que requiere razonamiento).
Exponerlos como aliases pnpm daría feedback parcial bajo el mismo nombre
que la versión completa, creando confusión sobre cuándo se obtiene el
output real (sección E del audit, body de reglas nuevas en promote,
propuestas semánticas en document).

El developer invoca esos flujos exclusivamente vía slash commands desde
Claude Code: `/business-rules:audit`, `/business-rules:promote`,
`/business-rules:document`. El slash command corre el script con
`npx tsx scripts/business-rules/<script>.ts` como Step 1 y continúa la
sesión como Step 2. Los scripts siguen siendo invocables directamente
con `npx tsx` para tooling externo o debugging (ver el header docstring
de cada uno).

Los aliases `pnpm br:*` reflejan exactamente lo que el developer USA
desde shell: regeneración de índice, validación, verificación de citas,
y consultas con `:check`. Nada más.

---

### 7. Slash commands de Claude Code (`.claude/commands/business-rules/`)

```
.claude/commands/business-rules/
├── promote.md     → /business-rules:promote
├── document.md    → /business-rules:document
├── audit.md       → /business-rules:audit
└── check.md       → /business-rules:check
```

**Qué son**: archivos markdown con frontmatter (`name`, `description`,
`category`, `tags`) y un cuerpo que documenta el comando para Claude.

**Cómo se materializa el namespace `:`**: la subcarpeta `business-rules/`
dentro de `.claude/commands/` produce comandos con prefijo
`/business-rules:`. Convención propia de Claude Code.

**Por qué wrappers documentales y no scripts directos**: los slash
commands son "guidance" — Claude lee el .md y decide cómo actuar (qué
preguntas hacer al usuario, cómo interpretar el output del script). Los
scripts pnpm son la ejecución real.

---

### 8. Skill `business-rules-guard` (`.claude/skills/business-rules-guard/`)

```
.claude/skills/business-rules-guard/
├── SKILL.md                ← entry point, frontmatter con auto_invoke
└── references/
    ├── format.md           ← formato exhaustivo
    ├── workflow.md         ← 6 workflows operativos
    └── audit.md            ← interpretación del audit
```

**Cómo se invoca**: NO la invocan los slash commands. Claude la auto-carga
cuando detecta los keywords del frontmatter `auto_invoke` (BR-XXX, business
rules, /business-rules:*, modificar archivos bajo `cliter/<bc>/business-rules/`,
etc.).

**Qué aporta**: contexto continuo sobre el sistema (anti-patterns, decisiones,
workflows). Los slash commands son acciones; la skill es el "manual de uso"
que Claude carga cuando aplica.

**Diferencia clave con un slash command**: la skill informa CADA acción de
Claude relacionada con el sistema, no solo cuando el user invoca un comando
explícitamente.

---

### 9. Hooks de Claude Code (`.claude/hooks/`)

#### `SessionStart` — `generate-skills-index.ts`

> Pre-existente en el proyecto. No es del sistema de business rules pero
> sí lo afecta: cuando creamos la skill `business-rules-guard` y los
> slash commands, este hook actualiza el índice del CLAUDE.md raíz para
> que Claude los vea.

**Cuándo corre**: al iniciar cualquier sesión de Claude Code.

#### `UserPromptSubmit` — `inject-rules-context.ts`

**Cuándo corre**: ANTES de cada prompt del usuario.

**Qué hace**:

1. Lee el prompt del JSON que Claude Code le pasa por stdin.
2. Localiza la raíz del repo (busca `package.json` con `name: aurora`).
3. Carga `cliter/business-rules-index.json`. Si no existe, exit silencioso.
4. Para cada regla activa o propuesta, calcula un **score de relevancia**:
   - Mención explícita del ID en el prompt: `+50`.
   - Nombre del agregado en el prompt: `+10`.
   - Nombre del bounded context en el prompt: `+5`.
   - Keyword del frontmatter (lowercase, longitud ≥ 4): `+3`.
5. Si alguna regla tiene score > 0, ordena por score y toma las **3 top**.
6. Lee el cuerpo de cada regla del archivo `.md` (truncando a 4000 chars
   por regla).
7. Escribe a stdout una sección formateada con las reglas. Claude la ve
   como contexto adicional al prompt.

**Naturaleza**: SOFT enforcement. Inyectar contexto NO obliga a Claude a
nada; es un nudge. Si Claude lo ignora, el sistema sigue funcionando
gracias al audit pairwise nocturno.

**Failsafe**: cualquier error (índice ausente, parse roto, pkg no
encontrado) hace que el hook salga silenciosamente. NUNCA bloquea un prompt.

**Cómo se desactiva**: eliminar la entrada `UserPromptSubmit` de
`.claude/settings.json`. El sistema sigue funcionando, solo pierdes la
inyección automática.

---

### 10. Pre-commit hook (Husky)

**Ubicación**: `.husky/pre-commit`.

**Qué hace**: detecta cambios staged en `cliter/<bc>/business-rules/*.md`
y, si hay alguno, ejecuta dos pasos en orden:

1. **`pnpm br:generate`**: regenera el índice (`business-rules-index.json` +
   `INDEX.md`) y los stagea en el mismo commit. Aborta si falla (frontmatter
   mal, IDs duplicados, parsing roto).
2. **`pnpm br:validate`**: valida el catálogo contra `_schema.json` y
   comprueba metadata (Estado/Severidad). Aborta si reporta errores
   estructurales (exit code `1`); permite continuar si solo hay warnings
   (exit code `2`, p.ej. paths inexistentes en `Implementa`).

**Por qué importa**: garantiza dos invariantes en cada commit:

- El índice committeado **siempre** coincide con los `.md` del mismo
  commit (sin esto sería trivial olvidar regenerar y el CI fallaría).
- El catálogo siempre cumple las reglas estructurales que verifica el CI
  (sin esto, errores de Estado/Severidad mal escritos llegarían al PR y
  romperían el workflow `business-rules-check.yml`).

**Cuándo aborta el commit**: si `br:generate` o `br:validate` retornan
error estructural. El developer recibe el detalle del archivo y campo
problemático para corregir antes del commit.

---

### 11. Workflow GHA `business-rules-check.yml`

**Ubicación**: `.github/workflows/business-rules-check.yml`.

**Trigger**: push a main + pull_request con paths que toquen
`cliter/**/business-rules/**`, `cliter/business-rules-index.json`,
`docs/business-rules/**`, `scripts/business-rules/**`, o el propio
workflow.

**Pasos**:

1. Setup pnpm + Node 24.
2. `pnpm install --frozen-lockfile`.
3. **Validate catalog**: `pnpm br:validate`. Falla en errores estructurales.
4. **Verify index in sync**: `pnpm br:generate` + `git diff --quiet`
   contra `cliter/business-rules-index.json` y `docs/business-rules/INDEX.md`.
   Falla si hay drift (alguien editó un .md y no committeó el índice
   regenerado — pero en teoría la pre-commit hook lo previene).
5. **Validate citations**: `pnpm br:validate:citations || true`. Advisory
   (no bloquea el job).

**Naturaleza**: estructural-only. La detección de contradicciones semánticas
queda fuera del CI: se hace **preventivamente** en `/opsx:propose` (vía
`openspec/config.yaml`) cuando se proponen cambios, y **on-demand** pidiendo
a Claude Code que razone sobre el catálogo cuando proceda.

---

### 12. Detección de tensiones semánticas (script + LLM continuation)

El sistema **no incluye un componente automatizado** (cron, routine,
GHA con SDK) para detección de contradicciones latentes entre reglas.
La cobertura se hace en dos planos sin infraestructura externa:

1. **Detección preventiva** (en `/opsx:propose` vía `openspec/config.yaml`):
   Claude consulta el catálogo y detecta conflictos ANTES de archivar el
   cambio. Es la red principal.

2. **Detección on-demand integrada en `/business-rules:audit`**: el
   slash command sigue el patrón **script + LLM continuation** (el mismo
   que usa `/business-rules:promote`):

   - Paso 1 — `npx tsx scripts/business-rules/audit.ts` corre la parte
     estructural (Salud, Drift, Asunciones, Cross-BC pointers).
   - Paso 2 — Claude continúa la sesión: lee el índice y los `.md` de
     las reglas, empareja las que comparten `bounded_contexts` /
     `aggregates` / `keywords`, clasifica cada par como
     `contradiction | extension | orthogonal | needs-review` con
     confianza 0–1, y añade la sección E al reporte.

   Sin SDK, sin API key, sin routine, sin GitHub App. El análisis usa
   los tokens de la suscripción de Claude Code que el usuario ya tiene
   abierta. El script `audit.ts` **no se expone como `pnpm br:*`**
   porque su output sin la sección E es un sub-producto; el flujo
   primario es invocar el slash command.

**Por qué no automatizar (cron / routine)**:

- El catálogo arranca pequeño; tensiones latentes son improbables hasta
  que crezca a 50-150 reglas.
- La detección preventiva (1) cubre la mayoría de casos.
- Una routine externa requiere instalar Claude GitHub App, gestionar
  permisos, mantener un prompt extenso fuera del repo, y se desactiva
  sola si el acceso al repo cambia. Para un piloto, el coste operativo
  supera el valor.
- El comando interactivo (2) recoge el mismo beneficio sin esa
  infraestructura: el dev decide cuándo correrlo.
- Si en el futuro el catálogo crece >250 reglas y la verificación
  on-demand resulta costosa de coordinar, se puede reintroducir una
  routine sin afectar al resto del sistema.

---

### 13. Integración con OpenSpec (`openspec/config.yaml`)

**ÚNICO archivo bajo `openspec/`** que el sistema modifica. El resto está
gestionado por OpenSpec CLI y `openspec update` puede regenerarlo.

**Qué añade**:

- **`context`** (~3.1K caracteres): instrucciones que la IA recibe al
  generar artefactos de un cambio. Cubre:
  - Ubicación del catálogo y del índice.
  - Procedimiento de 7 pasos para detección de conflictos en propose.
  - Convenciones (idiomas mixtos, casing, IDs).
  - Ciclo de vida post-implementación.
  - Citas opcionales `@rule`.
- **`rules.proposal`** (6 reglas): obliga a incluir sección
  `## Business rules affected` con sub-acciones `mantiene/deroga/extiende/Nuevas`,
  halt en conflictos, etc.
- **`rules.design`** (2): referencias por ID en cambios no triviales.
- **`rules.specs`** (2): citar IDs cuando spec codifica regla conocida.
- **`rules.tasks`** (2): incluir `/business-rules:promote` final si hay
  cambios; sugerir cita `@rule` opcional.

**Cómo se materializa**: cuando Claude ejecuta `/opsx:propose`, llama a
`openspec instructions <artifact> --json`. La respuesta incluye nuestro
`context` y `rules` mezclados con los del schema de OpenSpec. Claude los
trata como constraints al escribir el artefacto.

**Independencia**: si OpenSpec se retira, el sistema de business rules
sigue funcionando. Solo pierde la integración automática vía propose; los
demás workflows (document, promote sobre archives existentes, audit,
check) son independientes.

---

### 14. CLAUDE.md raíz

**Modificación**: nueva sección "Business rules" antes de "Skills" + fila
en la tabla de Skills auto-invoke + entrada en el índice automático
generado por `.claude/scripts/generate-skills-index.ts`.

**Qué le dice a Claude**:

- Dónde vive el catálogo y para qué sirve.
- Que las reglas son **memoria + detector**, no enforcement.
- Que `@rule` en código es opcional.
- Que tras `/opsx:archive` debe sugerir `/business-rules:promote`.
- Que la mezcla de idiomas es deliberada — **do NOT harmonize**.

**Por qué importa**: CLAUDE.md se carga en cada sesión. Es la primera línea
de defensa contra que Claude se invente convenciones distintas o
"armonice" idiomas.

---

## Escenarios operativos completos

> Esta sección describe **escenarios E2E** del sistema. Los escenarios A y B coinciden con
> los **Workflows A y B** documentados en `README.md` (acciones sobre el catálogo); el
> escenario D es una manifestación del Workflow C. Los escenarios C y E son casos de uso
> operativos (consulta, audit) que no se asocian a un workflow de modificación del catálogo.

### Escenario A — Añadir reglas vía OpenSpec (Workflow A)

```
Developer/IA
   │
   │  /opsx:propose "soporte puede borrar cualquier evento"
   ▼
openspec/config.yaml inyecta context + rules
   │
   │  IA al escribir proposal.md:
   │   1. Identifica BCs candidatos (production-planning, iam)
   │   2. Carga reglas relevantes del índice
   │   3. Pregunta a sí misma: ¿hay conflicto?
   │   4. Si hay, halt y pregunta al user (a/b/c)
   │   5. Escribe sección "## Business rules affected"
   ▼
proposal.md generado con sección rule actions
   │
   │  /opsx:apply (implementa código)
   │  /opsx:archive <name> (mueve a archive/)
   ▼
Developer ejecuta /business-rules:promote
   │
   │  El slash command coordina dos sub-pasos:
   │
   │  (a) promote.ts (Node, sin LLM):
   │       - mantiene BR-X → no-op
   │       - deroga BR-Y → cambia state, añade nota de derogación
   │       - Nuevas → crea scaffold con ID secuencial real, state=proposed,
   │                  body con [VERIFICAR]
   │       Regenera índice.
   │
   │  (b) Claude (continuación inmediata, misma sesión):
   │       Para cada regla scaffolded en (a):
   │         - Lee proposal.md archivado, código del agregado, tests, CONTEXT.md.
   │         - Redacta body real (Enunciado, Casos, Implementa, Tests).
   │         - Pide confirmación al usuario.
   │         - Sustituye los [VERIFICAR] por contenido real y cambia state a active.
   │       Regenera índice (o lo hace la pre-commit al commitear).
   ▼
Catálogo actualizado: nuevas reglas en state=active con body real,
INDEX.md y JSON index al día.
```

### Escenario B — Documentar retroactivamente (Workflow B)

```
Developer
   │
   │  /business-rules:document production-planning/<aggregate>
   ▼
document.ts:
   1. Valida que <aggregate>.aurora.yaml existe
   2. Lista archivos de dominio relevantes
   3. Crea scaffold cliter/<bc>/business-rules/<aggregate>.md
   4. Imprime guidance
   │
IA + dev sesión interactiva:
   1. Lee YAML, CONTEXT.md, código, tests
   2. Identifica reglas implícitas
   3. Para cada candidata, pide confirmación al dev
   4. Escribe la regla con [VERIFICAR] donde aplique
   ▼
pnpm br:generate (manual o pre-commit)
pnpm br:validate (manual o CI)
   ▼
Catálogo enriquecido
```

### Escenario C — Cliente pregunta "¿por qué este registro está en PENDING?" (consulta)

```
Cliente (vía soporte interno)
   │
   ▼
Soporte abre Claude Code con acceso al repo
   │
   │  "El cliente pregunta por qué el position con id X está en PENDING"
   ▼
UserPromptSubmit hook detecta keywords (production, position, PENDING)
   │
   │  Inyecta hasta 3 reglas relevantes en contexto
   ▼
Claude responde citando BR-PROD-HEADER-001 / BR-PROD-HEADER-002
   │
   │  Soporte tiene la respuesta + ID citable
   ▼
Cliente recibe explicación con referencia "según BR-PROD-HEADER-002, ..."
```

### Escenario D — Modificar regla = derogar + crear (sub-caso del Workflow C)

```
Developer detecta que BR-PROD-EVENT-005 ya no refleja la realidad
   │
   │  Edita cliter/production-planning/business-rules/<agg>.md:
   │   1. Cambia Estado a `derogated`
   │   2. Añade sección ### Derogación con fecha + razón
   │   3. Crea nueva regla BR-PROD-EVENT-XXX (siguiente seq) con la nueva semántica
   │
   │  pnpm br:generate
   │  pnpm br:validate
   │
   │  git commit (pre-commit hook regenera índice si lo olvidó)
   ▼
Catálogo evoluciona; el ID 005 queda permanentemente derogado y nunca se reusa
```

### Escenario E — Audit (estructural + semántico, integrados en el slash command)

```
Estructural — automático (cada PR):
  GHA business-rules-check.yml → pnpm br:validate + br:generate (diff) +
                                 br:validate:citations

Estructural + Semántico — desde Claude Code (developer):
  /business-rules:audit
    1. npx tsx scripts/business-rules/audit.ts    ← parte estructural (4 secciones)
    2. Claude continúa la sesión:                 ← parte semántica (sección E)
       - Lee cliter/business-rules-index.json
       - Empareja reglas que comparten BC/aggregate/keyword
       - Lee el body de cada par desde filePath
       - Clasifica: contradiction | extension | orthogonal | needs-review
       - Reporta tensiones con verdict, confidence y explicación corta
  Sin SDK, sin API key, sin routine. Mismo patrón que /business-rules:promote.

Tooling externo / debugging:
  npx tsx scripts/business-rules/audit.ts            ← reporte estructural humano
  npx tsx scripts/business-rules/audit.ts --json     ← machine-consumable
  (No hay alias `pnpm br:audit`: ver punto 6 — Comandos pnpm.)
```

---

## Mapa de archivos del sistema

Todos los archivos creados por el sistema (excluyendo las reglas en sí):

```
.
├── docs/
│   └── business-rules/
│       ├── README.md                                    ← guía operativa
│       ├── ARCHITECTURE.md                              ← este documento
│       ├── MIGRATION.md                                 ← portabilidad a otros repos
│       ├── _schema.json                                 ← JSON Schema del frontmatter
│       └── INDEX.md                                     ← navegación humana (generado)
│
├── cliter/
│   └── business-rules-index.json                        ← índice consolidado (generado)
│
├── scripts/
│   └── business-rules/
│       ├── types.ts                                     ← interfaces compartidas
│       ├── parse-catalog.ts                             ← parser del catálogo
│       ├── generate-index.ts                            ← regenera índice + INDEX.md
│       ├── validate-catalog.ts                          ← validate frontmatter + IDs
│       ├── validate-citations.ts                        ← @rule en código vs índice
│       ├── audit.ts                                     ← reporte estructural
│       ├── promote.ts                                   ← cristaliza desde archive
│       ├── document.ts                                  ← bootstrap retroactivo
│       └── check.ts                                     ← validación dirigida (4 modos)
│
├── .claude/
│   ├── settings.json                                    ← (modificado) hook UserPromptSubmit
│   ├── hooks/
│   │   └── inject-rules-context.ts                      ← inyecta reglas relevantes
│   ├── commands/
│   │   └── business-rules/
│   │       ├── promote.md                               ← /business-rules:promote
│   │       ├── document.md                              ← /business-rules:document
│   │       ├── audit.md                                 ← /business-rules:audit
│   │       └── check.md                                 ← /business-rules:check
│   └── skills/
│       └── business-rules-guard/
│           ├── SKILL.md                                 ← entry + 10 anti-patterns
│           └── references/
│               ├── format.md                            ← formato exhaustivo
│               ├── workflow.md                          ← 6 workflows
│               └── audit.md                             ← interpretación del audit
│
├── .husky/
│   └── pre-commit                                       ← regenera índice si .md cambió
│
├── .github/
│   └── workflows/
│       └── business-rules-check.yml                     ← CI estructural (cada PR)
│
├── openspec/
│   └── config.yaml                                      ← (modificado) context + rules
│
├── CLAUDE.md                                            ← (modificado) sección Business rules
└── package.json                                         ← (modificado) scripts + devDeps
```

**Archivos modificados** (no creados desde cero):

- `package.json` — añadidos scripts `br:*` + devDependencies (`ajv`,
  `ajv-formats`, `fast-glob`, `gray-matter`, `tsx`, `@types/node`).
- `.husky/pre-commit` — añadido bloque de regeneración del índice +
  validación.
- `.claude/settings.json` — añadido hook `UserPromptSubmit`.
- `openspec/config.yaml` — añadidos `context` y `rules`.
- `CLAUDE.md` — añadida sección "Business rules" + fila en tabla skills +
  índice regenerado entre `<!-- SKILLS-INDEX-START/END -->`.

**Archivos artefacto generados** (no editar a mano):

- `cliter/business-rules-index.json`
- `docs/business-rules/INDEX.md`
- El bloque entre `<!-- SKILLS-INDEX-START/END -->` en `CLAUDE.md`.

---

## Cómo evoluciona el sistema

**Día a día**:

- Cualquier cambio que toque dominio sigue el Workflow A (vía OpenSpec; ver
  `README.md` o el escenario A más arriba).
- La pre-commit hook mantiene el índice sincronizado y valida estructura.
- El CI estructural valida cada PR.

**Cada mes** (o cuando proceda):

- `/business-rules:audit` desde Claude Code: el script corre la parte
  estructural y, en la misma sesión, Claude continúa con el análisis
  pairwise semántico (ver Escenario E).
- Resolver `[VERIFICAR]` con producto.

**Cuando crece el catálogo**:

- 150 reglas → log informativo del audit.
- 250 reglas → empezar filtrado por BC en propose.
- 500 reglas → filtrado obligatorio.
- 1000 reglas → replantear arquitectura (RAG, sub-catálogos, embeddings).

**Cuando se añade un BC nuevo al proyecto**:

- Crear `cliter/<bc>/business-rules/<aggregate>.md` cuando aparezca la
  primera regla. No es necesario antes.
- Actualizar las keywords de las reglas que lo referencien (si aplica).

**Cuando se elimina un BC**:

- Sus reglas pasan a `state: derogated` con nota explicativa.
- Los IDs siguen quemados (no reusables).
- El audit eventualmente reportará reglas zombi en ese BC; el resolutor
  decide si se eliminan los archivos o se mantienen como histórico.

---

## Decisiones arquitecturales clave

| Decisión | Documento que la registra |
|---|---|
| Catálogo en `cliter/<bc>/business-rules/`, no `docs/` | `openspec/changes/add-business-rules-system/design.md` Decision 1 |
| Una regla = un archivo, cross-aggregate via apuntadores | Decision 2 |
| Frontmatter en plural (`bounded_contexts`, `aggregates`) | spec `business-rules-catalog/spec.md` |
| IDs en MAYÚSCULAS, frontmatter en kebab-minúsculas | Decision 4 |
| Mezcla deliberada de idiomas (no harmonize) | Decision 5 |
| Detección de conflictos por IA semántica, no tests negativos | Decision 6 |
| Integración OpenSpec via `config.yaml`, no wrappers | Decision 7 |
| `:promote` separado de `/opsx:archive`, no wrapper | Decision 8 |
| Reglas POST-implementación, no precondición | Decision 9 |
| Detección semántica integrada en `/business-rules:audit` (script + LLM continuation), sin componente automatizado | Decision 12 |

Cada decisión fue evaluada contra alternativas concretas, documentadas en
el `design.md` del cambio OpenSpec.
