# Catálogo de Business Rules

> Convenciones globales del catálogo de reglas de negocio del proyecto. **Léelo entero antes de
> añadir o modificar reglas.**

---

## Índice

- [Catálogo de Business Rules](#catálogo-de-business-rules)
  - [Índice](#índice)
  - [Propósito](#propósito)
  - [Ubicación](#ubicación)
  - [Convención de identificadores](#convención-de-identificadores)
  - [Estructura frontmatter](#estructura-frontmatter)
  - [Mezcla deliberada de idiomas](#mezcla-deliberada-de-idiomas)
  - [Estado y severidad](#estado-y-severidad)
    - [Estado](#estado)
    - [Severidad](#severidad)
  - [Estructura de un archivo de reglas](#estructura-de-un-archivo-de-reglas)
  - [Plantilla para reglas](#plantilla-para-reglas)
  - [Reglas cross-aggregate y cross-bounded-context](#reglas-cross-aggregate-y-cross-bounded-context)
    - [Ejemplo: cascada `delete(event)` → `position` → `header`](#ejemplo-cascada-deleteevent--position--header)
    - [Ejemplo cross-BC: regla con excepción para rol soporte](#ejemplo-cross-bc-regla-con-excepción-para-rol-soporte)
  - [Cómo crear, derogar y modificar reglas](#cómo-crear-derogar-y-modificar-reglas)
    - [Workflow A — Vía OpenSpec (recomendado para cambios planificados)](#workflow-a--vía-openspec-recomendado-para-cambios-planificados)
      - [Acciones que el proposal puede declarar](#acciones-que-el-proposal-puede-declarar)
      - [Asignación de IDs en reglas nuevas](#asignación-de-ids-en-reglas-nuevas)
      - [Estado del índice tras el flujo](#estado-del-índice-tras-el-flujo)
    - [Workflow B — Documentación retroactiva (vía `/business-rules:document`)](#workflow-b--documentación-retroactiva-vía-business-rulesdocument)
    - [Workflow C — Edición directa](#workflow-c--edición-directa)
      - [Crear una regla nueva](#crear-una-regla-nueva)
      - [Derogar una regla](#derogar-una-regla)
      - [Modificar una regla (= derogar + crear)](#modificar-una-regla--derogar--crear)
    - [Estado final de las reglas](#estado-final-de-las-reglas)
      - [¿Cuándo es legítimo dejar una regla en `proposed` en el catálogo committeado? (gap producto↔código)](#cuándo-es-legítimo-dejar-una-regla-en-proposed-en-el-catálogo-committeado-gap-productocódigo)
  - [Citas `@rule` en código (opcional)](#citas-rule-en-código-opcional)
  - [Asunciones y verificaciones pendientes](#asunciones-y-verificaciones-pendientes)
  - [Hook opcional `UserPromptSubmit`](#hook-opcional-userpromptsubmit)
  - [Comandos de operación](#comandos-de-operación)
    - [Ejemplos](#ejemplos)
      - [`pnpm br:generate`](#pnpm-brgenerate)
      - [`pnpm br:validate`](#pnpm-brvalidate)
      - [`pnpm br:validate:citations`](#pnpm-brvalidatecitations)
      - [`pnpm br:check [args]`](#pnpm-brcheck-args)
  - [Slash commands del catálogo](#slash-commands-del-catálogo)
    - [`/business-rules:promote [<archive>]`](#business-rulespromote-archive)
    - [`/business-rules:document <bc>/<aggregate>`](#business-rulesdocument-bcaggregate)
    - [`/business-rules:audit [<bc>]`](#business-rulesaudit-bc)
      - [Cobertura del audit](#cobertura-del-audit)
  - [Skill `business-rules-guard`](#skill-business-rules-guard)
    - [Cuándo se carga](#cuándo-se-carga)
    - [Qué aporta sobre los slash commands](#qué-aporta-sobre-los-slash-commands)
    - [Estructura](#estructura)
    - [Cuándo consultar la skill manualmente](#cuándo-consultar-la-skill-manualmente)
  - [Anti-patrones (no hacer)](#anti-patrones-no-hacer)
  - [Cómo se detectan conflictos al proponer cambios](#cómo-se-detectan-conflictos-al-proponer-cambios)
  - [Límites operativos del catálogo](#límites-operativos-del-catálogo)
  - [Referencias](#referencias)

> Para detalle técnico de cada componente del sistema (scripts, hooks, integración con OpenSpec,
> CI), ver [`ARCHITECTURE.md`](./ARCHITECTURE.md). Para portar el sistema a otro repo, ver
> [`MIGRATION.md`](./MIGRATION.md).

---

## Propósito

El catálogo guarda las reglas de negocio (invariantes, validaciones, cascadas y políticas) del
proyecto en forma legible para humanos y para la IA. Es:

- **Fuente de verdad complementaria**, no sustituta. La verdad ejecutable es el código y los tests;
  el catálogo describe la intención.
- **Memoria institucional**. Cuando el cliente pregunta "¿por qué este registro está en estado
  pending?", el catálogo lo explica con un identificador citable.
- **Detector de incoherencias**. Cuando se propone un cambio que contradice una regla pasada, la IA
  puede detectarlo gracias a este catálogo.

El catálogo se cristaliza **post-implementación**, no antes. No es un spec prescriptivo: documenta
lo que el sistema HACE (vía código) o se ha COMPROMETIDO a hacer (vía un cambio archivado en
OpenSpec).

---

## Ubicación

```
cliter/
├── <bounded-context>/
│   ├── CONTEXT.md                       # contexto del BC (existente)
│   ├── <aggregate>.aurora.yaml          # schema (existente)
│   └── business-rules/                  # 🆕
│       └── <aggregate>.md               # reglas del agregado
└── business-rules-index.json            # 🆕 índice consolidado (generado)
```

Una regla vive en **un único archivo**: el del agregado donde se observa su invariante final. Los
agregados o BCs adicionales afectados llevan apuntadores cortos, no contenido duplicado (ver "Reglas
cross-aggregate y cross-BC" más abajo).

---

## Convención de identificadores

```
BR-<BOUNDED-CONTEXT>-<AGGREGATE>-<NNN>

Ejemplos:
  BR-IAM-USER-001
  BR-PROD-EVENT-005           (BOUNDED-CONTEXT abreviado a PROD)
  BR-PRODUCTION-HEADER-007    (forma extendida)
```

- **MAYÚSCULAS** deliberadamente. Destacan visualmente como referencias en código, prosa, commits y
  PRs.
- `<BOUNDED-CONTEXT>` y `<AGGREGATE>` pueden ser el nombre completo o una abreviatura coherente. Lo
  importante: estables y únicos dentro del proyecto.
- `<NNN>` es un número secuencial, zero-padded a 3 dígitos, único dentro del agregado.
- **No se reutilizan IDs**. Cuando una regla se deroga, su ID queda permanentemente asignado a la
  entrada derogada. Las reglas siguientes usan IDs nuevos.

---

## Estructura frontmatter

Coherencia con `*.aurora.yaml` y `CONTEXT.md`:

- `bounded_contexts`, `aggregates`, `paths`, `keywords`: **kebab-minúsculas**
  (`production-planning`, `production-order-position-event`).
- IDs: **MAYÚSCULAS** con guiones (`BR-PROD-EVENT-005`).

```yaml
---
bounded_contexts:
  - production-planning # kebab-minúsculas
  - iam # idem
aggregates:
  - production-planning/production-order-header
  - production-planning/production-order-position-event
last_updated: 2026-05-02
keywords:
  - cancelación de evento # bilingüe deliberado
  - cancel event
  - cascada de revert
  - revert cascade
paths:
  - backend/src/@app/production-planning/production-order-header/**
  - frontend/src/app/modules/admin/apps/production-planning/production-order-header/**
---
```

---

## Mezcla deliberada de idiomas

Esta es una decisión de diseño **explícita** y debe respetarse:

| Elemento                                | Idioma                               |
| --------------------------------------- | ------------------------------------ |
| Prosa (enunciados, glosarios, casos)    | **Español**                          |
| Identificadores (IDs, frontmatter keys) | **Inglés**                           |
| Nombres de archivo y rutas              | **Inglés**                           |
| Citaciones en código (`@rule`)          | **Inglés** (el ID es inglés)         |
| Keywords                                | **Bilingüe** (español + inglés)      |
| Tablas de casos                         | **Español** en cabeceras y contenido |

**Razón**: producto y equipo discuten reglas en español; identificadores estables en inglés evitan
ambigüedades de traducción y se integran con tooling mayoritariamente en inglés.

> **CRÍTICO — DO NOT HARMONIZE**: ningún agente, script ni dev debe "armonizar" la mezcla. Si ves
> "español + inglés" en una regla, es a propósito. Traducir el catálogo a un solo idioma es un
> anti-patrón explícito que la skill `business-rules-guard` enumera y la audit detecta.

---

## Estado y severidad

Cada regla declara dos dimensiones en su tabla de metadatos:

### Estado

| Valor       | Significado                                                             |
| ----------- | ----------------------------------------------------------------------- |
| `active`    | Implementada y vigente.                                                 |
| `derogated` | Reemplazada o retirada. Su ID queda permanentemente tomado.             |
| `proposed`  | Documentada pero aún no implementada. Excepción al post-implementation. |

### Severidad

| Valor           | Significado                                                                |
| --------------- | -------------------------------------------------------------------------- |
| `blocking`      | Romperla causa fallo del sistema o comportamiento incorrecto del dominio.  |
| `informational` | Convención u orientación. Su violación no rompe nada pero genera fricción. |

> **Solo dos niveles**, sin "warning" intermedio. Los niveles intermedios generan fatiga.

---

## Estructura de un archivo de reglas

```markdown
---
<frontmatter file-level>
---

# <título humano del agregado>

> **Glosario**:
>
> - **<término>**: <definición breve>

---

## BR-<BC>-<AGG>-<NNN> — <título corto de la regla>

| Campo      | Valor                                               |
| ---------- | --------------------------------------------------- |
| Estado     | `active` \| `derogated` \| `proposed`               |
| Severidad  | `blocking` \| `informational`                       |
| Origen     | <retro-documentada \| openspec/changes/archive/...> |
| Trigger en | <agregado origen, opcional>                         |
| Efecto en  | <agregado destino, opcional>                        |

### Enunciado

<una o dos frases en español describiendo la regla.>

### Diagrama de cascada

<!-- opcional, solo cuando la regla describe un efecto en cascada -->

trigger │ ▼ condición │ ▼ sí efecto

### Casos exhaustivos

| #   | <columna 1> | <columna 2> | ... | Resultado |
| --- | ----------- | ----------- | --- | --------- |
| 1   | ...         | ...         | ... | ...       |
| 2   | ...         | ...         | ... | ...       |

### Asunciones a verificar

<!-- opcional -->

- **[VERIFICAR caso N]**: <descripción de qué falta confirmar>
- **[ASUNCIÓN]**: <hipótesis tomada por defecto>

### Implementa

- `backend/src/@app/<bc>/<agg>/.../<file>.ts` — <qué hace en el flujo>
- ...

### Tests

- `backend/test/<path>:'<test name>'`
- ...

### Reglas relacionadas

- **BR-XXX-YYY-NNN** — <descripción corta de la relación>
```

---

## Plantilla para reglas

Una sola plantilla que se adapta al contenido de la regla. Las únicas secciones obligatorias son
**Enunciado**, **Casos exhaustivos** e **Implementa**. Añade `Trigger en`, `Efecto en` y
`Diagrama de cascada` si la regla describe un efecto que se propaga; añade `Tests` si tienes tests
linkeables; añade `Asunciones a verificar` si quedan dudas.

```markdown
## BR-<BC>-<AGG>-<NNN> — <título corto>

| Campo      | Valor                 |
| ---------- | --------------------- |
| Estado     | `active`              |
| Severidad  | `blocking`            |
| Origen     | retro-documentada     |
| Trigger en | <opcional, si aplica> |
| Efecto en  | <opcional, si aplica> |

### Enunciado

<una o dos frases en español describiendo la regla.>

### Diagrama de cascada

<!-- opcional, solo si la regla describe efectos que se propagan -->

trigger │ ▼ condición │ ▼ sí efecto

### Casos exhaustivos

| #   | <columna 1> | <columna 2> | ... | Resultado |
| --- | ----------- | ----------- | --- | --------- |
| 1   | ...         | ...         | ... | ...       |

### Implementa

- `backend/src/@app/<bc>/<agg>/.../<file>.ts:<line>` — <qué hace en el flujo>

### Tests

- `backend/test/.../<file>.spec.ts:'<test name>'`

### Reglas relacionadas

- **BR-XXX-YYY-NNN** — <relación>
```

---

## Reglas cross-aggregate y cross-bounded-context

Una regla puede afectar a varios agregados (cascada delete-event → position → header) o a varios
bounded contexts (excepción de borrado para rol soporte afectando a IAM y a PRODUCTION-PLANNING).

**Convención**:

1. La regla **vive en UN solo archivo**: el del agregado donde se observa su invariante final.
2. Su frontmatter declara TODOS los bounded contexts y agregados afectados (en plural).
3. Los archivos de los demás agregados/BCs llevan **apuntadores cortos** sin contenido duplicado.

### Ejemplo: cascada `delete(event)` → `position` → `header`

**Vive en**: `cliter/production-planning/business-rules/production-order-header.md` (porque el
invariante final es sobre el header).

```yaml
---
bounded_contexts:
  - production-planning
aggregates:
  - production-planning/production-order-header
  - production-planning/production-order-position
  - production-planning/production-order-position-event
...
---
## BR-PROD-HEADER-007 — Revert por borrado del último evento
...
```

**Apuntador en**: `cliter/production-planning/business-rules/production-order-position-event.md`

```markdown
## Cascadas que dispara este agregado

- `delete(event)` → ver **BR-PROD-HEADER-007** (revert cascade hacia header cuando se borra el
  último evento de su position).
```

**Apuntador en**: `cliter/production-planning/business-rules/production-order-position.md`

```markdown
## Cascadas que afectan a este agregado

- ← **BR-PROD-POSITION-003** (atómica, vive aquí)
- ← **BR-PROD-HEADER-007** (vive en `production-order-header.md`, position es punto medio de la
  cascada)
```

### Ejemplo cross-BC: regla con excepción para rol soporte

Si la regla `BR-PROD-EVENT-006` permite a soporte borrar cualquier evento, su frontmatter declara
dos BCs:

```yaml
bounded_contexts:
  - production-planning
  - iam
```

Y `cliter/iam/business-rules/role.md` lleva un apuntador:

```markdown
## Excepciones por rol que afectan a otros BCs

- Rol `support` → ver **BR-PROD-EVENT-006** (excepción de borrado de evento DONE).
```

---

## Cómo crear, derogar y modificar reglas

Tres workflows independientes. Cada uno soporta varias acciones (crear nuevas reglas, derogar
existentes, mantener referencias). **Modificar una regla en su semántica = derogar la antigua +
crear una nueva con ID nuevo** (los IDs no se reutilizan).

> En todos los workflows, **el script hace el trabajo mecánico** (asignación de IDs, regeneración
> del índice, modificación de campos en la tabla de metadata) y **Claude + tú ponéis el contenido
> rico** (enunciado preciso, casos exhaustivos, paths reales del código, decisiones cuando hay
> conflicto).

### Workflow A — Vía OpenSpec (recomendado para cambios planificados)

Para cambios que pasan por el ciclo de OpenSpec (`propose → apply → archive`). El comando
`/business-rules:promote` maneja **todas las acciones** declaradas en el `proposal.md` en una sola
pasada.

| Paso | Quién                          | Qué pasa                                                                                                                                                                                                                                                                                                                            |
| ---- | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1    | Tú + Claude (interactivo)      | `/opsx:propose <descripción>`. El `config.yaml` de OpenSpec instruye a Claude a consultar el catálogo, detectar conflictos y generar `proposal.md` con sección `## Business rules affected` declarando las acciones (ver [lista de acciones](#acciones-que-el-proposal-puede-declarar)).                                            |
| 2    | Tú                             | Si Claude detectó conflictos, eliges resolución (a/b/c) y la registra en el proposal.                                                                                                                                                                                                                                               |
| 3    | Claude                         | `/opsx:apply` implementa el código del cambio.                                                                                                                                                                                                                                                                                      |
| 4    | Tú                             | `/opsx:archive <name>` cierra el cambio y lo mueve a `openspec/changes/archive/`.                                                                                                                                                                                                                                                   |
| 5    | **`/business-rules:promote`**: | El slash command coordina dos sub-pasos en la misma sesión:                                                                                                                                                                                                                                                                         |
| 5.a  | Script `:promote`              | `mantiene`/`extiende`: no-op del script. `deroga`: `Estado` → `derogated` + sección `### Derogación`. `Nuevas`: scaffold con ID secuencial real, `Estado: proposed`, body con `[VERIFICAR]`. Regenera el índice.                                                                                                                    |
| 5.b  | Claude (continuación)          | Para cada regla creada en 5.a, lee el `proposal.md` archivado + código + tests + `CONTEXT.md`, redacta el body real (enunciado, casos exhaustivos, `Implementa`, `Tests`), te pide confirmación, escribe la regla y cambia `Estado` a `active`. `pnpm br:generate && pnpm br:validate` (o lo hace la pre-commit hook al commitear). |

#### Acciones que el proposal puede declarar

Cuatro acciones posibles sobre reglas del catálogo:

- **Mantiene regla**: el cambio referencia una regla existente sin modificarla.
- **Deroga regla**: el cambio retira una regla del catálogo (su ID queda permanentemente ocupado).
- **Extiende regla**: el cambio añade un caso o excepción a una regla existente; el script no
  modifica el `.md`, solo recuerda actualizar manualmente la sección "Reglas relacionadas".
- **Nueva regla**: el cambio introduce una regla nueva en el catálogo.

Formato en la sección `## Business rules affected` del proposal:

```markdown
### Existentes

- mantiene BR-PROD-EVENT-005 — referencia, sin cambios.
- deroga BR-PROD-EVENT-003 — razón breve.
- extiende BR-PROD-HEADER-001 — relación; el script no modifica el .md, recordatorio para actualizar
  manualmente "Reglas relacionadas".

### Nuevas

- en `production-planning/production-order-position-event` — Validación de cancelación.
- BR-PROD-EVENT en `production-planning/production-order-position-event` — Validación de
  cancelación. (forma con prefijo de ID explícito; necesaria solo si el archivo destino no tiene
  reglas previas de las que el script pueda inferir el prefijo)
```

Si no hay reglas afectadas: escribe `None` bajo la sección.

#### Asignación de IDs en reglas nuevas

`:promote` lee el path y asigna el siguiente número secuencial. Para el prefijo:

- **Inferencia automática**: si el archivo destino ya tiene reglas, el script toma el prefijo más
  común entre ellas (en la práctica, el del último spec/regla creada en ese archivo). Ejemplo: si
  las reglas existentes son `BR-PROD-EVENT-001` y `BR-PROD-EVENT-002`, la nueva será
  `BR-PROD-EVENT-003`.
- **Prefijo explícito**: si el archivo está vacío, el proposal debe declararlo en el formato
  `- BR-PROD-EVENT en \`...\` —
  Título`. Por convención, **el explícito debe coincidir con el prefijo del último spec creado** en agregados similares del mismo bounded context, para mantener coherencia (ej. si el resto de archivos del BC usan `BR-PROD-...`,
  esta nueva regla también).

#### Estado del índice tras el flujo

- Tras 5.a el script ya regeneró el índice automáticamente.
- En 5.b Claude edita los `.md` (sustituye `[VERIFICAR]`, cambia `proposed` → `active`), por lo que
  el índice se desactualiza.
- Antes del commit hay que regenerar otra vez. **La pre-commit hook lo hace automáticamente al
  detectar cambios staged en el catálogo**, así que en la práctica no necesitas correrlo a mano.

### Workflow B — Documentación retroactiva (vía `/business-rules:document`)

Para reglas implícitas en código que no pasan por OpenSpec: bootstrap inicial, bug que destapa una
regla no documentada, importación de un Word funcional, etc.

> Este workflow **solo crea** reglas nuevas (no deroga ni modifica existentes). Para derogaciones o
> modificaciones de reglas que ya están en el catálogo, usa el Workflow C.

| Paso | Quién                       | Qué pasa                                                                                                                                                                                  |
| ---- | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1    | Script `:document`          | `/business-rules:document <bc>/<aggregate>` valida el agregado, lista los archivos de dominio relevantes, crea scaffold con frontmatter mínimo si no existe.                              |
| 2    | Claude (sesión interactiva) | Lee YAML, CONTEXT.md, código y tests del agregado. Identifica reglas implícitas y propone candidatas con contenido completo (enunciado, casos, implementa).                               |
| 3    | Tú                          | Confirmas o ajustas cada candidata antes de que Claude la escriba. Marca `[VERIFICAR]` los CASOS dudosos (filas concretas de la tabla), no toda la regla.                                 |
| 4    | Claude                      | Escribe las reglas confirmadas con body completo. Estado = `active` (regla implementada en código) o `proposed` (intención sin implementar; ver sección "gap producto↔código" más abajo). |
| 5    | Tú                          | `pnpm br:generate && pnpm br:validate` (o lo hace la pre-commit hook al commitear). Commit.                                                                                               |

Para importar un Word funcional convertido a markdown:

```
/business-rules:document <bc>/<aggregate> --from <path-to-md>
```

### Workflow C — Edición directa

Para fixes ad-hoc, ajustes sin cambio semántico, derogations puntuales fuera de OpenSpec, o
cualquier intervención manual sobre el catálogo.

#### Crear una regla nueva

Editar el `.md` del agregado a mano:

1. Asignar un ID nuevo siguiendo el prefijo del último spec creado del agregado (no reusar
   derogados).
2. Completar las secciones obligatorias del formato.
3. Marcar `[VERIFICAR]` o `[ASUNCIÓN]` solo en los casos concretos que dudes.
4. `pnpm br:generate && pnpm br:validate` (o lo hace la pre-commit hook al commitear).

#### Derogar una regla

Las reglas **no se editan en su semántica**. Para retirar o reemplazar una regla:

1. **Localiza la regla**: `pnpm br:check BR-PROD-EVENT-005`.
2. **Modifica la tabla de metadata** en su `.md`:

   ```diff
   | Campo       | Valor              |
   | ----------- | ------------------ |
   - | Estado      | `active`           |
   + | Estado      | `derogated`        |
   | Severidad   | `blocking`         |
   - | Origen      | retro-documentada  |
   + | Origen      | retro-documentada (derogada por BR-PROD-EVENT-007) |
   ```

3. **Añade sección `### Derogación`** al final del cuerpo:

   ```markdown
   ### Derogación

   Derogada el 2026-06-15 por **BR-PROD-EVENT-007**. Razón breve: <motivo>.
   ```

4. **Si hay regla sustituta**, créala con un ID nuevo (no se reutiliza el de la derogada). Documenta
   la relación en la sección "Reglas relacionadas" de la nueva:

   ```markdown
   - **BR-PROD-EVENT-005** (derogada por esta regla)
   ```

5. `pnpm br:generate && pnpm br:validate` (o lo hace la pre-commit hook al commitear).

> **No se borra la regla derogada en ningún workflow**. Permanece visible para que las citaciones
> históricas en código y commits sigan siendo unívocas.

#### Modificar una regla (= derogar + crear)

Modificar la semántica de una regla equivale a derogar la antigua y crear una nueva con ID nuevo.
Sigue los pasos de "Derogar una regla" + "Crear una regla nueva" arriba.

Edits permitidos en sitio sin derogar (no son cambios semánticos):

- Corregir typos, gramática, formato.
- Reformular sin alterar significado.
- Arreglar paths rotos en `Implementa` / `Tests`.
- Refrescar `last_updated`.
- Resolver `[VERIFICAR]` o `[ASUNCIÓN]` con contenido validado.
- Añadir filas a la tabla de casos (cubrir más espacio sin contradecir lo cubierto).

---

### Estado final de las reglas

| Workflow | Estado al terminar | Body al terminar | Notas                                                                                                                                                      |
| -------- | ------------------ | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| A        | `active`           | real             | El sub-paso 5.a deja la regla transitoriamente en `proposed` con `[VERIFICAR]`, pero el sub-paso 5.b la lleva a `active` con body real en la misma sesión. |
| B        | `active` (default) | real             | Excepción: gap producto↔código (ver más abajo) → `proposed`.                                                                                               |
| C        | `active` (default) | real             | Igual que B.                                                                                                                                               |

#### ¿Cuándo es legítimo dejar una regla en `proposed` en el catálogo committeado? (gap producto↔código)

Solo cuando documentas una intención que el código no satisface todavía y quieres que el gap sea
visible y auditable. Ejemplo: `BR-PROD-HEADER-002` describe la cascada de revert al borrar el último
evento; producto la quiere pero el código no la implementa. Marcarla como `proposed` hace el gap
explícito y trazable.

NO es legítimo dejar `proposed` con `[VERIFICAR]` en headers como resultado de una sesión
interrumpida del Workflow A. Si ves uno, es porque el sub-paso 5.b se cortó — completa el body antes
de commitear.

Las **reglas piloto del repo** (`BR-PROD-HEADER-001`, `BR-PROD-POSITION-001`, etc.) se documentaron
por el Workflow B: nacen con body completo y `state: active`. La única excepción es
`BR-PROD-HEADER-002`, que usa `state: proposed` precisamente para señalar el gap descrito arriba.

---

## Citas `@rule` en código (opcional)

Se permite citar una regla en JSDoc o equivalente:

```typescript
/**
 * Cancela un evento de producción.
 *
 * @rule BR-PROD-EVENT-001 — No cancelar eventos DONE
 * @see cliter/production-planning/business-rules/production-order-position-event.md
 */
async cancel(eventId: string): Promise<void> {
  ...
}
```

**Reglas de uso**:

- Las citas son **opcionales**. No bloquean PRs ni builds.
- Si citas una regla inexistente, el `audit` lo reporta como huérfana, pero el build pasa.
- Si una regla activa lleva más de 30 días sin ninguna cita en código, el `audit` la lista como
  "zombi candidata" para considerar.

---

## Asunciones y verificaciones pendientes

Cuando se documenta una regla y un caso no es trivial de derivar del código:

```markdown
- **[VERIFICAR caso 4]**: si X entonces ¿Y o Z?
```

```markdown
- **[ASUNCIÓN]**: asumimos que <condición>; verificar con producto.
```

El `audit` reporta marcadores con más de **30 días** sin resolver. La recomendación es revisarlos
periódicamente con `/business-rules:audit`.

---

## Hook opcional `UserPromptSubmit`

**Cómo se comporta**: el hook NO bloquea ni obliga a nada. Cuando detecta que tu prompt menciona
algo del catálogo, añade las reglas relevantes al contexto de Claude para que las tenga a mano;
Claude decide si las usa o no. Es información disponible, no enforcement. Para desactivarlo,
eliminar la entrada `UserPromptSubmit` de `.claude/settings.json`.

Se registra en `.claude/settings.json` un hook que ejecuta `.claude/hooks/inject-rules-context.ts`
antes de cada prompt del usuario.

**Qué hace**:

- Lee el prompt del usuario.
- Calcula un score de relevancia contra cada regla del índice usando:
  - Mención explícita del ID (peso 50).
  - Nombre del agregado (peso 10).
  - Nombre del bounded context (peso 5).
  - Keywords (peso 3, ignorando palabras < 4 caracteres).
- Si encuentra reglas con score > 0, inyecta hasta las **3 más relevantes** (cuerpo completo, hasta
  4000 caracteres por regla) en el contexto de Claude.

**Failsafe**: si `cliter/business-rules-index.json` no existe, el hook sale silenciosamente. El
desarrollo no se interrumpe.

## Comandos de operación

| Comando                             | Función                                                     |
| ----------------------------------- | ----------------------------------------------------------- |
| `pnpm br:generate`                  | Regenera `business-rules-index.json` y `INDEX.md`.          |
| `pnpm br:validate`                  | Valida formato y consistencia del catálogo.                 |
| `pnpm br:validate:citations`        | Cruza `@rule` en código con el catálogo (huérfanas/zombis). |
| `pnpm br:check [args]`              | Validación dirigida (agregado, regla, PR, working dir).     |

> Los flujos de **promote**, **document** y **audit** se invocan exclusivamente vía slash command
> desde Claude Code. No se exponen como `pnpm br:*` porque siguen el patrón **script + LLM
> continuation**: el script automatiza lo determinista y Claude continúa la sesión rellenando lo que
> requiere razonamiento (bodies de reglas nuevas, propuestas de reglas implícitas, análisis pairwise
> de tensiones). Exponerlos como `pnpm br:*` daría feedback parcial al developer y rompería la
> claridad de la API pública sobre qué flujos requieren sesión interactiva.

### Ejemplos

#### `pnpm br:generate`

```bash
# Tras editar manualmente una regla en cliter/<bc>/business-rules/<agg>.md:
pnpm br:generate
# → ✓ Índice regenerado: 5 reglas (4 activas, 0 derogadas, 1 propuestas) en 3 archivos.
```

Es **idempotente**: ejecutarlo sin cambios produce el mismo output. La pre-commit hook lo invoca
automáticamente cuando detecta cambios staged en `cliter/<bc>/business-rules/*.md`, y a continuación
ejecuta `br:validate` (ver siguiente).

#### `pnpm br:validate`

```bash
pnpm br:validate
# → Resumen: 3 archivos analizados, 0 errores, 0 warnings.
# → ✓ Catálogo válido.
```

Falla con exit `1` si hay errores estructurales (frontmatter inválido, IDs duplicados,
Estado/Severidad ausentes o desconocidos), `2` solo con warnings (paths inexistentes en
`Implementa`). La pre-commit hook lo invoca automáticamente tras `br:generate` cuando hay cambios
staged en el catálogo; aborta el commit si exit code es `1` (warnings exit 2 no bloquean).

#### `pnpm br:validate:citations`

```bash
pnpm br:validate:citations
# → Citaciones encontradas: 3
# → Reglas con al menos una cita: 2
# → ⚠ Citaciones huérfanas (1):
# →   - BR-INVENT-XXX-001 en backend/src/.../foo.ts:42
```

Cruza los `@rule BR-...` del código con el índice. Reporta huérfanas (cita inexistente) y zombis
(regla activa sin citas tras 30 días). Advisory; no bloquea builds.

#### `pnpm br:check [args]`

Cuatro modos según argumento:

```bash
# Mode 1 — Por agregado (lista las reglas del agregado):
pnpm br:check production-planning/production-order-position-event

# Mode 2 — Por regla concreta (detalle completo):
pnpm br:check BR-PROD-HEADER-001

# Mode 3a — Por branch (diff vs main):
pnpm br:check --branch feature/cancel-event

# Mode 3b — Por PR de GitHub (vía gh CLI):
pnpm br:check --pr 123

# Mode 4 — Working tree (archivos modificados sin commitear):
pnpm br:check
```

---

## Slash commands del catálogo

Tres flujos del sistema viven exclusivamente en Claude Code y siguen el patrón **script + LLM
continuation**: el script TypeScript automatiza lo determinista y Claude continúa la sesión
rellenando lo que requiere razonamiento (bodies de reglas nuevas, propuestas de reglas implícitas,
análisis pairwise de tensiones). No tienen alias `pnpm br:*` para no confundir al developer con
output parcial bajo el mismo nombre que la versión completa.

### `/business-rules:promote [<archive>]`

```text
# Sin argumento: opera sobre el archive más reciente
/business-rules:promote

# Con argumento explícito (recomendado):
/business-rules:promote 2026-05-03-cancel-event-restriction
```

Lee la sección `## Business rules affected` del `proposal.md` archivado y aplica las acciones
declaradas (`mantiene`, `deroga`, `extiende`, `Nuevas`). El script hace la parte determinista
(derogations, scaffolds) y Claude continúa la sesión leyendo proposal/design/código/tests para
rellenar el body de cada regla nueva, dejándola con `state: active` antes del commit.

### `/business-rules:document <bc>/<aggregate>`

```text
# Bootstrap retroactivo desde código:
/business-rules:document production-planning/production-order-position-event

# Bootstrap desde un Word funcional convertido a markdown:
/business-rules:document iam/role --from docs/word-export/roles.md
```

El script valida el agregado, crea el scaffold con frontmatter y lista las fuentes a consultar.
Claude continúa la sesión leyendo esas fuentes, identifica reglas implícitas y las propone con
confirmación del usuario antes de escribir cada una.

### `/business-rules:audit [<bc>]`

```text
# Reporte completo del catálogo (estructural + semántico):
/business-rules:audit

# Para limitar el sweep semántico a un BC concreto:
/business-rules:audit production-planning
```

El script corre la parte estructural (4 secciones) y Claude continúa la sesión añadiendo la sección
E con análisis pairwise de tensiones semánticas entre reglas que comparten BC/aggregate/keyword.

#### Cobertura del audit

**Parte estructural** (la genera el script `scripts/business-rules/audit.ts`):

- **Salud**: counts por estado, BCs cubiertos, tamaño estimado en tokens.
- **Drift**: paths/tests inexistentes en `Implementa`, citaciones `@rule` huérfanas, reglas zombi
  (activas >30 días sin citas).
- **Asunciones**: marcadores `[VERIFICAR]` o `[ASUNCIÓN]` con > 30 días.
- **Cross-BC pointers**: reglas que listan varios `bounded_contexts` sin apuntadores en los archivos
  no-dueños.

**Parte semántica** (la rellena Claude tras el script, dentro de la misma sesión):

- **Tensiones**: pares de reglas que comparten BC/aggregate/keyword y muestran contradicción,
  extensión dudosa o necesitan revisión humana. Verdicts:
  `contradiction | extension | orthogonal | needs-review` con confianza 0–1 y explicación corta en
  español.

Sin SDK, sin API key, sin routine, sin GitHub App.

**Detección preventiva en cambios**: complementaria al audit, ocurre al proponer cambios vía
`/opsx:propose` — el `openspec/config.yaml` instruye a Claude a consultar el catálogo y detectar
conflictos ANTES de archivar (Workflow A, paso 1). Es la principal red contra contradicciones.

> Para tooling externo o debugging, los scripts pueden invocarse directamente con `npx tsx
> scripts/business-rules/<script>.ts [args]` (ver el header docstring de cada uno).

---

## Skill `business-rules-guard`

Vive en `.claude/skills/business-rules-guard/`. **No la invoca ningún slash command directamente**;
Claude la auto-carga cuando detecta señales del sistema en la conversación.

### Cuándo se carga

La skill declara `auto_invoke` en su frontmatter cubriendo:

- Mención de business rules / reglas de negocio en cualquier idioma.
- Citaciones explícitas como `BR-PROD-EVENT-005`.
- Invocaciones de `/business-rules:*`.
- Modificaciones a archivos bajo `cliter/<bc>/business-rules/`.
- Cuando se va a tocar código de un agregado con reglas activas.

Cuando alguno de esos triggers aparece, Claude carga el `SKILL.md` y, si necesita guidance
específica, los archivos de `references/`.

### Qué aporta sobre los slash commands

| Slash command                 | Skill `business-rules-guard`                                 |
| ----------------------------- | ------------------------------------------------------------ |
| Acción concreta y delimitada. | Contexto y guidance que aplica a TODA acción.                |
| Se invoca explícitamente.     | Se auto-carga cuando aplica.                                 |
| Documenta su comando.         | Documenta el SISTEMA: convenciones, anti-patterns, workflow. |
| Self-contained.               | Apunta a 3 references con detalle exhaustivo.                |

### Estructura

```
.claude/skills/business-rules-guard/
├── SKILL.md                          ← entry point + 10 anti-patterns
└── references/
    ├── format.md                     ← formato exhaustivo del .md (estructura
    │                                   por regla, plantillas por Tipo, errores
    │                                   comunes)
    ├── workflow.md                   ← 6 workflows: añadir vía OpenSpec,
    │                                   retroactivo, derogar+crear, cross-BC,
    │                                   resolver asunciones, citas opcionales
    └── audit.md                      ← interpretación del audit, buckets de
                                        tamaño, resolución por sección
```

### Cuándo consultar la skill manualmente

- Cuando vas a añadir tu primera regla y quieres ver el formato canónico.
- Cuando un audit reporta un finding que no sabes resolver.
- Cuando dudas si un cambio requiere derogar o si basta con edit en sitio.
- Cuando vas a hacer bootstrap de un BC nuevo y quieres el workflow paso a paso.

```
# Lectura recomendada por orden:
1. Este README (visión general + comandos).
2. .claude/skills/business-rules-guard/SKILL.md (anti-patterns + 3 rutas).
3. references/format.md cuando necesites escribir una regla.
4. references/workflow.md cuando necesites aplicar uno de los 6 workflows.
5. references/audit.md cuando necesites interpretar findings.
```

---

## Anti-patrones (no hacer)

1. **Editar la semántica de una regla en sitio**. Cambiar el enunciado o la tabla de casos sin
   derogar es un anti-patrón. Cualquier cambio semántico exige derogar + crear nueva.
2. **Reutilizar IDs de reglas derogadas**. Compromete la trazabilidad histórica.
3. **Traducir el catálogo a un solo idioma** o "armonizar" la mezcla. La mezcla es deliberada.
4. **Crear reglas bajo `openspec/`** o `docs/business-rules/<bc>/`. Las reglas viven en
   `cliter/<bc>/business-rules/<aggregate>.md`.
5. **Modificar nada bajo `openspec/`** excepto `openspec/config.yaml`. `openspec update` puede
   regenerar otros archivos.
6. **Citar `@rule` con un ID inexistente**. La cita debe coincidir con una regla del catálogo. El
   audit lo detecta.
7. **Documentar reglas exhaustivamente up-front** para todo el dominio. Es la receta para que el
   catálogo se abandone. Bootstrap perezoso.
8. **Tratar el catálogo como verdad ejecutable**. La verdad ejecutable es el código y los tests. El
   catálogo es **memoria** y **detector de incoherencias**.

---

## Cómo se detectan conflictos al proponer cambios

Cuando ejecutas `/opsx:propose`:

1. La IA recibe instrucciones (vía `openspec/config.yaml`) para consultar el catálogo.
2. Identifica los bounded contexts candidatos (por keywords del prompt y por paths previstos del
   cambio).
3. Carga las reglas activas de esos BCs filtradas por `bounded_contexts` en plural (las cross-BC
   aparecen automáticamente).
4. Pregunta al LLM si la propuesta contradice alguna regla.
5. Como paso de gracia, pregunta también si hay BCs que no se han considerado.
6. Si encuentra conflicto, presenta opciones al user antes de seguir:
   - **(a)** Derogar la regla anterior.
   - **(b)** Crear una excepción que coexista.
   - **(c)** No es conflicto, justifica.
7. La resolución queda registrada en `proposal.md` (sección "Business rules affected") y se
   cristaliza al archivar con `/business-rules:promote`.

Esa detección preventiva es la principal red contra contradicciones. Para revisar el catálogo
completo on-demand, ejecuta `/business-rules:audit`: corre la parte estructural y, en la misma
sesión, Claude analiza pares de reglas y reporta tensiones semánticas (sección E del reporte). No
hay routine automatizada — la simplicidad operativa pesa más que la red de seguridad continua.

---

## Límites operativos del catálogo

El audit emite warnings escalonados según el tamaño del catálogo:

| Reglas activas | Comportamiento                                              |
| -------------- | ----------------------------------------------------------- |
| < 150          | Operación cómoda. Sin warnings.                             |
| 150 – 250      | Log informativo. Tamaño aún manejable.                      |
| 250 – 500      | Warning. Sugerir filtrado por BC en propose.                |
| 500 – 1000     | Soft error. Filtrado por BC obligatorio.                    |
| > 1000         | Replantear arquitectura (RAG / sub-catálogos / embeddings). |

---

## Referencias

- [`_schema.json`](./_schema.json) — JSON Schema del frontmatter.
- [`INDEX.md`](./INDEX.md) — navegación humana generada (no editar a mano).
- [`docs/proposals/business-rules-system.md`](../proposals/business-rules-system.md) — antecedente
  histórico, conservado.
- [`openspec/changes/archive/2026-05-02-add-business-rules-system/`](../../openspec/changes/archive/)
  — propose original (cuando se archive).
