# Evolution 4 — forensic baseline (2026-10-04, before any E4 code)

The owner's prompt was "Causal Autonomous Governance & Decision Intelligence Fabric". The 30-day directive
(docs/strategy/FOUR-PRODUCT-30-DAY-PLAN.md) limits work to RIG, OpenShell, MemVault and SCITT, so the baseline covers
the contract fabric over those four products. It does not cover all 20 Frontier products.

## What already existed for the E4 prompt's sections (checked in source, not inferred from names)

| E4 section | Existing implementation | Status |
|---|---|---|
| §11 decision integrity receipt | `cain_governance_contract.execution_binding` (16 digests: identity, authority, capability, policy, risk, action, resource, environment, model/runtime, tenant, epoch, nonce, …) is signed into every decision receipt and checked again at the E8 commit point (E2) | IMPLEMENTED, ENFORCED, TESTED, DEPLOYED |
| §12 decision basis graph | `cain_contract_graph.ContractGraph`: `why_authorized`, `which_boundary`, `what_changed`, `time_machine`; route `/fabric/contracts/explain/{receipt}` (E2) | IMPLEMENTED, TESTED, DEPLOYED |
| §6 decision counterfactuals | `cain_contract_forecast.counterfactuals`: SIMULATED variants (as requested / not executed / stronger policy / compromised dependency …) (E3) | IMPLEMENTED, TESTED, DEPLOYED; outcomes unobservable by definition |
| §7 causal blast radius | E3 RIG-reachability blast radius (KNOWN/LIKELY/POSSIBLE/UNKNOWN), delegated children | IMPLEMENTED, TESTED, DEPLOYED; reachability, not causal propagation |
| §13 decision challenger | `cain_governance_learning.challenge` (E5 WIP, uncommitted at baseline time) | IMPLEMENTED in WIP, TESTED, NOT DEPLOYED |
| §19/§18 model substitution and versioning | E3 `model_set_digest` bound into every E8 binding; a model change invalidates in-flight forecasts | IMPLEMENTED, ENFORCED, TESTED, DEPLOYED |
| §29 decision memory | signed receipts + replay records + E3 prediction corpus (hash-chained) | IMPLEMENTED, TESTED, DEPLOYED |
| **§30 decision regression, §31 governance differential testing** | **nothing**: `replay()` re-runs ONE receipt under the SAME rules; no replay under different rules, no N vs N+1 comparison, no report | **ABSENT** (this is what E4 built) |
| §3–5 causal graph / hypothesis / intervention engines | `cain_causal_forensics.py` (177 lines) and `cain_causal_world_model_12.py` (312 lines) exist from earlier "CAIN 12" work. They are used by `cain_trust_kernel*.py` and the `_12` campaign modules, NOT by the contract fabric. Not audited in this session | UNKNOWN for the 4 products |
| §8–10 feedback loops, runaway autonomy, autonomy trajectory | E3 autonomy budget (atomic, per agent) only | PARTIAL (budget); trajectory/escalation ABSENT |
| §35 distributed causal state | single host; blue+green share tenant SQLite; quorum in-process only (E2 limitation) | NOT DISTRIBUTED |

## Why E4 built the differential and not a causal graph

A causal graph over four products with no observed intervention data would be guesswork labelled "causal". The
differential is different. It answers a question every operator of the fabric has: "if I change the forecast policy,
tighten a contract, or ship new decide() code, which of my real past decisions would flip, and does any become an
ALLOW?" It works entirely from signed evidence the system already produces. It is also the missing input to E5's
proposal lifecycle (REGRESSION_ATTACHED needs an E4 report digest).

## Defects found while building E4

None in existing code. Two bugs in the new code were caught by its own tests before commit:
1. Report keys broke the canonical form (CJ1 keys must match `[a-z0-9_]`).
2. Git-ref module copies failed on dataclasses (they need a `sys.modules` entry; now registered under a unique
   content-hashed name, never under `cain_governance_contract`).

One design attempt was refused by the session's safety classifier and abandoned: a `pop_fp` parameter on the
production `decide()` that would have relaxed which fingerprint the proof-of-possession is checked against. Template
replay re-signs inside a sandbox copy instead. A test asserts that `decide()` has no such parameter.
