Implements: ADR-030 · Status: Proposed · Updated: 2026-06-13
This spec defines the contract that separates the domain-agnostic engine from a domain plugin. It is a design target, not yet an implementation; the Lean plugin (below) is the conformance reference because it already exists in behaviour, only not yet behind the seam.
The engine never imports domain code directly; it calls the plugin through the operations in §3.
A plugin describes its unit with a small, serialisable record:
WorkUnit {
id : stable slug (Id grammar, as today)
spec : the problem statement, by value or content-address
status : open | claimed | solved | blocked
difficulty : integer rung (drives ranking/credit; plugin-assigned)
deps : ids this unit depends on (⟨⟩ if none)
tier : VERIFIED | SCORED | CONSENSUS (see §4)
}
status is the lifecycle the engine drives; spec, difficulty, deps, and
tier are plugin-owned. The canonical state of a unit lives in the corpus
(today: goals/<id>.aisp on main), which is the single source of truth the
engine reads when deciding whether work remains.
generate(unit, context) -> Candidate
Produce a candidate solution. `context` carries prior-attempt lessons
(ADR-024) and provider/effort selection. The engine owns the provider call;
the plugin owns the prompt/result shaping.
verify(unit, candidate) -> Verdict {
accepted : bool # VERIFIED: deterministic truth
score : number | null # SCORED: higher is better
evidence : artifact # audit report / kernel log / metrics
cost : { wall_s, ... } # for telemetry, never a trust input
}
MUST be deterministic and SHOULD be cheap relative to generate(). This is
the trust kernel; it is the only thing that decides acceptance.
decompose(unit) -> [WorkUnit] # optional
Split an unsolved unit into strictly-smaller sub-units (ADR-009). Advisory:
a unit still closes only through verify(), never through its decomposition.
assimilate(candidate) -> CorpusChange
Render the accepted candidate as the change set merged into the canonical
corpus (new module + index entry + provenance, today).
verify is authoritative and self-contained: the engine trusts its Verdict
and nothing else. generate/decompose are best-effort and may fail without
affecting soundness — a bad candidate simply fails verify.
The tier field selects how many accepted verdicts close a unit:
verify is a deterministic checker; a single accepted
verdict is ground truth. No redundancy needed. (Lean: the kernel gate.)verify returns a score; the unit keeps the best-scoring
accepted candidate; later candidates may supersede on a strictly better score.The engine implements all three; a plugin declares which it uses. The VERIFIED tier is the design centre and the reason unsorry needs no redundancy today.
| Seam element | Lean realisation |
|---|---|
WorkUnit |
a goal (goals/<id>.aisp + .lean), tier = VERIFIED |
generate |
the proof prompt to the selected provider |
verify |
Gate A: lake build --wfail, axiom_audit, leanchecker, statement-binding (ADR-011) |
decompose |
sub-lemma split (ADR-009) |
assimilate |
new library/Unsorry/<Camel>.lean + index entry + provenance |
Re-expressing today’s behaviour through the seam must be behaviour-preserving: the same proofs pass, the same gate fails closed, no soundness change.
Because this is a design ADR, conformance is defined for the eventual implementation, not asserted here:
VERIFIED arithmetic-identity
domain) onboards by implementing only §3, with zero engine edits — the
acceptance test for “is this actually a template.”These are not decided here and have no ADR yet, but are recorded so they are not lost when this seam is built out:
verify (§3) is the trust kernel: a plugin that
lies in its Verdict (e.g. returns accepted=true for an unchecked
candidate) breaks soundness for that domain. First-party plugins (Lean) are
trusted by code review; third-party plugins are not trusted until there is
sandboxing, an independent re-verification path, or a signed/audited plugin
registry. Generalization must never let a plugin lower a domain’s soundness
bar (cf. ADR-030’s “trust model unchanged”).verify reproducibility. A deterministic verdict must hold across
machines and over time; each plugin needs the per-domain analog of ADR-002
(mathlib/toolchain pinning), or a verdict could differ by environment.backlog/ + translation, ADR-007). A general template needs a
pluggable problem-source contract and a way to vet/scope submitted problems.