# E-014 — A nested Git repository hid the project contract

- Date: 2026-08-03
- Status: confirmed integration false negative, fixed with an automated regression.

## Hierarchy of objectives

- Parent objective: alert early and deterministically about changes relevant to the declared objective.
- Phase objective: offer global integration with Claude Code and a sanitized global inventory.
- Round objective: validate every `Edit`/`Write` event against the contract of the project that contains the edited file.

## Observed trajectory

In `schema-forge`, the local contract and its plan were created correctly and excluded before `oguard init`; therefore they did not enter the Git index or the `d54db03cf` commit, which predates the contract's creation. When working from `etendo_core/modules/com.etendoerp.go`, which is a nested Git repository, the global context reported that the repository had no contract.

The contract was still present in the parent project and `git status --porcelain` remained empty. The negative signal came from resolving `git rev-parse --show-toplevel` first from the nested directory: the hook picked the nearest Git repository, not the Objective Guard project that contained the file.

## Expected and actual behavior

The expected behavior is to look for the nearest contract by walking up from the edited file — and, as a fallback, from the `cwd` supplied by Claude — even if a nested Git boundary is crossed. Once found, the check must run from that project root.

The actual behavior stopped the search at the first `git rev-parse`; as a result, it skipped a valid contract and emitted the absence message. It is a false negative of the integration: it is not a finding from a product rule, nor evidence about the sensitivity of semantic detectors.

## Evidence

- Local contract and plan created after commit `d54db03cf` (08:35:58), at approximately 08:50.
- From `schema-forge`, the hook resolved the contract; from `etendo_core/modules/com.etendoerp.go`, it did not.
- The artifacts remained local, excluded, and outside Git state.
- `test/claude-hook.test.mjs` reproduces a parent structure with a contract and a nested directory, and requires resolving the parent.

## Fix and impact

The hook now searches for `oguard.contract.json` up through the ancestors before resolving Git, using the event's file as the primary anchor point. The global prompt adopts the same search and delivers commands explicitly anchored to the contract root.

The fix avoids incorrect silence in monorepos or nested repositories. It does not yet extend the contract to multiple candidate contracts, nor does it decide what to do when two contracts apply: the current policy picks the nearest one, which is deterministic and is covered by the regression.

Subsequent dogfooding of this repository produced `goal-substitution` with five files outside the scope of the frozen round `R-GOVERNANCE-HEALTH`. The signal was useful: the hook change does not belong to that round, and the contract is not modified retroactively to make it pass. The closure remained at `WARN`, not `PASS`; a follow-up must open an explicit integration round before further expanding this capability.
