# Catálogo de Harness Rules

> Convenciones globales del catálogo de **Harness Rules (HR)**: reglas arquitectónicas y de
> framework del proyecto. **Léelo entero antes de añadir o modificar reglas.**

> Este catálogo es **paralelo** al de [Business Rules (BR)](../business-rules/README.md). BR
> documenta invariantes de **negocio** (cancelaciones, cascadas, políticas de dominio); HR documenta
> reglas del **arnés**: cómo se estructura el código, dónde viven las cosas, qué patrones se siguen.
> Un developer debería descubrir AMBOS catálogos: si buscas una regla de dominio, ve a BR; si buscas
> una convención arquitectónica, estás en el sitio correcto.

---

## Propósito

El catálogo HR guarda las reglas de **arnés** (harness) del proyecto en forma legible para humanos y
para la IA. Es:

- **Guía arquitectónica enforce-able**. Cada regla expresa una convención (ubicación de ports, edición
  de generados, modos de desarrollo) en un formato que un futuro guard (slice S3) podrá vigilar vía
  sus `### Anchor patterns`.
- **Human-readable y searchable desde el primer merge**. Aunque el enforcement automático llegue
  después, el valor del catálogo es inmediato: un dev puede leer `HR-MODULES-001` y entender por qué
  no crear un módulo paralelo.
- **Portable**. Como el de BR, se distribuye por filesystem: el catálogo core viaja con el proyecto y
  las reglas bc-bound viajan con su BC.

El catálogo se cristaliza **post-implementación**, igual que BR: documenta convenciones ya vigentes
en el código y validadas (cada seed rule proviene de un memo de auto-memory verificado o de un spec
de OpenSpec existente), no specs prescriptivos inventados up-front.

---

## Ubicación (dual)

```
cliter/
├── harness-rules/                        # core (scope: project)
│   ├── modules.md
│   ├── bridges.md
│   ├── generated-code.md
│   ├── ports-adapters.md
│   ├── dev-modes.md
│   └── types-acl.md
├── <bounded-context>/
│   └── harness-rules/                     # bc-bound / multi-bc
│       └── <topic>.md                     # solo activa si <bc> está instalado
└── harness-rules-index.json               # índice consolidado (generado)
```

El parser globbea **ambos patterns**: `cliter/harness-rules/*.md` (core) y
`cliter/*/harness-rules/*.md` (bc-bound / multi-bc). La portabilidad la da el filesystem: si un BC no
se instala, su carpeta `cliter/<bc>/harness-rules/` no existe y sus HRs no aparecen en el índice.

Una regla vive en **un único archivo de topic**. El nombre del archivo describe el TOPIC (`modules`,
`bridges`, `ports-adapters`), no un agregado — las HRs son mayoritariamente procedurales.

---

## Convención de identificadores

```
Core:        HR-<TOPIC>-<NNN>            → HR-MODULES-001
Bc-bound:    HR-<BC>-<TOPIC>-<NNN>       → HR-TOOLS-MIGRATIONS-001 (cuando exista)
Multi-BC:    usa el BC primario en el ID, declara el resto en bounded_contexts
```

- **MAYÚSCULAS** deliberadamente, igual que BR. Destacan como referencias en código, prosa y PRs.
- El ID es legible sin abrir el índice: leyendo `HR-TOOLS-MIGRATIONS-001` se entiende que solo aplica
  si `tools` está instalado.
- El ID debe casar el patrón `[A-Z]{2,}-[A-Z0-9-]+`.
- `<NNN>` es secuencial, zero-padded a 3 dígitos, único dentro de su prefijo de topic.
- **No se reutilizan IDs**: una regla derogada conserva su ID permanentemente.

---

## Scope (semántica)

`scope` es un campo de frontmatter exclusivo de HR (BR no lo tiene). Gobierna dónde aplica la regla y
cuándo aparece en el índice:

| `scope`    | `bounded_contexts`        | Aparece en el índice cuando…                                                  |
| ---------- | ------------------------- | ----------------------------------------------------------------------------- |
| `project`  | `[]` (vacío, obligatorio) | Siempre. Regla core/global, sin anclaje a BC.                                 |
| `bc-bound` | exactamente un BC          | El BC declarado está instalado (`cliter/<bc>/` existe).                       |
| `multi-bc` | dos o más BCs              | **TODOS** los BCs declarados están instalados.                                |

La regla **multi-bc "ALL declared BCs installed"** es deliberada: si la integración no es físicamente
posible por presencia parcial de BCs, citar la regla sería ruido y produciría reglas zombi por
diseño. Las seis seed rules actuales son todas `scope: project`.

---

## Estructura frontmatter

Validada por [`_schema.json`](./_schema.json) (`additionalProperties: false`: no metas otras keys).

```yaml
---
scope: project # project | bc-bound | multi-bc
bounded_contexts: [] # vacío para project; kebab-lowercase para bc-bound/multi-bc
last_updated: 2026-05-28 # ISO YYYY-MM-DD
keywords: # ≥1, BILINGÜE deliberado (es + en)
  - shadow module
  - módulo paralelo
paths: # ≥1; cada uno empieza por backend/ frontend/ cliter/ scripts/ docs/
  - backend/src/@app/**/*.module.ts
---
```

Diferencias clave con el frontmatter de BR:

- `bounded_contexts` admite `minItems: 0` (BR exige ≥1).
- `aggregates` es **opcional** (BR lo exige). Las HRs core son procedurales: NO declaran `aggregates`.
- `scope` es un campo **nuevo y obligatorio** (BR no lo tiene).
- `paths` deben empezar por un top-level del repo (`backend/`, `frontend/`, `cliter/`, `scripts/`,
  `docs/`).

---

## Mezcla deliberada de idiomas

Idéntica a la de BR. Es una decisión de diseño **explícita**:

| Elemento                                | Idioma                          |
| --------------------------------------- | ------------------------------- |
| Prosa (enunciados, glosarios, matrices) | **Español**                     |
| Identificadores (IDs, frontmatter keys) | **Inglés**                      |
| Rutas, paths, JSON keys, citaciones     | **Inglés**                      |
| Keywords                                | **Bilingüe** (español + inglés) |

> **CRÍTICO — DO NOT HARMONIZE**: ningún agente, script ni dev debe "armonizar" la mezcla a un solo
> idioma. Si ves "español + inglés" en una regla, es a propósito.

---

## Estado y severidad

Cada regla declara dos dimensiones en su tabla de metadatos, justo tras el `##` heading:

### Estado

| Valor       | Significado                                                 |
| ----------- | ----------------------------------------------------------- |
| `active`    | Convención vigente. (Las seis seed rules son `active`.)     |
| `derogated` | Reemplazada o retirada. Su ID queda permanentemente tomado. |
| `proposed`  | Documentada pero aún no vigente.                            |

### Severidad

| Valor           | Significado                                                                   |
| --------------- | ----------------------------------------------------------------------------- |
| `blocking`      | Romperla rompe la arquitectura o introduce un anti-patrón estructural grave.  |
| `informational` | Orientación de criterio. Su violación genera fricción, no rotura estructural. |

> Solo dos niveles, sin "warning" intermedio. `HR-MODES-001` (Solution mode = YAGNI) es la única seed
> rule `informational`: es una guía de criterio, no un invariante estructural bloqueante.

---

## Estructura de un archivo de reglas

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

# <título humano del topic>

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

---

## HR-<TOPIC>-<NNN> — <título corto de la regla>

| Campo     | Valor                                 |
| --------- | ------------------------------------- |
| Estado    | `active` \| `derogated` \| `proposed` |
| Severidad | `blocking` \| `informational`         |
| Origen    | <cita de fuente: memo o spec>         |

### Enunciado

<una o dos frases en español describiendo la convención.>

### Casos exhaustivos

<!-- DECISION MATRIX: situación → ¿permitido? → acción correcta -->

