# E-014 — Un repositorio Git anidado ocultó el contrato del proyecto

- Fecha: 2026-08-03
- Estado: falso negativo de integración confirmado y corregido con regresión automatizada.

## Jerarquía de objetivos

- Objetivo principal: alertar de forma temprana y determinista sobre cambios relevantes para el objetivo declarado.
- Objetivo de fase: ofrecer integración global con Claude Code e inventario global sanitizado.
- Objetivo de ronda: validar cada evento `Edit`/`Write` contra el contrato del proyecto que contiene el archivo editado.

## Trayectoria observada

En `schema-forge`, el contrato local y su plan fueron creados correctamente y excluidos antes de `oguard init`; por lo tanto no entraron en el índice de Git ni en el commit `d54db03cf`, anterior a la creación del contrato. Al trabajar desde `etendo_core/modules/com.etendoerp.go`, que es un repositorio Git anidado, el contexto global informó que el repositorio no tenía contrato.

El contrato seguía presente en el proyecto padre y `git status --porcelain` permanecía vacío. La señal negativa provenía de resolver primero `git rev-parse --show-toplevel` desde el directorio anidado: el hook elegía el repositorio Git más cercano, no el proyecto Objective Guard que contenía el archivo.

## Comportamiento esperado y real

El comportamiento esperado es buscar el contrato más cercano ascendiendo desde el archivo editado —y, como respaldo, desde el `cwd` entregado por Claude— incluso si se atraviesa un límite Git anidado. Una vez encontrado, el chequeo debe ejecutarse desde esa raíz de proyecto.

El comportamiento real detenía la búsqueda en el primer `git rev-parse`; en consecuencia, omitía un contrato válido y emitía el mensaje de ausencia. Es un falso negativo de la integración: no es un hallazgo de una regla de producto ni evidencia sobre la sensibilidad de detectores semánticos.

## Evidencia

- Contrato y plan locales creados después del commit `d54db03cf` (08:35:58), aproximadamente a las 08:50.
- Desde `schema-forge`, el hook resolvía el contrato; desde `etendo_core/modules/com.etendoerp.go`, no.
- Los artefactos permanecieron locales, excluidos y fuera del estado de Git.
- `test/claude-hook.test.mjs` reproduce una estructura padre con contrato y directorio anidado, y exige resolver el padre.

## Corrección e impacto

El hook busca ahora `oguard.contract.json` hacia los ancestros antes de resolver Git, tomando como punto primario el archivo del evento. El prompt global adopta la misma búsqueda y entrega los comandos anclados explícitamente a la raíz del contrato.

La corrección evita un silencio incorrecto en monorepos o repositorios anidados. No amplía todavía el contrato a varios contratos candidatos ni decide qué hacer ante dos contratos aplicables: la política actual elige el más cercano, que es determinista y queda cubierta por la regresión.

El dogfooding posterior de este repositorio produjo `goal-substitution` con cinco archivos fuera del scope de la ronda congelada `R-GOVERNANCE-HEALTH`. La señal fue útil: el cambio de hook no pertenece a esa ronda y no se modifica el contrato retrospectivamente para hacerlo pasar. El cierre quedó en `WARN`, no en `PASS`; una continuación deberá abrir una ronda de integración explícita antes de seguir ampliando esta capacidad.
