# The Santiago Doctrine — Canonical English Edition

**Audience:** independent reviewer (Craig Bracken)
**Version:** Doctrine 1.0.0 · Canonicalization `deep-stable-sort-nfc-utf8-v2`
**Date:** July 31, 2026
**Status:** Enforced in code. Every claim below maps to a module, a gate, or a sealed artifact.

This is the complete judgment doctrine that TACTIK stamps on every simulation, every
debrief, and every sealed run. It has two halves:

- **Part A — The Judgment Lens.** How a run is *read*. Codified in
  `supabase/functions/_shared/doctrine.ts` (single source of truth; the ledger engine and
  the debrief engine both render projections from it — neither re-authors the prose).
- **Part B — The Evidentiary Discipline.** How a run is *proven*. Objective Lock, seal
  gates, canonicalization, signing, Bitcoin anchoring, and the fail-closed verdict states.

Part A makes the output non-generic. Part B makes it non-deniable. Neither is optional.

---

## PART A — THE JUDGMENT LENS (6 canonical sections)

### A1. MASTER GATE — `relational_intent` (read FIRST; governs everything below)

Classify the counterparty's underlying intent:

- **WIN-WIN** — both sides build long-term value. Margin/price concessions are **rational**;
  they protect a continuing relationship.
- **EXTRACTION** — one side leverages size/power to take advantage. "There is no future."
  Guard hard; give no cushion.

