# Finding Format — Sub-agent Return Contract

Every validation sub-agent dispatched by `validate-repo` MUST return a single JSON object that conforms to this schema. The orchestrator parses this JSON to consolidate the final report.

## Top-level shape

```json
{
  "agent": "<agent identifier>",
  "findings": [ ... ],
  "skipped_checks": [ ... ],
  "metadata": {
    "files_examined": <int>,
    "rules_evaluated": <int>,
    "duration_seconds": <float, optional>,
    "notes": "<free text, optional — degradations, limitations, surprises>"
  }
}
```

`agent` MUST be one of: `gemma-style`, `naming`, `logic`, `target-structure`, `claudemd`, `kimball`.

## Finding entry

Every entry in `findings` MUST have these keys:

```json
{
  "agent": "<same as top-level agent>",
  "severity": "critical" | "major" | "minor" | "info",
  "rule": "<short kebab-case rule identifier>",
  "file": "<repo-relative path or null>",
  "line": <int or null>,
  "summary": "<one-line description>",
  "details": "<longer description with context, can be multi-line>",
  "suggested_fix": "<concrete actionable suggestion>",
  "confidence": "high" | "medium" | "low",
  "evidence": "<optional, but REQUIRED for absence/negative claims — the exact block inspected at file:line, quoted, that proves the claim. See 'Evidence requirement' below.>"
}
```

### Severity definitions

| Severity | Definition | Examples |
|---|---|---|
| `critical` | Will produce wrong numbers, broken dependencies, or block CI. Must fix before next release. | Non-additive measure stored as a fact, dim table without a primary-key test, circular ref. |
| `major` | Strong convention violation that hurts maintainability or correctness in foreseeable cases. Should fix soon. | Base model selecting from another base model, missing surrogate key on SCD2 dim, model named inconsistently with peers. |
| `minor` | Cosmetic or low-impact convention violation. Fix opportunistically. | Missing column descriptions, single-line comments where multi-line would be clearer, inconsistent indentation. |
| `info` | Observation, not a violation. Useful context for the reader. | "This dim has 47 columns — consider whether it should be split", "kimball check downgraded because CLAUDE.md overrides it". |

### Confidence definitions

| Confidence | When to use |
|---|---|
| `high` | The rule is unambiguous and the violation is visible in the file content. |
| `medium` | The rule applies but context might exempt it (e.g., naming convention violation that could be intentional for client-specific reasons). |
| `low` | Heuristic judgement — the agent suspects a problem but cannot prove it without more context. Always pair with a `details` block explaining what would resolve the ambiguity. |

### Evidence requirement for absence/negative claims

A large share of false positives come from asserting something is *missing* without having
actually looked at the place it would live. So:

- Any finding whose claim is that something is **absent / missing / not declared** (e.g. "no
  `unique` test", "missing column description", "lacks a PK test", "no grain documented") MUST
  populate the `evidence` field with the **exact block inspected, quoted, at a cited
  `file:line`** — i.e. the column/model's `data_tests:` / `tests:` / description block, showing
  what *is* there so the absence is provable from the quote.
- **If you did not open and read that exact block, you may NOT assert the absence.** Either omit
  the finding and record it in `skipped_checks` (reason: e.g. `could-not-verify-yaml-block`), or
  emit it at `confidence: low` with `details` stating it is unverified.
- A "missing X" finding without `evidence` is treated as unverified. For **critical and major**
  findings the orchestrator's C3b verification step will withdraw it. For **minor and info**
  findings no withdrawal gate applies — so prefer skipping (`skipped_checks`) over emitting an
  unverified absence claim at any confidence level.

### Degraded mode (no `dbt parse` / filename-only)

When `dbt parse` did not produce a manifest (filename-only mode — common in CI), grepping nested
YAML test/config blocks is error-prone:

- Findings that depend on reading a nested YAML block (test presence, grain/additivity
  annotations, persist_docs, etc.) must **not** use `confidence: high` — cap at `medium`.
- Absence claims in this mode still require the quoted `evidence` block. If the block can't be
  located unambiguously, skip the finding (`skipped_checks`) rather than asserting absence.

## Skipped check entry

```json
{
  "rule": "<rule identifier>",
  "reason": "<why this check could not run or was suppressed>"
}
```

Examples of legitimate skips:
- `dbt-parse-failed` → logic checks that depend on manifest.json could not run
- `kimball-rule-overridden-by-claudemd` → CLAUDE.md explicitly contradicts this Kimball rule
- `target-structure-doc-not-found` → the project supplied no architecture doc, so no consistency check could be performed

## Override precedence

When `kimball` checks conflict with `target-structure` or `claudemd` findings, the kimball agent MUST:
1. Detect the override (the orchestrator will pass it the override summary).
2. Either suppress the rule entirely (add to `skipped_checks`) or downgrade severity to `info`.
3. Never report a `kimball` rule as `critical` or `major` if it has been explicitly overridden.

This is the only cross-agent precedence rule. All other agents are independent.

## Validity rules (orchestrator will reject malformed responses)

- `findings` must be an array (possibly empty)
- `severity` and `confidence` must use the exact spellings above
- `rule` must be kebab-case, ≤ 60 characters
- `summary` must be a single line, ≤ 120 characters
- `file` is repo-relative (never absolute) or `null`
- If `line` is provided, `file` must also be provided
- `evidence` is optional in general but REQUIRED for absence/negative claims (see above)
- Sub-agent must NOT include any keys outside the schema above — `evidence` is now part of the schema (forward-compat is otherwise achieved via `metadata.notes`)
