<!-- COUPLING: the verb guidance, boundary rule, and residue taxonomy below
     must match app/translator.py's `_system_prompt()` exactly (§9 invariant
     #2 — the vocabulary in the translator prompt, this primer, and the live
     interpreter surface must be the same 61-word set). If the translator's
     prompt changes, re-verify this document against it. -->

# The Translate Mapping Guide

**How a policy sentence becomes a `.limn` line — and what happens when it can't.**

---

## Why Translate exists

You have a document. A vendor policy, an SOP, a compliance spec — something with rules buried in prose. You don't want to hand-write Liminate. You want to arrive with what you have and leave with something you can read and check.

That's Translate. Paste a document, upload a PDF, or point at a URL. Translate drafts a `.limn` contract from it, plus a fidelity report: a line-by-line account of what was expressed, what had to be adjusted, and what genuinely couldn't fit.

The fidelity report is not a disclaimer at the bottom of the page. It is the product. A tool that quietly turns every sentence into a checkbox and calls it done is giving you the illusion of comprehension — it looks complete and isn't. Translate would rather tell you the truth about a rule it couldn't express than fake one that reads fine and means nothing. Every claim in the report is checked against the real Liminate interpreter, not just the model's word for it — a draft line the interpreter rejects is marked as a translator mistake, not a language limitation.

---

## The three verbs and when to use them

Liminate has three deontic verbs. The source rule's own legal shape tells you which one applies.

**`forbid` — a prohibition.** The source says something must not happen.

> Source: *"Wikipedia is not an acceptable primary source."*
> `forbid primary-source is equal to "Wikipedia" because "Wikipedia is not an acceptable primary source"`

**`require` — a mandatory gate.** The source says something must be true, unconditionally, for the thing being checked to proceed.

> Source: *"Every invoice must match a purchase order."*
> `require purchase-order is equal to "matched" because "every invoice must match a PO"`

**`permit` — a recorded allowance.** The source describes something that's allowed, not demanded — worth recording, not worth blocking on.

> Source: *"AI-assisted drafting is permitted when disclosed."*
> `permit ai-assisted is equal to "true" because "AI drafting is permitted when disclosed"`

### Conditional obligations: don't reach for "when"

The trickiest shape is a *triggered* obligation — "if X, then Y is required." Liminate has no way to gate a `require`/`forbid`/`permit` line with a second inline condition. (`when` is reserved for a different construct entirely — a reactive handler, not an inline connective — so a line like `require manager-approval when amount is above 4999` isn't valid syntax, even though it reads naturally in English.)

Instead, reframe the trigger as a prohibition with a named carve-out, using `define` to name the triggering condition:

> Source: *"Invoices of at least $5000 require manager approval."*
> ```
> define large: is above 4999
> forbid amount is large unless manager-approval is equal to "yes" because "large invoices need manager sign-off"
> ```

Read it out loud: "It's forbidden for a large invoice to go through — *unless* manager approval was given." Same meaning as "large invoices require manager approval," expressed in a shape the interpreter can run.

---

## Thresholds and the boundary rule

`is above` and `is below` are strict — greater-than and less-than, not greater-than-or-equal. There is no `is above or equal to`.

Plenty of real policies use inclusive language: "at least 5000," "no more than 30," "≥," "≤." When Translate hits one of these, it converts the threshold rather than silently rounding it away:

- "at least N" (`>= N`) becomes `is above (N-1)`
- "no more than N" (`<= N`) becomes `is below (N+1)`

So "invoices of **at least** $5000" becomes `is above 4999` — every dollar from 4999 upward now counts as large, which correctly includes exactly 5000.

This conversion always shows up in the fidelity report as **boundary converted**, with the exact shift stated. It is never folded silently into "expressed." Why it matters: an off-by-one here changes who the rule actually catches — an invoice of exactly $5000 either needs manager approval or doesn't, and that's not a detail Translate will bury in a summary.

---

## What Translate can't express (yet), and why that's the point

Some provisions don't fit the bounded vocabulary. When that happens, the line has no `.limn` counterpart at all — it's marked **residue**, and the report says exactly why, using one of these reasons:

- **Discretionary predicate** — soft, subjective language: "good faith," "reasonable," "as appropriate." There's no threshold to check.
- **Relative date arithmetic** — "within 180 days," "3 months before renewal." Liminate has no relative-date math.
- **String operation** — the rule needs to manipulate text, not compare it.
- **Cross-reference** — the rule just points at another section or document.
- **Quantified relation** — "each X has exactly one Y." Liminate checks single values, not relations across a set.
- **Counterfactual** — "the amount that would have been due." There's no hypothetical to compute.
- **Logic not expressible** — the general case: the only way to render this rule is to fake it (see below), so Translate won't.

That last one is the important one. A residue line is never a shortcut for "this was hard, so we skipped it." It's a boundary: translating the *logic* of a rule and translating its *conclusion* are different things. A field named `invoice-submitted-on-time` that's just a bare boolean, with no real condition behind it, looks like a translated rule and is actually a guess wearing a rule's clothes. Translate would rather show you residue than show you that.

Residue isn't necessarily permanent, either. If a domain pack is loaded for your translation, a provision that pack can express shows up as **pack-expressible** instead, naming the pack — still not in the draft, but not a dead end.

---

## From draft to decision

A Translate draft is a rule *template* — like the examples in Liminate's own Agreements product, it names conditions (`amount`, `manager-approval`, `purchase-order`) without yet binding them to real values. That's normal. The draft is meant to be read and edited, then handed off to wherever the values will come from:

- **Save to Agreements** carries the draft into Liminate Agreements, where you (or whoever submits the evidence) can run it against real values and get a decision — passed, blocked, or needs review.
- **Save to Receipts** stores the draft as a Receipt you can share or revisit later.

Before either hand-off, use **Re-validate** as many times as you like — it's free, it never calls the model again, and it always reflects exactly what the interpreter thinks of your current edits. Only a fresh translation from a new source spends another credit.

---

*Translate by Liminate — liminate.dev/translate*
*The interpreter is the trust root. The fidelity report just tells you the truth about it.*
