# Claim Graph Schema

> JSON schema for `claims.json` and `sources.json`. Replaces the flat
> `findings.tsv` ledger. Load when reasoning about claim state, source
> tiers, dependencies, or status transitions.

## Why a graph, not a list

A flat ledger answers "what did we find?" but not "what does each finding
depend on?" or "if we retract this claim, what else collapses?" The graph
makes the dependency structure explicit so:

- Priority can rank by `centrality` (how many other claims depend on this one)
- Skeptic can target the most load-bearing claim, not the weakest one
- Retraction propagates (downgrading a root claim downgrades its descendants)
- Contradictions surface as edges, not as silently-averaged confidence

## `claims.json` — one entry per discovered claim

```json
{
  "version": 1,
  "claims": {
    "C-001": {
      "statement": "Softer rear axle increases lateral grip on no-diff karts.",
      "source_tier": "community-lore",
      "conditions": {
        "kart_class": "OK1",
        "track_temp": "any",
        "tire_compound": "any"
      },
      "evidence_for": ["S-003", "S-007", "S-012"],
      "evidence_against": ["S-005"],
      "depends_on": ["C-002"],
      "supports": ["C-009"],
      "contradicts": [],
      "impact": "high",
      "centrality": 0.83,
      "uncertainty": 0.62,
      "confidence": "medium",
      "status": "contested",
      "last_attacked_iter": 4,
      "history": [
        {
          "iter": 1,
          "status": "open",
          "confidence": "low",
          "note": "Seeded from spec sub-question Q3."
        },
        {
          "iter": 4,
          "status": "contested",
          "confidence": "medium",
          "note": "Skeptic surfaced S-005 (SAE paper) contradicting forum consensus. Judge moved to contested."
        }
      ]
    }
  }
}
```

### Field rules

| Field | Required | Notes |
|---|---|---|
| `statement` | yes | One sentence, falsifiable form |
| `source_tier` | yes | `peer-reviewed \| independent-test \| primary-doc \| vendor-doc \| community-lore \| derived-theory \| hypothesis` |
| `conditions` | yes | Object of context dimensions; `{}` if unconditional |
| `evidence_for` / `evidence_against` | yes | Arrays of source IDs (`S-NNN`) |
| `depends_on` | yes | Claim IDs this claim derives from; empty if root |
| `supports` / `contradicts` | yes | Claim IDs this claim affects |
| `impact` | yes | `high \| medium \| low` — from spec `## Critical claims` |
| `centrality` | computed | 0-1; out-degree on `supports`+`contradicts` normalized by graph size |
| `uncertainty` | computed | 0-1; function of evidence-for/against ratio + source tiers |
| `confidence` | yes | `high \| medium \| low \| contested` |
| `status` | yes | `open \| live \| dead \| contested \| retracted \| hypothesis \| converged` |
| `last_attacked_iter` | yes | Integer; iter number when last picked by priority |
| `history[]` | yes | Append-only; every Judge ruling adds an entry |

### Status transitions

```
open ─────┬──> live ──┬──> converged
          │           │
          ├──> contested
          │           │
          ├──> hypothesis (domain ≠ mechanistic + first-principles output)
          │
          └──> retracted (citation audit failed, no other support)

dead = candidate approach with documented prior failure (SOLVE mode)
```

## `sources.json` — one entry per discovered source

```json
{
  "version": 1,
  "sources": {
    "S-003": {
      "url": "https://www.kartpulse.com/threads/axle-stiffness-explained.1234",
      "tier": "community-lore",
      "type": "forum-post",
      "fetched_at": "2026-05-24T10:14:22Z",
      "verified_at": "2026-05-24T11:42:08Z",
      "derivation_chain": [],
      "quality": {
        "author_credentials": "unknown",
        "replicated": false,
        "peer_reviewed": false
      },
      "quote_anchor": "softer axles flex more under load and..."
    },
    "S-005": {
      "url": "https://saemobilus.sae.org/papers/...",
      "tier": "peer-reviewed",
      "type": "paper",
      "fetched_at": "2026-05-24T10:22:09Z",
      "verified_at": "2026-05-24T10:22:31Z",
      "derivation_chain": [],
      "doi": "10.4271/2019-01-1234",
      "doi_verified_via": "crossref",
      "quality": {
        "n": null,
        "design": "analytic",
        "peer_reviewed": true,
        "replicated": null
      },
      "quote_anchor": "torsional stiffness coupling to inside-rear lift..."
    }
  }
}
```

### Field rules

| Field | Required | Notes |
|---|---|---|
| `url` | yes | Canonical URL or DOI URL |
| `tier` | yes | Matches the claim `source_tier` taxonomy |
| `type` | yes | `paper \| RCT \| meta-analysis \| primary-doc \| vendor-doc \| forum-post \| blog \| book \| video \| code` |
| `fetched_at` | yes | ISO-8601 timestamp at first fetch |
| `verified_at` | nullable | ISO-8601 timestamp at last citation-audit pass; `null` until verified |
| `derivation_chain` | yes | Array of source IDs this source cites; **load-bearing for independence checks** |
| `quality` | yes | Object; fields depend on `type` — `{n, design, peer_reviewed, replicated}` for empirical |
| `quote_anchor` | yes | The exact substring the claim relies on; citation audit re-fetches and checks ≥80% match |

### Derivation chain (the independence check)

Three sources that all cite the same primary source = **one** source for the
purpose of triangulation. When computing `centrality` and `uncertainty`, the
Judge must walk `derivation_chain` to deduplicate effective sources. The
schema captures this explicitly so future iterations don't double-count.

## Computed fields

`centrality` and `uncertainty` are recomputed by the Judge at the end of every
iteration, NEVER written by Builder/Skeptic/Retriever. Formula reference:

```
centrality(C) =
  ( |descendants_in_supports(C)| + |descendants_in_contradicts(C)| )
  / max(1, |total_claims|)

uncertainty(C) =
  1 - weighted_evidence_for(C) / (weighted_evidence_for(C) + weighted_evidence_against(C))

  where weight(S) =
    { peer-reviewed: 1.0,
      independent-test: 0.85,
      primary-doc: 0.7,
      vendor-doc: 0.5,
      community-lore: 0.25,
      derived-theory: domain == mechanistic ? 0.6 : 0.2,
      hypothesis: 0.1 }
```

`impact` is NOT computed — it comes from the spec's `## Critical claims`
section, optionally adjusted by the Judge when graph structure changes
(a claim acquiring many descendants gets impact bumped).

`priority(C) = impact(C) × centrality(C) × uncertainty(C)` — the next claim
to attack is `argmax(priority)` over claims with `status ∈ {open, contested,
live}`.

## Round-trip render to `RESEARCH.md`

The Judge renders a compact human-readable view into `RESEARCH.md`'s
`## Claim graph summary` section at synthesis time. The graph itself
(`claims.json` + `sources.json`) is the canonical store; the markdown is a
projection. Renderer guidance:

- Top 5 by `impact × centrality`
- Format: `**[{status}]** {statement} _(confidence: {confidence}, tier: {source_tier}, conditions: {summary})_`
- For each contested claim, render both positions and their strongest evidence
  side-by-side. Do NOT collapse them.

## Migration from `findings.tsv` (if encountered on resume)

If `--resume` finds a legacy `findings.tsv` (from a pre-v1 session), the skill
must:
1. Stop. Print: "Legacy findings.tsv detected. Migrate with `apt-tools research migrate-graph {task-id}`."
2. Refuse to continue until migration runs.

This is intentional — there is no automatic mapping from flat ledger to graph
because the dependency edges aren't recoverable from the old format.
