|
| 1 | +# `MemoryEvent.ProvenanceCommitted` — Design Placeholder |
| 2 | + |
| 3 | +**Issue:** [AMPR-176](https://linear.app/miley/issue/AMPR-176) (forward-looking design placeholder) |
| 4 | +**Status:** Not implemented. Design sketch only — promote to an implementation ticket when one of the trigger conditions in [Promotion criteria](#promotion-criteria) fires. |
| 5 | +**Related work:** [AMPR-168](https://linear.app/miley/issue/AMPR-168) (Wave 3 cognitive event audit, Gap-5), [AMPR-175](https://linear.app/miley/issue/AMPR-175) (`MemoryEvent.MilestoneReached`), [AMPR-169](https://linear.app/miley/issue/AMPR-169) (Phosphor / Lumos bridge). |
| 6 | +**Last verified:** 2026-05-30 |
| 7 | + |
| 8 | +--- |
| 9 | + |
| 10 | +## Context |
| 11 | + |
| 12 | +The Wave 3 cognitive event audit (AMPR-168) flagged that the original Wave 3 plan referenced "blockchain provenance entries" without confirming whether such an event existed. It does not. `MemoryEvent` today carries only `KnowledgeStored` and `KnowledgeRecalled` (see `ampere-core/src/commonMain/kotlin/link/socket/ampere/agents/domain/event/MemoryEvent.kt`). The `OutcomeRecorded` event referenced in `docs/concepts/memory-provenance.md` is itself aspirational. |
| 13 | + |
| 14 | +The Wave 3 Lumos bridge ships without provenance-chain semantics. Routine milestones — the STAR glyph signal — are covered by `MemoryEvent.MilestoneReached` (AMPR-175). Provenance is a *separate* axis: a verifiable cross-history chain for every memory write, hash-linked back to its parent, optionally signed. |
| 15 | + |
| 16 | +This document captures the design space so the work is not re-derived from scratch when AMPERE grows that surface. |
| 17 | + |
| 18 | +--- |
| 19 | + |
| 20 | +## Suggested event shape |
| 21 | + |
| 22 | +```kotlin |
| 23 | +@Serializable |
| 24 | +data class ProvenanceCommitted( |
| 25 | + override val eventId: EventId, |
| 26 | + override val timestamp: Instant, |
| 27 | + override val eventSource: EventSource, |
| 28 | + override val urgency: Urgency = Urgency.LOW, |
| 29 | + |
| 30 | + // Identity of the committed memory entry. |
| 31 | + val entryId: String, // KnowledgeId or OutcomeId — depends on publish-site choice. |
| 32 | + val entryKind: ProvenanceEntryKind, // KNOWLEDGE | OUTCOME | CHECKPOINT |
| 33 | + val knowledgeId: String? = null, // present when entryKind == KNOWLEDGE |
| 34 | + val taskId: String? = null, // optional — present when the commit was task-scoped |
| 35 | + |
| 36 | + // Chain linkage. |
| 37 | + val parentHash: String?, // null only for genesis; otherwise the prior entry's contentHash |
| 38 | + val contentHash: String, // canonical hash of (entryId, payload, parentHash, timestamp) |
| 39 | + val signer: SignerId, // who signed; opaque ID (agent / device / key) |
| 40 | + val signature: ByteArray? = null, // optional in early designs; required once signing is live |
| 41 | + |
| 42 | + val runId: String? = null, // preserves ArcTraceProjection wiring |
| 43 | +) : MemoryEvent |
| 44 | +``` |
| 45 | + |
| 46 | +`ProvenanceEntryKind` is a sealed enum (`KNOWLEDGE`, `OUTCOME`, `CHECKPOINT`) so consumers can filter without inspecting payload shape. `SignerId` is intentionally opaque — leave the key-material story to the implementation ticket. |
| 47 | + |
| 48 | +The exact field set is **not load-bearing** at this stage. The point of fixing a sketch now is so future code-review on the implementation ticket has a fixed reference to argue against, not so this becomes the schema. |
| 49 | + |
| 50 | +--- |
| 51 | + |
| 52 | +## Hash / signature scheme |
| 53 | + |
| 54 | +**Working assumption:** Ed25519 signatures over a Merkle-DAG-style parent-hash chain (each entry references one parent by `contentHash`; checkpoint entries may reference multiple parents, forming a DAG rather than a strict list). |
| 55 | + |
| 56 | +**Why Ed25519:** small keys, small signatures, fast verification, well-supported in KMP targets via `kotlin-crypto` or platform-actual wrappers. No hard requirement — Schnorr or post-quantum schemes are equally compatible with the chain shape. |
| 57 | + |
| 58 | +**Why a parent-hash chain, not a Merkle tree:** the agent writes entries serially. There is no batch-verification motivation today. A simple `parentHash` link gives append-only tamper detection at minimal complexity; the DAG generalisation is reserved for checkpoint entries that summarise N predecessors. |
| 59 | + |
| 60 | +**Explicitly out of scope at this stage:** |
| 61 | +- Choice of hash function (BLAKE3, SHA-256, both — open). |
| 62 | +- Key custody / rotation story. |
| 63 | +- Off-chain anchoring (timestamp-server, public-ledger anchoring) — none of which is in scope unless a consumer pulls. |
| 64 | + |
| 65 | +--- |
| 66 | + |
| 67 | +## Publish site |
| 68 | + |
| 69 | +Three plausible patterns. The implementation ticket picks one. |
| 70 | + |
| 71 | +**A. Co-located with every memory write.** Every `KnowledgeRepository.storeKnowledge` and every `OutcomeMemoryRepository.recordOutcome` emits one `ProvenanceCommitted`. Highest fidelity, highest event volume. |
| 72 | + |
| 73 | +**B. Checkpoint-only.** Provenance entries are emitted only at *checkpoints* — e.g., end-of-phase, end-of-run, or explicit `flushProvenance()` calls. Lower volume; each checkpoint hash covers a batch of writes via DAG fan-in. Aligns better with the OpenAI grant's formal verification cadence (one proof obligation per checkpoint, not per write). |
| 74 | + |
| 75 | +**C. Hybrid.** Per-write commit for `Knowledge`; checkpoint-only for `ExecutionOutcome` (which can be high-frequency). Operationally cheap, semantically awkward — two chains to reason about. |
| 76 | + |
| 77 | +**Recommendation for the implementation ticket:** start with **B (checkpoint-only)**. Per-write provenance can be added incrementally without breaking the chain shape; checkpoint-only is the cheaper default and matches the verification workstream's natural granularity. |
| 78 | + |
| 79 | +--- |
| 80 | + |
| 81 | +## Relationship to OpenAI grant — formal verification workstream |
| 82 | + |
| 83 | +Open question for the implementation ticket: **does AMPERE need `ProvenanceCommitted` before formal verification work begins, or does the event shape *emerge from* that work?** |
| 84 | + |
| 85 | +Two paths: |
| 86 | + |
| 87 | +1. **Event first.** Ship `ProvenanceCommitted` with a conservative shape; let the verification workstream consume it. Risk: the verifier needs a field we did not include, and we churn the event shape early in its life. |
| 88 | +2. **Verifier first.** Let the formal verification workstream define what it needs to prove (chain integrity, signer attribution, replay determinism); derive `ProvenanceCommitted` from those proof obligations. Risk: the verification workstream blocks on event design that nobody else is paid to do. |
| 89 | + |
| 90 | +Path 2 is preferred *unless* a non-verification consumer (see below) creates pull first. The event exists to be verified; designing it without the verifier's input is speculative. |
| 91 | + |
| 92 | +--- |
| 93 | + |
| 94 | +## Promotion criteria |
| 95 | + |
| 96 | +This ticket may be promoted from "design placeholder" to "implementation" when **any one** of these fires: |
| 97 | + |
| 98 | +- **Act 5 closes** and the next phase (Act 6?) opens its scope discussion — provenance is a natural Act 6 candidate. |
| 99 | +- **OpenAI grant work begins concretely** and provenance becomes a deliverable in workstream-1 (formal verification). |
| 100 | +- **Consumer pull arrives** — most likely Socket's "time-travel debugging" feature, which already reads `ArcTraceProjection` and would benefit from a tamper-evident chain. CHI / interpretability is a secondary consumer. |
| 101 | + |
| 102 | +Until one of these fires, the cost of speculative implementation (event shape churn, premature commitment to a signature scheme, write-amplification on the memory path) exceeds the value. |
| 103 | + |
| 104 | +--- |
| 105 | + |
| 106 | +## What this design does *not* lock in |
| 107 | + |
| 108 | +- The exact event payload — fields above are a sketch, not a contract. |
| 109 | +- The hash function or signature scheme. |
| 110 | +- Whether `ProvenanceCommitted` extends `MemoryEvent` or becomes its own sealed family. The current proposal keeps it under `MemoryEvent` because the publish site is the memory layer; a `ProvenanceEvent` family is reasonable if non-memory commits (config, plan, identity) join the chain. |
| 111 | +- Batching, retention, and pruning behaviour. |
| 112 | +- The wire format used by any future Lumos / Phosphor bridge — Wave 3's STAR glyph signal is `MilestoneReached`, not `ProvenanceCommitted`. |
| 113 | + |
| 114 | +--- |
| 115 | + |
| 116 | +## Implementation ticket — checklist seed |
| 117 | + |
| 118 | +When the implementation ticket is filed, it should at minimum: |
| 119 | + |
| 120 | +1. Pick one of the three publish-site patterns and justify the choice against the then-current consumer set. |
| 121 | +2. Pick a hash function and signature scheme (or explicitly defer signing to a follow-up). |
| 122 | +3. Define `ProvenanceEntryKind` and the genesis-entry convention. |
| 123 | +4. Add `provenance_store.sq` (SQLDelight schema) carrying `entry_id`, `parent_hash`, `content_hash`, `signer`, `signature`, `run_id`, `timestamp`. |
| 124 | +5. Update `docs/concepts/memory-provenance.md` to make the chain an invariant rather than an aspiration. |
| 125 | +6. Add `ArcTraceProjection` support so a time-travelled run includes its provenance entries. |
| 126 | +7. Remove the `TODO(AMPR-176)` marker in `MemoryEvent.kt`. |
| 127 | + |
| 128 | +--- |
| 129 | + |
| 130 | +*Design placeholder filed for AMPR-176. No code changes accompany this document beyond the TODO marker in `MemoryEvent.kt`.* |
0 commit comments