Correction (2026-09-23), scoped (2026-09-25). This correction is about EARL modifying its own code. Where this document describes EARL "authoring", "self-modifying" or repairing its own mechanism, read: EARL's governed loop replaces one bounded expression by random search under a grammar the lab supplied, accepted by a lab-frozen gate; nothing in that apparatus learns from its attempts, and self-repair has not been demonstrated. Autonomous code authorship is not an earned claim. Record:
earl/genesis/g9/CORRECTION-2026-09-23-CODE-AUTHORSHIP.md; the original text is preserved as written.It does not describe
repair.develop, the code-repair developer preview, which is a different and narrower capability: EARL diagnoses, constructs and verifies supported repairs in the Rust code you designate, from taught operator meanings plus the requirement you supply, and applies them only on your approval. Its scope and limits are its own — see code-repair.md.
What an engineering team needs to build a vertical of their own on EARL: an application that owns its business logic and data, remembers what it learns, reasons over evidence it was given, and acts under a policy it can read — with a signed record of every call. Start with a working application, then adapt its data and workflow to your domain: the package ships three reference applications (procurement review, claims triage, journal-entry review) whose domain files, SDK code and expected outcomes are measured; copy the one nearest your vertical and change its nouns, rules and loop.
This is the guide to read once end to end; the reasoning and planning guide shows the cognitive operations composed, the API walkthrough has every call, authoring a domain has every contract, and deploying applications says what runs where.
A vertical application: your service, holding the application's key, that
submit what the application learns at run time; the
runtime keeps it across calls and restarts; beliefs reads it back;query derives relations, prove closes chains, evaluate
checks a claim against the evidence with the pair, predicate, polarity and
source clause named;plan orders work, observe snapshots the state;ALLOW,
REVIEW or DENY with every matching rule named; advisory unless a
deployed adjudicator or a gateway is the only path to the action.Evaluating claims is one use of the platform, and the deployable frozen adjudicator is one artifact of it (deploying an adjudicator). The stateful capabilities run on the persistent runtime behind the platform API; see deploying applications for exactly what is hosted where.
EARL is not a language model, a search index, or a rules engine. Treat
VERIFIED as "consistent with the corpus, on the asserted pair", never as
"true"; the register states per capability what is qualified.
| action | role |
|---|---|
| install EARL engine releases, approve foundational KB packages, run engine example checks, create organizations and accounts | super_admin (Jou Labs during the POC) |
| create domains, upload corpus, author or generate vocab and curriculum, school, train, test, write policy, freeze, deploy, adjudicate | user (your engineers) |
everything a user can do, plus the read-only auditor surfaces (an auditor must be able to exercise what it audits) |
auditor |
Your accounts live in your organization. You see your organization's domains, packages, deployments and evidence and nothing else; another tenant's ids answer 404. Engines and foundational KBs are platform-level and shared. An engine and an approved foundational KB are installed before you arrive, so you begin at the domain step.
Before any corpus work, write the claims your vertical must answer, with the verdict each one should get and the disposition your business needs. Ten to twenty is enough for a POC. Include:
VERIFIED → ALLOW)CONTESTED → DENY)UNCERTAIN → REVIEW)UNCERTAIN from the engine and, once you
write the rule in step 6, DENY from the policy)HALLUCINATED → DENY)This list is your acceptance test. Every later step is judged against it, and it is what an engine example check will run for your domain when we bundle it.
Upload .md, .txt, .html, .pdf, .docx or .csv. Anything that is
not markdown is converted to markdown at upload; the original is kept beside
it and the record says which converter ran. Conversion preserves what is
there — headings, paragraphs, lists, tables — and adds nothing. A scanned PDF
has no text layer and is refused.
The extractor forms a belief about two entities only when the vocabulary
already links them (step 2) and the two mentions sit in one clause — the
rule since engine v0.66.0-rc6, and the one this package ships. A line break or
a table cell ends a clause. authoring-domains.md is the authority on this;
read it before designing a corpus.
Historical, no longer in force: engine v0.65.0 used a 25 entity mention window across sentences instead, which paired entities in adjacent sentences and gave them the verb found near them. On that engine a run of short list-style sentences ("The gold policy covers fire. The gold policy covers flood.") could produce beliefs the corpus never stated, such as fire covers flood, and those verify. Write for that, whatever the file type:
Atlas and the corpus says
Atlas EU, declare an alias (step 2); nothing guesses.settles used consistently teaches one
relation; four synonyms teach four weak ones.Keep the long-form prose too; the plain-fact sentences are the part the adjudicator grounds on, the prose is what makes the domain legible to a reviewer.
The vocabulary decides which words are concepts and which pairs may ever be asserted. Read authoring a domain § Assert the pairs your claims are about before writing it. In short:
acquirers, schemes, …) with its anchor first;anchor_chain() linking the type bands into one connected region;ALIASES for every long form the corpus uses;Save it with PUT /tuning-runs/{id}/vocab-draft (in the builder: Author
manually opens the editor with a starter template, Import draft takes
your .py file); the response carries the validator's verdict on the
contract. Fix errors there, not hours later in a build. No external model is
involved unless you choose Generate; if generation is not configured on
your platform, Step 1 says so and the two authoring routes are what you use.
A DAG of lessons. Each lesson teaches propositions (short subject-verb-object
sentences over grounded tokens) and is assessed on (a, b) pairs the engine
must connect unaided. Order lessons bottom-up: parts and names first, relations
after. Put the named-entity facts in their own lesson (the example's PR.5) so
their assessments show you directly whether those pairs took hold.
A failed lesson stalls its dependents. That is the intended behaviour: it names the gap. Fix the corpus or vocabulary the gap points at, not the assessment. As with the vocabulary: Author manually or Import curriculum (.json) in Step 2, then edit in Step 3; generation is optional.
school_summary: every lesson should be mastered; an
unmastered lesson lists its unreachable assessments. From rc24 an assessment
passes only on a fact the brain holds between the two concepts (see
authoring-domains), so a gap means the proposition was
not learned as written. Rewrite the sentence; don't loosen the assessment.GET /domains/{id}/kg/beliefs?query=<entity>. Every pair your claims need
must appear as a three-word proposition (atlas settles eur). If a pair is
missing, no claim about it can verify; go back to steps 1–2.claim | EXPECTED_VERDICT | EXPECTED_DISPOSITION (over the API:
expectations on POST /domains/{id}/adjudication-runs). The result
reports expected versus actual per claim and whether the whole list
matched, so the acceptance test is run by the platform, not by eye. Read
the fields, not just the label:| field | what it tells you |
|---|---|
verdict |
the outcome |
endpoints, endpoints_asserted |
the pair the verdict is about, and whether it is the pair the claim asserts rather than an easier pair in the sentence |
grounding |
path strength 0–1 |
contradictions |
contradiction edges crossed: corpus evidence against the claim |
demotion_reason |
why a verdict was lowered: relation_unasserted (grounded, but no belief asserts this pair), relation_unrecognized (the verb is not a relation the brain learned), negated_claim_grounds_only_affirmative_evidence (a denial with no denying evidence) |
policy.disposition, policy.matched_rules |
once you have a policy (step 6): the disposition and the rules that raised it, with their reasons |
Iterate until your list is green. Test runs write "Verifier claim" entries into the working belief store; they never count as asserted pairs, and the frozen package is built from the trained checkpoint. Every test run is recorded as a signed evidence package (step 7); download it from the result modal if you want to keep the run.
Once the belief store reads right, your application can answer plain-English
questions over it: wf.ask("What does a loan require?"). Every answer names
the facts it rests on (taught or derived), how the question was read, and a
faithfulness check; unfamiliar verbs are clarified, and writing, advice and
authorization requests are declined. Try questions on the domain page's Ask
tab while you author, and read natural-language-questions
before putting a question box in front of users.
POST /tuning-runs/{id}/freeze-domain produces an immutable, portable package
(grounded KB, trained checkpoint, an earl-verify binary for the container
platform, a small FastAPI server, your policy and its evaluator, an identity
record, Dockerfile, Compose file, curl examples, manifest) with a codename, a
hash, and a substrate identity: one sha256 naming exactly what the package
runs — the engine binary, the KB, the checkpoint, the policy. It is on the
package, on every deployment built from it, in the container's GET /identity
and every reply, and in every evidence package. "What you deployed is what you
tested" is a string comparison, not a belief.
POST /deployments builds and starts it under Compose on the platform host;
poll until running. Then call it — through the platform, under your
normal bearer token (the "Test a claim through the platform" card on the
deployment page does exactly this):
POST /api/deployments/{id}/verify {"candidate_text": "Atlas settles eur currency"}
GET /api/deployments/{id}/health
The platform forwards the request to the container and returns the
container's own reply unchanged under result — a pass-through, not a
re-adjudication — together with a signed evidence package for the exchange.
A non-running deployment returns 409; a container that does not answer
returns 502, never a fabricated verdict. If the container's stated identity
differs from the platform's record, substrate_identity_mismatch is true
and the reply says so.
Inside the platform host the container also answers directly on its runtime
port (curl-examples/ in the package), and the frozen archive is downloadable
and runs anywhere Docker runs, with no platform present — which is how a
vertical leaves the POC. Standalone, it answers with its identity but does
not sign; signing is the platform's.
The adjudicator will not invent a denial from a closed-world list, and on
current engines it will not verify a pair the corpus never asserts either. So
a claim like "Northstar supports the amex scheme", against a corpus that says
Northstar takes Visa and Mastercard only, comes back UNCERTAIN with
relation_unasserted — honest, and not a denial.
If your vertical must say no to that, write it as a rule in the domain's
policy (PUT /domains/{id}/policy, or the "Post-verdict policy" card):
{"format": "earl-domain-policy/1", "name": "payments v1",
"rules": [
{"id": "northstar-schemes", "type": "closed_world", "entity": "northstar",
"permitted": ["visa", "mastercard"], "action": "deny",
"reason": "Northstar supports Visa and Mastercard only"},
{"id": "asserted-pair", "type": "require_asserted_pair", "action": "review"},
{"id": "clean", "type": "max_contradictions", "max": 0, "action": "deny"}]}
The policy is deterministic, versioned, and small enough to read in one
sitting. Rules only ever raise a disposition (ALLOW → REVIEW → DENY),
so no rule can make a contested claim pass. It runs after the verdict on the
working checkpoint during your tests and inside the container for a frozen
package — the very same evaluator file — so what you tested is what deploys.
The full rule set is in evidence packages and policy § 3.
Every adjudication, in testing or through a deployment, yields an Ed25519-signed evidence package: the full result with every claim field, bound to the substrate identity, the engine, the KB and checkpoint fingerprints, the policy hash and the claims asked. List them per domain or deployment, fetch any by id, and verify one offline with nothing but the file and the platform's public-key fingerprint:
python verify_evidence_package.py evidence-<id>.json --pin-key <key_id>
Get the script at GET /api/evidence/verifier and the fingerprint at
GET /api/evidence/public-key, once, and keep the fingerprint with your trust
anchors: --pin-key is the check that proves a package came from this
platform and not merely from a consistent signer. Exit status 0 means every
check passed. What each check proves is in
evidence packages and policy § 2.1.
Replay any package (the "Replay and compare" button, or
POST /api/evidence-packages/{id}/replay) to re-adjudicate its claims on the
same source and get a signed comparison: identical, or a named list of what
changed. Together, a package's fingerprints, its verdict evidence with
expected-versus-actual, and its replay comparison are the export an
evaluator asked for.
The 2026-09-22 audit of engine v0.65.0 found false VERIFIED verdicts from correct text (cross-sentence pairing, undirected pair checks). The candidate engine (v0.66.0-rc26, and every release since rc6) repairs those classes and re-measures them: pairs form from the clause that states them, direction and predicate are checked, a denial contests the affirmative claim, and every verdict names its pair, predicate, polarity and source clause. The register (capability limitations) lists every finding with its status; read it before you rely on a verdict, and keep your acceptance list running with expected outcomes so a regression is counted, not noticed.
The register is authoritative. In short: reasoning, memory, planning,
observation, plain-English questions over your domain (language.ask),
authority checks and records are available through the SDK with stated limits
(evidence.evaluate is not qualified for unattended approval decisions;
language.ask answers only from the domain's facts and was measured on
lab-authored domains, not yet on a customer's; planning is stateless; episodic
recall is not offered). Governed language generation (language.realize)
and the developmental capabilities (self-modification, self-repair,
inherited development) are qualified research results with no developer
interface in this intermediate build; installed-release adoption is what an
application can invoke. The release gate for the developmental capabilities is
in the register's roadmap section.
running; a claim answered through
the platform with substrate_identity_mismatch: falseall_matched: true--pin-key, exit 0, and
replayed with identical: trueendpoints_asserted, demotion_reason) and the policy
reviewed by someone who did not write the corpusgenerate-vocab and generate-lessons, and both are optional: author
the drafts yourself and no external model is in the path at all.