# RFC: P2.2 — wiring `Gov` to real witnesses + olog instances (the dependency plan)

**Status:** active (scaffold landing). **Depends on:** P2.1 (`nucleus-witness-olog`
— `Gov`, manifest, pinning, all merged) + the discharged functoriality/fork-cost
Lean (`#1759`). **Companion:** [`witness-olog-functor.md`](./witness-olog-functor.md).

## What P2.2 connects

P2.1 shipped the *contract* (the `Gov` functor + signed manifest + pinning) with
stand-in digests and no live data source. P2.2 wires it to reality:

- the **olog instance digest** (so a fact's `instance_digest` is the real
  content-address of an olog instance, not the P2.1 SHA-256 stand-in), and
- the **admitted-witness source** (so `Gov` folds over real merge-gate-admitted
  witnesses, not test fixtures).

## Repo topology decision (Option 1, recorded)

Three topologies were weighed (published thin crates / git-deps / private-side).
**Decision: publish a thin library crate + keep `Gov` in OSS** — because the whole
accumulation moat rests on the witness→olog mapping being **independently
recomputable** by a relying party, which requires `Gov` to live in the neutral OSS
core. Git-deps were rejected (they block publishing `nucleus-witness-olog` to
crates.io). A full monorepo consolidation is the eventual destination, not a
prerequisite for P2.2.

The inventory made this cheap: **`olog-core` already exists** as a zero-dependency,
`#![no_std]`, Aeneas-translatable crate carrying exactly the types `Gov`'s target
needs (`OlogInstance`, `RowId`, `MorphismEndpoint`, `Provenance`). So "extract a
thin crate" is really "publish the thin crate that's already there + add one
function."

## The two net-new pieces

### 1. `olog-core::instance_digest` (the only new dependency surface)

Neither repo currently has a canonical olog-instance digest. Add:

```rust
// olog-core, behind an off-by-default `digest` feature (keeps the crate
// zero-dep + no_std by default; nucleus turns it on).
pub fn instance_digest(inst: &OlogInstance) -> [u8; 32]
```

**Decisions (recommended, now adopted):**
- **Hash = BLAKE3**, to match `ck-types::ArtifactDigest`'s content-addressing
  family (`"blake3:<hex>"`). The olog instance is a *content-address field* inside
  `OlogFact`/`AccumulationManifest`; it is **not** the transparency-log leaf. The
  log leaf is the manifest's canonical bytes, hashed by `ct-merkle` (SHA-256, RFC
  6962). The two hash families coexist by role — BLAKE3 names artifacts, SHA-256 is
  the Merkle accumulator — and must **not** be "unified."
- **Structural content only.** Digest the rows + action (morphism graph) + fields,
  over a **canonical sorted serialization** (domain-tagged, length-prefixed, the
  same discipline as the rest of the crate). **Exclude wall-clock provenance**
  (`timestamp_ms`, `fetch_ts`, …) so the *same logical fact* has a *stable*
  digest across runs — otherwise re-deriving a fact would never reproduce its
  address, defeating recomputability.
- Flips the P2.1 stand-in (SHA-256, `…/instance-standin/v1`) to BLAKE3 — a v1 wire
  change, safe since nothing is anchored yet.

### 2. `WitnessSource` (the OSS read contract) — *shipped in this PR*

A read-only trait an archive implements; `Gov` folds over it.

```rust
pub trait WitnessSource {
    fn admitted(&self) -> Vec<WitnessNode>;
    fn lineage(&self) -> Vec<LineageEdge>;
}
pub fn accumulate<G: Gov, S: WitnessSource>(gov: &G, src: &S) -> Vec<OlogFact>;
```

Defined in OSS `nucleus-witness-olog` (with a `FakeWitnessSource` for tests/demo)
so the mapping stays recomputable. merge-gate's ReDB archive — which has **no
public read API today** — implements it in P2.2 step 4.

## Dependency-version alignment (smaller than it looked)

`olog-core` brings **no `ed25519-dalek`, no `ct-merkle`, no `sha2`** — it's
zero-dep. So the `dalek 2 vs 3.0-pre.7` clash is **not** introduced by this
dependency; it is an *internal* nucleus issue (`nucleus-lineage` pins
`=3.0.0-pre.7`).

| Dep | Plan |
|---|---|
| `ed25519-dalek` | `olog-core` adds none. **Invariant:** `nucleus-witness-olog` must **not** depend on `nucleus-lineage` (that's what would drag in `3.0-pre.7`); it already avoids this (pinning uses `ct-merkle` directly). Stay on workspace dalek `2`. |
| `blake3` | add `blake3 = "1"` to `olog-core` **only** under the `digest` feature (ck-types already uses `1.x`; olog has none → no clash). Crate stays zero-dep / `no_std` by default. |
| `sha2` | nucleus `0.11`; `olog-core` doesn't use it (instance_digest is BLAKE3; SHA-256 enters only via `ct-merkle`). No action. |

Net new third-party dep entering the verifiable core: **`blake3` only, behind a
feature** (and already present transitively via `ck-types`).

## What lands where (the recompute line)

- **OSS, published:** `coproduct-olog-core` (types + `instance_digest`); `Gov` +
  manifest + pin + `WitnessSource` (`nucleus-witness-olog`); `ck-types`.
- **Stays as a service (not a dep):** the olog runtime/server, ReDB, the live
  archive — read through `WitnessSource`, never vendored.
- **Result:** the witness→olog mapping is recomputable by any relying party from
  published crates.

## PR sequence

1. **`coproduct-olog-core`:** add `instance_digest` (BLAKE3, `digest` feature);
   publish to crates.io. *(olog repo; publish gated on name confirmation.)*
2. **`WitnessSource` trait + `FakeWitnessSource` + `accumulate`** — *this PR*
   (OSS contract; no external dep yet).
3. **Wire `Gov`:** depend on published `coproduct-olog-core`, replace the SHA-256
   stand-in with `instance_digest`, drive `Gov` over a `WitnessSource`, emit + sign
   + pin manifests. **Lift functoriality to the full lineage DAG** (the remaining
   `MODELED` piece — the real edges now exist).
4. **merge-gate:** implement `WitnessSource` over the ReDB archive. *(merge-gate
   repo.)*

Steps 1–3 are OSS and unblock the full-DAG functoriality proof; step 4 is the
live-data hookup.

## Honest boundary

- `instance_digest` excluding provenance timestamps means two facts with identical
  structure but different capture times share a digest — *intended* (logical-fact
  identity), but it means the digest is **not** a uniqueness key over capture
  events; the manifest's `ci_run_id` / `commit_sha` carry that.
- Publishing a crate name is a commitment (crates.io names are not reclaimable);
  `coproduct-olog-core` is the proposed name, gated on confirmation before the
  actual `cargo publish`.
