An agent researches, drafts and scores. A human commits.
- BUILT
- October 2026 · Cloudflare Workers + D1
- AGENTS MAY
- read · draft · score
- AGENTS NEVER
- send · scrape · invent
- LEDGER
- SHA-256 chain over canonical JSON
The rule
The repository opens with one rule: anything with commercial, relational or legal meaning goes through a human action that the ledger records. The rest follows from it.
AN AGENT MAY
- Read what its principal can read.
- Run a research job within a budget of ten requests.
- Record documents, claims and contradictions, each with an honest status.
- Open up to six reported URLs and check that the quote is on the page.
- Write a thesis, always marked as a hypothesis, and a draft, always marked as a draft.
A PERSON DECIDES
- Approving a draft, and anything a client will see.
- Moving an opportunity past research.
- Adding a person to the network.
- Recording a contact, a signed agreement or a legal review.
- Any commission term.
NO AGENT EVER
- Sends a message. This build has no send path at all.
- Scrapes behind a login, or bypasses robots.txt or bot protection. LinkedIn is opened by hand.
- Invents a figure, a guest, a relationship, a meeting, a result or a contract.
- Passes client data to an outside model without the owner's approval. The model adapter is off by default.
Two layers, one direction
The evidence layer follows IRA, an evidence grammar I wrote before MANDATE: public sources only; every claim traces to a source; a lead never confirms; a contradiction is registered as a seam and stays open until a person resolves it. Above it, the derivative layer may recommend. Its output carries the label derivative and never flows back down as evidence.
DERIVATIVE LAYER · MAY RECOMMEND
a thesis is always a hypothesis
READS FROM · NEVER WRITES TO
EVIDENCE LAYER · IRA LAWS
strength is computed, never typed in
Every human act in either layer commits in the same batch as its audit event. Events chain by SHA-256 over canonical JSON, and a route recomputes the chain on demand.
Claim status and strength
Research from outside, from a person or from another AI, enters as reported. MANDATE then opens each URL itself and looks for the quote. Only facts found at their source reach the client's hand-off or count as proof that two people are linked.
568 const host = domainOf(f.url);569 if (!host || /(^|\.)linkedin\.com$/.test(host)) { results.push({ fact: f.id, status: f.status, note: 'LinkedIn is opened by hand.' }); continue; }570 if (!f.quote || f.quote.trim().length < 12) { results.push({ fact: f.id, status: f.status, note: 'No quote to look for.' }); continue; }571 try {572 const got = await fetchPublic(ctx.fetch, f.url!, { ua, accept: 'text/html,application/xhtml+xml,text/plain', agentToken: 'MANDATE-research', maxHops: 3, robotsCache });573 if (!got.ok) { mark('unreachable', got.reason); continue; }574 const raw = new TextDecoder('utf-8').decode(got.body);575 const page = /html/i.test(got.contentType) ? extractPage(raw) : { text: raw, title: null as string | null };576 if (!flat(page.text).includes(flat(f.quote))) { mark('not_found', 'The quote is not on the page.'); continue; }
STRENGTH, COMPUTED
| STRENGTH | RULE |
|---|---|
| HIGH | A primary source, corroborated by an independent source that is not a lead |
| MEDIUM | A single primary source, or two independent semi-primary sources |
| LOW | Leads only: press, directories, marketing pages. Press discovers; it never confirms. |
39const independent = (a: EvidenceSource, b: EvidenceSource) =>40 a.id !== b.id &&41 a.publisher.trim().toLowerCase() !== b.publisher.trim().toLowerCase() &&42 (recordUrl(a.uri) === null || recordUrl(a.uri) !== recordUrl(b.uri));4344export function sourceRank(list: EvidenceSource[]): Strength {45 const primary = list.filter(s => rankClass(s.class) === 'primary');46 if (primary.some(p => list.some(s => rankClass(s.class) !== 'lead' && independent(p, s)))) return 'High';47 const semi = list.filter(s => rankClass(s.class) === 'semi');48 if (primary.length || semi.some(a => semi.some(b => independent(a, b)))) return 'Medium';49 return 'Low';
One case, end to end
FROM THE TEST SUITE · NAMES INVENTED
Another AI's research answer is pasted into MANDATE. It names one person at a fictional agency and gives four facts, each with a URL and the quote that supports it. The operator asks MANDATE to check them.
| THE RESEARCHER WROTE | URL | WHAT MANDATE DID | STATUS |
|---|---|---|---|
| “Its site says it flies the Riviera.” | exemple-agency.example/about | Opened the page, found “Fly the Riviera”, stored the page as a primary source with its SHA-256 | FOUND_AT_SOURCE |
| “A press piece quotes her.” | press.example/a | Opened the page. The quote is absent | NOT_FOUND |
| “Her profile.” | linkedin.com/in/… | Never fetched. A person opens it | REPORTED |
| “A private page.” | press.example/private/x | robots.txt closes the path, so MANDATE stays out | UNREACHABLE |
One correction is easy to miss. The researcher wrote that the agency flies the Riviera. The note MANDATE writes quotes the page, “Fly the Riviera”, and leaves the paraphrase out. The test asserts both, and a second test checks that a redirect cannot carry the fetch into a closed path.
THREE SHAPES MANDATE KEEPS OPEN · INVENTED EXAMPLES
- 01
A contradiction: active in the register, closed in the press.
Company A is active in the national register. A trade article reports that it closed its French arm. Both are sourced. The register is primary and the article is a lead, so the article cannot overrule the register, and the register cannot silence the article. MANDATE opens a seam that reads: “An active register entry and a reported closure cannot both describe a trading company. Confirm before any approach.” It closes when a person resolves it. - 02
A true record, a false fit.
An activity code says what a company filed as, sometimes decades ago. Company B is filed under a luxury trade and builds industrial equipment today. MANDATE keeps the code as a sourced claim, treats it as a lead for fit, and leaves the fit to a person. - 03
An absence: what the system cannot know.
Some jurisdictions publish no open company register. When the most relevant companies sit there, MANDATE records an absence: it cannot establish them from a primary source, and it says so. Their identity has to come from that register or from the companies themselves.
Limits
- 01
A match proves the words are on the page.
Context stays with the reader. A quote can sit on a page and mean something else there. - 02
Strength rests on the class of each source.
A primary source can be wrong. The system knows only what disagrees with it. - 03
A person's decision is as good as the person.
The ledger records who decided, and when. It cannot record how carefully. - 04
The chain is unsigned.
Ed25519 signatures on the chain come next, as in the Seal.
SOURCES
- mandate / AGENTS.md — how an agent works inside MANDATE
- mandate / src/core/evidence.ts — strength, ported from the Ramification workbench (IRA)
- mandate / src/worker/routes/network.ts — the quote check, lines 568–576
- mandate / tests/network.test.ts — the case in section 04