The compile path takes free-form ROE prose and produces a Zod-typed, paragraph-traced, Ed25519-signed MissionArtifact. A language model reads the prose; an allowlist filters what the model is allowed to authorize; an operator validates the parsed result against the source; the commander signs the canonical-JSON of the final artifact. The signed binary is then the only artifact that crosses into execute time. Everything that fires at execute time can be traced to a paragraph in this binary, and from there to the prose the commander signed.
ROE is not one document. In the field it is a stack: an OPORD with engagement annexes, an ROE card distilled for the operator, weapons-control schedules, coalition coordination overlays, and an issuing authority's signed cover. Sometimes a hand-marked map. Sometimes a photo of a card on a clipboard. The compile path has to accept all of these and produce one thing.
The compile path expects prose paragraphs, numbered, in conventional ROE structure: Paragraph 1 mission window, Paragraph 2 geographic boundaries, Paragraph 3 positive identification criteria, Paragraph 4 engagement authority by class and zone, Paragraph 6 coalition coordination, Paragraph 7 escalation, Paragraph 8 prohibitions. The numbering matches how staff already write ROE, and every rule the artifact emits cites back to its source paragraph.
The intake surface accepts text (pasted ROE), image (photo of a card or briefing slide), and structured uploads (an existing JSON ROE from a prior mission). Image inputs run through gpt-4o vision for OCR and structure extraction; text inputs go directly to gpt-4o-mini. The intake layer adds no semantics — it normalizes the source into a single “ROE document” shape that the frontend can consume.
@lincoln/interpretation contains the only language-model code in the compile path. The model is given the source ROE plus a strict schema and asked to produce candidate values for each field of the artifact: PID criteria, engagement authority entries, geographic boundary polygons, temporal conditions, escalation procedures, prohibited actions. Each candidate carries a paragraph_ref sourced from the model's reading of the prose.
The model performs translation: it interprets what the prose says and produces candidate fields. The downstream gates verify those candidates. The cache layer (cache.ts) memoizes the model's output across runs of the same input.
The model's output runs straight into @lincoln/interpretation/allowlist, a hardcoded gate that rejects anything outside a tight grammar:
crew, battle_captain, higher_echelon. autonomous is not a value the gate will accept; the model can write it but the gate refuses it.higher_echelon by default — they do not silently authorize a crew shot.HOLD, TIGHT, FREE.PROHIB-NFA, PROHIB-NO-PID, etc.). A rule the engine cannot evaluate is not a rule the artifact can carry.BLOCK. The gate rejects an artifact that would default to AUTO or EXECUTE if a human is unreachable.
The parsed-and-allowlisted artifact is rendered alongside the source ROE for a commander or staff officer to review. Each field shows its paragraph_ref; clicking the field highlights the corresponding span in the source prose. The operator can edit any field — this is the moment when human judgment about the meaning of the prose lives. The model produced a hypothesis; the staff officer ratifies, corrects, or rejects it.
The validation step is also where pre-mission staffing happens. JAG can review for legal compliance. The S3 can verify zone polygons against the operations overlay. The fires officer can confirm engagement authority allocations. None of these reviewers is being asked to read the model's prompt or its raw output — they are looking at a typed artifact and the source prose, side by side, with the model's role no more visible than a syntax highlighter's.
On approve, the artifact is canonicalized — keys lex-sorted recursively, no NaN / Infinity, no fractional whitespace — hashed (SHA-256), and signed with the issuing-node Ed25519 private key. The signature is bound to the commander's identity through the issuing authority's certificate; in production this would chain to an organizational PKI. The signed artifact is the only thing that crosses into execute time.
What ends up on disk after a successful compile (excerpted from the demo's 06_signed_mission_artifact.json):
{
"artifact_id": "ALPHA-2026-0502-ARTIFACT-001",
"mission_id": "ALPHA-2026-0502",
"schema_version": "lincoln.mission_artifact.v1",
"effective": { "start_iso": "...", "end_iso": "..." },
"issuing_authority": {
"rank": "LTC", "name": "J. Hayes",
"unit": "6-56 ADA", "callsign": "WARHORSE 6"
},
"parsed_roe": {
"positive_identification_criteria": {
"required_confirmations": 2,
"criteria": [
{ "id": "PID-RF", "source": "rf", "confidence_floor": 0.80, "paragraph_ref": "Para 3.a" },
{ "id": "PID-EOIR", "source": "eo_ir", "confidence_floor": 0.75, "paragraph_ref": "Para 3.b" },
{ "id": "PID-ORIGIN", "source": "intelligence", "confidence_floor": 0.70, "paragraph_ref": "Para 3.c" },
{ "id": "PID-PROFILE","source": "radar", "confidence_floor": 0.75, "paragraph_ref": "Para 3.d" }
]
},
"engagement_authority": [
{ "threat_class": "group_1", "zone_id": "ZONE-EZ-EAST",
"authority_level": "crew", "weapons_control_status": "tight",
"paragraph_ref": "Para 4.a" },
{ "threat_class": "group_2", "zone_id": "ZONE-EZ-EAST",
"authority_level": "battle_captain", "weapons_control_status": "tight",
"paragraph_ref": "Para 4.b" },
{ "threat_class": "unknown",
"authority_level": "higher_echelon", "weapons_control_status": "tight",
"paragraph_ref": "Para 4.c" }
],
"geographic_boundaries": [
{ "id": "ZONE-EZ-EAST", "kind": "engagement_zone", "polygon_wgs84": [...], "paragraph_ref": "Para 2" },
{ "id": "ZONE-CC-CORRIDOR", "kind": "coalition_corridor", "polygon_wgs84": [...], "paragraph_ref": "Para 6" },
{ "id": "ZONE-NFA-FOB", "kind": "no_fire_area", "polygon_wgs84": [...], "paragraph_ref": "Para 8.a" }
],
"escalation_procedures": [
{ "trigger": "threat_class == group_2 AND no_battle_captain_approval",
"escalate_to": "battle_captain", "timeout_sec": 20,
"default_on_timeout": "BLOCK", "paragraph_ref": "Para 4.b / Para 7.a" },
{ "trigger": "intercept_geometry_intersects_corridor AND coalition_corridor_active",
"escalate_to": "higher_echelon", "timeout_sec": 30,
"default_on_timeout": "BLOCK", "paragraph_ref": "Para 4.c / Para 6.a" }
],
"prohibited_actions": [
{ "id": "PROHIB-CORRIDOR", "paragraph_ref": "Para 8.a", "description": "..." },
{ "id": "PROHIB-OUTSIDE-EZ", "paragraph_ref": "Para 8.b", "description": "..." },
{ "id": "PROHIB-WCS-HOLD", "paragraph_ref": "Para 8.c", "description": "..." },
{ "id": "PROHIB-NO-PID", "paragraph_ref": "Para 8.d", "description": "..." }
]
},
"node_signature": "ed25519:..."
}
Every rule carries a paragraph_ref. The artifact functions as its own debug info: a decision firing at execute time cites the source paragraph through the artifact, and the staff officer locates that paragraph in the original prose.
Rule IDs (PROHIB-NFA, PROHIB-NO-PID, etc.) name evaluators in @lincoln/policy-engine/hard_blocks.ts. The artifact's prohibition list is a parameter set for those evaluators; the rule ID binds the artifact's description to the engine's evaluator. The allowlist refuses any rule ID the engine cannot evaluate.
| Property | Frontend (LM) | Allowlist gate | Validation UI | Signing |
|---|---|---|---|---|
| Deterministic | No | Yes | Yes (UI) | Yes |
| Auditable input | Source ROE | Model output | Allowlisted artifact | Validated artifact |
| Auditable output | Candidate fields | Accepted-or-refused | Edits + approval | Signature |
| Human in path | No | No | Yes (operator) | Yes (commander) |
| Failure mode | Wrong field values | Refuses output | Operator catches | Boot rejects mismatch |
| Recoverable? | Yes — re-run | Yes — re-prompt | Yes — edit | Yes — re-sign |
Each LM error has a corresponding downstream gate. autonomous as an authority level is refused by the allowlist. A polygon with the wrong altitude band is caught by the operator comparing parsed result to source prose. An unknown rule ID is refused by the allowlist. Errors that reach the commander are caught at signature; an unsigned or mis-signed artifact does not boot.
The commander does not see prompts. The commander does not see model outputs. The commander sees:
The mental model is “sign the contract” — the commander reviews the contract, edits anything they disagree with, signs when satisfied. The model is plumbing they don't have to think about, and the allowlist gate is a backstop they don't have to know exists. What they sign is what executes; what executes is what the audit chain proves; what the audit chain proves is what they reviewed.
Three constraints to note.