Judged from **tone, openness, and genuine search for negotiation** — not from words alone.
Every signal carries a **decisiveness** score 0..1: an explicit door-slam ("you will get
nothing you asked for") ≈ 1.0; a tepid pitch ≈ 0.2.

**Hard rule:** the model never *declares* extraction. It emits signal + decisiveness; the
engine runs the sequential threshold and owns the verdict. Never over-declare extraction on
a single soft signal. This gate sets the width of the negotiation cushion **before any math
matters**, and it sets the ceiling/floor of `success_probability` in the debrief.

### A2. PROBE-STALL — the canonical defensive-offensive posture

When intent is ambiguous or pressure is one-sided, the correct move is **neither exit nor
rigid hold**. It is probe-stall: a hard-but-not-extreme position + buy time ("let me review
my numbers") + a deliberate second meeting under that pretext + tactical indifference.

The debrief must flag when the operator **should** have used probe-stall and didn't — or
used it well. It surfaces as a `before_real_meeting` action, not as commentary.

### A3. SUBJECT AXIS — actor × org

Every read is anchored to **both** the person (who carries trust history, `actor_id`) and
the org/philosophy that modulates them (`org_context_id`). The same person in a different
org **is not the same subject**: trust transfers at a discount and must be re-tested against
the new org's incentives. This is what sharpens `next_challenge`.

### A4. THEATER vs BELIEF — `tactical_signal`

Separate what the counterparty **projected on purpose** (feigned indifference, stalling as
strategy, a worried face) from what they actually believe. A deliberately projected posture
is theater and **must not** change the read of their true position — and must not lower
internal confidence. Theater is recorded in a separate layer, named explicitly in the
debrief as a risk, and is barred from inflating Counterpart Receptivity.

### A5. GOVERNANCE — recurring rules of judgment

Surface a **rule of judgment** only when it recurs across subjects (e.g. "whoever decides
owns the consequence"; "consult broadly, then own the decision alone"). One-offs are
observations, not governance. Governance enters the forecast as a caveat.

### A6. MAGNITUDE DOCTRINE — how much, not just which way

- **Asymmetry.** Killing or weakening a well-supported read costs **more** evidence than
  forming one. A consolidated judgment does not collapse on a single soft signal. The engine
  enforces an asymmetric floor on confidence drops.
- **Aging is mechanical.** Time decay is engine-owned, never the model's to guess.
- **Decidability.** Every claim is a falsifiable statement grounded in a **verbatim quote**
  from the source. No invented beliefs.

### A7. Output requirement (every debrief, no exceptions)

Each debrief must explicitly state: (a) the `relational_intent` verdict + decisiveness +
the evidence quote behind it; (b) any theater detected; (c) the recommended posture
(win-win concession vs probe-stall vs guard); (d) what the operator missed; (e) next move +
next document. Match the source language. Analytically honest, never diplomatic.

---

## PART B — THE EVIDENTIARY DISCIPLINE

### B1. Objective Lock, sealed before turn 1

The operator's objective — target, floor, non-goals, red lines — is captured and sealed
**before the first turn exists** (`action: "seal_start"`). A simulation cannot start against
an incomplete lock: the seal gate returns HTTP **400 `seal_gate_violation`**. This is what
makes "we hit the objective" a measurement instead of a story told afterwards. It is also
what exposes the **consolation prize**: progress under the generic mode that is *not*
progress toward the declared target.

### B2. Substitution is computed, never classified

```
gap      = max(0, outcome@mode − outcome@target)
detected = gap > 0.15
```

The threshold **0.15 is frozen** and is never recalibrated from results. A model may
*explain* the computed signal; it may never reclassify it. With no declared target there is
nothing to substitute for, so `applicable = false` by construction.
(`_shared/irr-substitution.ts`)

### B3. Blind roles — Mandate Delegation

- **Principal** declares the objective and is **blind to live turns**.
- **Operator** negotiates and is **blind to the sealed objective**.
- **Observer** gets **no live turns at all** — sealed and debriefed views only.

Role bindings are sealed pre-turn-1, immutable by trigger, keyed by `run_id`. Blindness is
attested per session (`BLINDNESS_HELD` with `principal_live_reads: 0`); denied attempts are
logged verbatim. A `BLINDNESS_BROKEN` attestation forces the score to `undetermined`.

### B4. Two metrics, never interchangeable

- **DQ — Discipline Quotient:** the operator authored their own Objective Lock.
- **MF — Mandate Fidelity:** the principal authored the lock (delegated mode).

They are never averaged, never renamed into each other.

### B5. Fail-closed predicates (permanent doctrine)

Any boundary the predicate compiler cannot fully parse renders **`undetermined`** — never
clean, never a "human review" row that *reads* clean. Magnitude-aware parsing (units,
scale, sign) is mandatory; a red line stated in different units than the transcript is a
violation to detect, not noise to skip.

### B6. Verdict states are first-class (`_shared/verdict-state.ts`)

| State | Meaning |
|---|---|
| `measured` | measured end to end by the currently live scoring layer |
| `retired` | previously published, now **withdrawn**; the old number is preserved in `retired_value` and **never re-issued** |
| `undetermined` | could not be resolved — fail-closed, never rendered as clean |
| `unverifiable` | structurally impossible to verify (e.g. seal predates preimage persistence) |

`null` and blank are prohibited: a blank reads as "clean" to a human. **Correction by
addition, never by mutation.** Precedent: run `3050d24d`'s DQ 100/100 was *retired*, not
recalculated — published as a correction notice, superseded bundle, and re-anchored.

### B7. Canonicalization v2, versioned and published

`deep-stable-sort-nfc-utf8-v2` — deep stable key sort, NFC normalization, UTF-8 bytes. The
label is versioned inside the sealed payload. Reference implementations are published in
**both Python and JavaScript** so a third party reproduces the hash without our code:
`/downloads/tactik-canonicalize-v2.py`, `/downloads/tactik-canonicalize-v2.mjs`.

### B8. The preimage is persisted, not just the hash

A hash alone is unfalsifiable theater. Every sealed run persists the **preimage** next to
the digest, so any reviewer can recompute the hash from the bytes. Seals that predate this
rule are labeled `unverifiable` — permanently, by name.

### B9. Provenance lives inside the seal

Room, channel, and origin are **server-derived** and sit inside the sealed payload, so they
are covered by the anchored SHA-256. Provenance is never accepted from the request body.
The `declared_adjacency` verbatim declaration is likewise inside the preimage: the hash
depends on what was actually declared.

### B10. Authorship signature + independent time custody

- **ed25519** detached signature over the preimage; public key published
  (`/downloads/tactik-authority.pub`).
- **OpenTimestamps → Bitcoin.** Existence in time is attested by the chain, not by us.
  Run `3050d24d` is confirmed in blocks **960198 / 960199**; the July 28 preimage in
  **959991 / 959992**.
- **No retroactive signing.** Authorship signatures exist only for seals dated
  **≥ July 28, 2026**. Older seals are never backfilled — the gap is disclosed, not closed.

### B11. Terminology discipline

- **"Objective Lock sealed"** = hash only (runs ≥ July 21).
- **"Seal Certificate"** = full record including preimage (runs ≥ July 25 only).

We do not stretch either term backwards over runs that don't carry the artifact.

### B12. Realism guards inside the engine

- **Anachronism Guard** — a simulated actor cannot use an instrument that didn't exist in
  the scenario's frame; it emits `[novel_instrument_required]` instead of inventing one.
- **Political Cost Filter** — blocks *deus-ex-machina* commitments a real counterparty
  could not survive politically (live in `simulate-response`, step 7.7).
- **Citation enforcement / factual grounding** — parity requirement across every
  simulation engine, not one privileged path.
- **Institutional voice rule** — zero first-person pronouns when speaking as an
  institution; resistance by default.
- **Log integrity** — exactly one entry per turn, no encoding garbage, no `[object Object]`
  in any appendix. Enforced by canonicalizer + safe serialization.

### B13. Access discipline

Strict RLS on every table. Append-only `access_audit` (immutable by trigger, founder-read
only) records who touched which sensitive surface. Room catalogs are allow-listed so no
private case can appear across tenants. Guest surfaces are rate-limited per IP.

---

## What this doctrine is honest about

Radical honesty is part of the doctrine, so the open items are stated, not buried:

1. **Predictive validity is the load-bearing assumption.** Everything in Part B is
   credibility infrastructure for a claim Part B does not itself prove: that a counterparty
   simulated from institutional DNA behaves close enough to the real one to change the real
   outcome. The answer is a prediction ledger scored **after** the real event, with hit rate
   *and* failures published. That is the work in front of us, not behind us.
2. **Scores are scarce on purpose.** Most historical runs read `unverifiable` or
   `undetermined` because they were not measured end to end by the live layer. A thin column
   of real numbers is worth more than a full column of generous ones.
3. **One retired verdict already exists in public.** We published the withdrawal instead of
   quietly recomputing it. That precedent is the doctrine working.

---

## Verify it yourself

- Public verification surface (renders every artifact inline — no downloads needed):
  **`/verify/3050d24d`**
- Canonicalizer (Python / JS), authority public key, preimage, detached signature,
  OpenTimestamps receipt, verification bundle v2, correction notice: all under
  **`/downloads/`**, mirrored as `.txt` for in-app browsers that block file downloads.

Recompute the hash from the preimage with our canonicalizer or your own implementation of
`deep-stable-sort-nfc-utf8-v2`. Verify the signature against the published key. Verify the
timestamp against Bitcoin. If any of the three fails, the doctrine says the run does not
count — and so do we.

---

*Santiago Maspons · TACTIK · Doctrine 1.0.0 · Canonical source:
`supabase/functions/_shared/doctrine.ts`*