| #   | Situación | ¿Permitido? | Acción correcta |
| --- | --------- | ----------- | --------------- |
| 1   | …         | SÍ / NO     | …               |

### Anchor patterns

<!-- globs / heurísticas que un futuro guard (slice S3) debe vigilar -->

- <glob o heurística>

### Implementa

<!-- OPCIONAL. Si la incluyes, cada `path` en backticks DEBE resolver en disco
     o `hr:validate` saldrá con exit 2 (warning). Si no estás seguro, OMÍTELA
     y referencia la fuente en prosa. -->

### Tests

<!-- OPCIONAL -->

### Reglas relacionadas

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

El separador del heading es un **EM-DASH** `—` (U+2014) con un espacio a cada lado: `## <ID> — <título>`.
La tabla de metadatos va INMEDIATAMENTE después del heading. La separación entre clave y valor en la
tabla debe casar el formato `| Campo | Valor |`.

---

## Diferencias de formato HR vs BR

| Aspecto             | Business Rules (BR)                              | Harness Rules (HR)                                                       |
| ------------------- | ------------------------------------------------ | ------------------------------------------------------------------------ |
| `scope`             | No existe.                                       | Campo nuevo y obligatorio (`project \| bc-bound \| multi-bc`).            |
| `bounded_contexts`  | ≥1 obligatorio.                                  | `minItems: 0` (vacío para `scope: project`).                             |
| `aggregates`        | Obligatorio.                                     | **Opcional**. Las HRs procedurales no anclan a agregados.                |
| `### Implementa`    | **Obligatorio**.                                 | **Opcional**. Muchas HRs no anclan a líneas concretas.                   |
| `### Tests`         | Opcional.                                        | Opcional (igual).                                                        |
| `### Anchor patterns` | No existe.                                      | **Añadido**: globs/heurísticas para el futuro guard (slice S3).          |
| `### Casos exhaustivos` | Rangos de input/output → resultado.            | **Decision matrix**: situación → ¿permitido? → acción correcta.          |
| `### Diagrama de cascada` | Disponible (cascadas de dominio).            | No aplica (las HRs no describen cascadas de negocio).                    |

El parser es compartido (`scripts/_catalog-runtime/`): el regex de heading admite tanto `BR-...` como
`HR-...`, así que la mecánica de extracción es idéntica.

---

## Lifecycle (post-implementation)

Las HRs se documentan DESPUÉS de que la convención exista y esté validada en el código. No se inventan
reglas up-front. Cada seed rule cita su `Origen`: un memo de auto-memory verificado
(`feedback_*.md`) o un spec de OpenSpec existente (`openspec/specs/cross-bc-ports/spec.md` para
`HR-BRIDGES-001`). Cuando una convención cambia, se deroga la regla antigua y se crea una nueva con ID
nuevo (los IDs no se reutilizan) — mismo lifecycle que BR.

> **Enforcement (estado actual)**: la skill `harness-rules-guard` y la integración con
> `/opsx:propose` (sección `## Harness rules applicable`) ya están activas, igual que
> `/harness-rules:audit`. **`document` y `promote` NO existen para HR por diseño**: una HR no se
> bootstrappea desde el código de un agregado ni se cristaliza por cambio archivado, a diferencia de
> BR. El enforcement es de dos niveles: _content-blocked_ (reglas con `detect`, hoy solo
> `HR-ACL-001`, bloqueadas por un content-guard `PreToolUse`) y _advisory_ (las demás: inyección en
> prompt, detección en propose, mensajes del checkpoint y criterio del agente).

---

## Cómo crear, derogar y modificar reglas

A diferencia de BR, **HR no tiene `promote` ni `document`**: no se cristalizan por cambio archivado
ni se bootstrappean desde el código de un agregado. Una HR es una **restricción arquitectónica** y su
catálogo se opera a mano, por decisión deliberada. Hay tres rutas de interacción con el catálogo:

| Ruta                               | Cuándo aplica                                                                                  | Qué haces                                                                          |
| ---------------------------------- | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| **Consultar antes/durante codear** | Vas a tocar un path que gobierna una HR activa, o un write fue bloqueado citando un `HR-*` id  | Busca en `harness-rules-index.json` (`byPath` / `byKeyword` / `byId`), lee, COMPLY |
| **Declarar en `/opsx:propose`**    | El cambio toca paths gobernados; registras `## Harness rules applicable` en `proposal.md`      | Por cada regla que gobierna: **COMPLY** / **VIOLATE** / **DEROGATE**               |
| **Editar el catálogo**             | Una convención cambió de verdad y la regla debe retirarse o reemplazarse                       | Derogar + crear nueva con ID nuevo; `pnpm hr:generate && pnpm hr:validate`         |

> **Crear, editar o borrar una HR es siempre la tercera ruta (edición directa).** No busques un
> `/harness-rules:promote` ni un `/harness-rules:document`: no existen por diseño. Como crear o retirar
> una HR cambia una convención del proyecto, **envuélvelo idealmente en su propio cambio OpenSpec**.

### Crear una regla nueva

Editar a mano el `.md` del topic correspondiente:

1. Elige el archivo por **topic**, no por agregado: `cliter/harness-rules/<topic>.md` (core) o
   `cliter/<bc>/harness-rules/<topic>.md` (bc-bound / multi-bc).
2. Asigna un ID nuevo `HR-<TOPIC>-<NNN>` (secuencial dentro del prefijo de topic; no reusar
   derogados). Recuerda el **EM-DASH** `—` (U+2014) en el heading: `## HR-... — <título>`.
3. Completa las secciones obligatorias: tabla de metadata (Estado/Severidad/Origen), `### Enunciado`,
   `### Casos exhaustivos` (decision matrix, **mínimo 2 casos con al menos uno negativo**),
   `### Anchor patterns`. `### Implementa` y `### Tests` son opcionales.
4. **Si la regla es mecánicamente detectable por contenido**, añade el bloque `detect: { pattern,
   message }` al frontmatter para hacerla _content-blocked_ (un content-guard `PreToolUse` bloquea
   writes que casen el patrón; hoy la única es `HR-ACL-001`). Si no, omítelo: la regla es _advisory_ y
   su enforcement es el criterio del agente + la skill + la inyección en prompt/propose.
5. `pnpm hr:generate && pnpm hr:validate` (o lo hace la pre-commit hook al commitear).

### Derogar una regla (= "borrar")

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

1. Localízala: `pnpm hr:check HR-MODULES-001`.
2. En su `.md`, cambia la fila `Estado` de la tabla de metadata a `derogated` y anota en `Origen` la
   regla sustituta.
3. Añade una sección `### Derogación` al final del cuerpo (fecha + razón en una frase).
4. Si hay sustituta, créala con un **ID nuevo** (no reuses el derogado) y enlázalas en
   `### Reglas relacionadas`.
5. `pnpm hr:generate && pnpm hr:validate`.

> Derogar una HR es **raro** y es en sí una decisión arquitectónica — no un efecto colateral de un
> proposal ordinario. La regla derogada permanece visible para que las citas `HR-*` históricas en
> código y PRs sigan siendo unívocas.

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

Cambiar la semántica equivale a derogar la antigua y crear una nueva con ID nuevo. Sigue "Derogar" +
"Crear".

**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`.

**Cambios que exigen derogación**: cambiar el resultado de un caso ya enumerado, el significado del
`### Enunciado`, o la severidad (`blocking` ↔ `informational`).

### COMPLY / VIOLATE / DEROGATE (vocabulario de propose, NO `mantiene`/`deroga`/`extiende`)

En la sección `## Harness rules applicable` de un `proposal.md`, cada regla que gobierna un path
tocado se resuelve con exactamente UNA de:

- **COMPLY** — el enfoque honra la regla. Es el default y el resultado deseado.
- **VIOLATE** — el enfoque la rompe. Para una regla `blocking` esto **detiene** `/opsx:propose` y
  exige decisión del usuario; para `informational` se reporta sin detener.
