Developers

The type contract

The invariant is not a convention, it is a type. Every module compiles against a shape where a green cannot exist without a span.

Two verdicts, a cite, a reason

A result is proven with a cited span or a gap with a reason. There is no field for an approval that skips the chart.

lib/engine/types.ts
export type Verdict = "proven" | "gap";

export interface CriterionResult {
  criterion: Criterion;
  verdict: Verdict;
  passes: EvidencePass[];
  // Present if and only if proven: the cited span.
  cite?: ChartSpan;
  // Present if and only if gap: what is missing and the one line that fixes it.
  gapReason?: string;
  rationale: string;
}

What the shape guarantees

No third verdict

Verdict is proven or gap. Nothing else type-checks.

A cite rides every green

The cited span travels on the result, so a reviewer can follow it.

A reason rides every gap

A gap carries what is missing, so the output is a to-do, not a dead end.