- **DEROGATE** — decisión deliberada de retirar/reemplazar la regla (su propio cambio arquitectónico).

> **No hay subsección "Nuevas" en un proposal de HR.** Un proposal **registra compliance con el
> catálogo EXISTENTE**; no declara HR a crear después (eso es vocabulario de BR). Si ninguna regla
> activa gobierna los paths tocados, escribe `None`.

---

## Comandos de operación

| Comando            | Función                                                          |
| ------------------ | --------------------------------------------------------------- |
| `pnpm hr:generate` | Regenera `cliter/harness-rules-index.json` y `INDEX.md`.        |
| `pnpm hr:validate` | Valida formato y consistencia del catálogo (exit 0 / 1 / 2).    |
| `pnpm hr:check`    | Validación dirigida (topic, regla, branch, PR, working tree).   |

> `/harness-rules:audit` es el **único** slash command de HR (read-only: salud/drift estructural +
> tensiones semánticas que Claude continúa en la misma sesión). **No tiene alias `pnpm hr:*`** —
> invócalo desde Claude Code o, para tooling, `npx tsx scripts/harness-rules/audit.ts` (`--json`). No
> hay `document` ni `promote` para HR (ver [Cómo crear, derogar y modificar
> reglas](#cómo-crear-derogar-y-modificar-reglas)).

### Exit codes de `pnpm hr:validate`

- `0` — catálogo válido ("Catálogo válido").
- `1` — errores estructurales (frontmatter inválido, IDs duplicados, Estado/Severidad ausentes o
  desconocidos).
- `2` — solo warnings: típicamente un `path` citado en `### Implementa` que ya no resuelve en disco.
  Por eso la regla práctica es: cita en `### Implementa` solo paths verificados; si no, omítela.

### Pre-commit

La pre-commit hook regenera y valida el catálogo HR cuando hay cambios staged en
`cliter/harness-rules/**/*.md` o `cliter/*/harness-rules/**/*.md`, y añade el
`harness-rules-index.json` y el `INDEX.md` regenerados al mismo commit. Un cambio solo-BR NO dispara
la regeneración de HR (y viceversa).

---

## Anti-patrones (no hacer)

1. **Crear un módulo paralelo para no tocar un generado** — ver `HR-MODULES-001`.
2. **Editar la semántica de una regla en sitio** sin derogar. Cambio semántico = derogar + crear con
   ID nuevo.
3. **Reutilizar IDs derogados**. Compromete la trazabilidad histórica.
4. **Armonizar la mezcla de idiomas** a un solo idioma. Es deliberada.
5. **Editar `cliter/harness-rules-index.json` o `docs/harness-rules/INDEX.md` a mano**. Son
   generados: corre `pnpm hr:generate`.
6. **Declarar `aggregates` en una HR procedural** solo por costumbre de BR. Es opcional; omítelo salvo
   que la regla sea aggregate-specific.
7. **Citar paths inexistentes en `### Implementa`**. Provoca exit 2; cita solo paths verificados o
   referencia la fuente en prosa.

---

## Referencias

- [`_schema.json`](./_schema.json) — JSON Schema del frontmatter HR.
- [`INDEX.md`](./INDEX.md) — navegación humana generada (no editar a mano).
- [`.claude/skills/harness-rules-guard/SKILL.md`](../../.claude/skills/harness-rules-guard/SKILL.md)
  — skill que guía la gestión del catálogo (las tres rutas, COMPLY/VIOLATE/DEROGATE, advisory vs
  content-blocked). Detalle exhaustivo en sus `references/format.md` y `references/enforcement.md`.
- [`docs/business-rules/README.md`](../business-rules/README.md) — catálogo paralelo de reglas de
  negocio. Descúbrelo también.
- `openspec/specs/cross-bc-ports/spec.md` — convención cross-BC formalizada (fuente de
  `HR-BRIDGES-001`).
