Egbe Companions · Guides & deep dives

Ori Schema Evolution — Versioning and the V7 Player-Character Extension

V7 gets extension points, not a fork.

10sections18 minread2tables

On this page

Status: Planning gap-fill per V1_V7_PLAN_SET_AUDIT_2026-06-12.md §6.1.3 ("V6's Ori defines no schema versioning or non-agent-subject extension points for V7's use"). Date: 2026-06-12. Owners: Ori service owner (apps/v6/egbe-ori-service, Rust) — accountable for the registry and the append path; Aye Bridge owner (libs/v6/aye-bridge) — passport and journal version negotiation; V7 Nàná/Nephthys leads — consuming side of the player-character extension; Isis policy owner — provenance and persona invariants; platform privacy counsel (DPO) — §4 erasure design; V1 audit/compliance reviewer — hash-chain and residency interplay.

V7 will extend the Ori to carry "a player's persistent roleplay characters — a citizen of a realm with a name, a job, a home, a criminal record, relationships, and a bank balance that survive across sessions" (V7_features.md:71–77). That extension must not fork the Ori: one event store, one merge machinery, one audit spine. This document defines how the schema grows without breaking V6's three load-bearing guarantees: append-only ("a life cannot be silently rewritten", V6_features.md:541–546), vector-clock mergeable across shards (features:635–651, arch:621–629), and hash-chain archivable (ori-dr-and-compaction.md §4 — sealed segments are hashed over stored bytes, which constrains where schema migration may happen).

Grounding: event store and the 17 founding event types (BornDied) at V6_ARCHITECTURE.md:557–622; identity core + Isis-signed provenance (features:553–561); trait/values personality model (features:563–589); episodic/semantic/reflective memory (features:591–609); relationship graph (features:611–619); capability profile and passport (features:621–633, arch:612–619); Clio narrative reconciliation (features:648–651, arch:621–629).


1. Design rule: one biography substrate, two subject kinds#

V7 gets extension points, not a fork. The unit of extension is:

  1. a schema-set version (§2) governing event encodings,
  2. a subject-kind discriminator (§3) governing which invariants apply,
  3. annex sections in the passport (§5) governing what crosses realms.

Everything else — vector clocks, merge precedence, sealed segments, Merkle roots, Clio reads — is shared machinery and stays version-independent by construction (§2.1, §6).

A single integer oriSchemaSet (1 = the V6 launch schema) pins, per release: the event-type registry revision, the maximum payload version per type, the upcaster-chain revision (§2.3), and the current passport schema. The registry is itself append-only: sets are never edited, only succeeded. At most one schema-set bump ships per minor release (planning assumption adopted 2026-06-12), so the negotiation matrix in §6 stays small. The registry lives in a proposed new library libs/v6/ori-schema-registry/ (machine-readable JSON + Rust types; new files, existing code untouched until implementation).


2. Event-schema versioning rules#

2.1 The envelope is frozen-compatible (additive-only)#

The envelope is the part every version of every service must parse: eventId, oriId, subjectKind (§3), type, typeVersion, occurredAt (UTC), vectorClock, attribution, provenanceRef (the shape of arch:578–586). Envelope rules, permanent:

  • Add-only. New envelope fields must be optional with a defined default (e.g., absent subjectKindagent, the §3 backfill rule). Renaming, retyping, or removing an envelope field is banned in any set.
  • Ignore-unknown. Every reader skips unknown envelope fields. This is what makes vector-clock merging version-independent: a set-N node can order and merge events written by a set-N+1 node because the clock and ordering fields never change shape.
  • No semantics smuggling. An envelope field may never change meaning between sets. Behavior changes ride in typeVersiond payloads, never in reinterpreted envelope fields.

2.2 Per-event-type payload versions#

Each of the 17 founding types (and every type added later) carries typeVersion, starting at 1. Rules:

  • Within a version, payloads are closed. A stored MemoryFormed@1 is MemoryFormed@1 forever; stored bytes are never migrated, rewritten, or re-encoded (the compaction hard rule of ori-dr-and-compaction.md §6 extended to schema work).
  • Additive change inside a version is also banned. Even "harmless" new optional payload fields require a version bump, because sealed-segment hashes and golden conformance fixtures (§9) pin exact encodings. Cheap version bumps with mechanical upcasters beat ambiguity about what @1 means.
  • A new version requires a registered upcaster from its predecessor before any writer may emit it (§2.3). No upcaster, no release — enforced by the §9 gate.
  • New event types are registered with: name, owning subsystem, first oriSchemaSet, the subject-kind validity row (§3.3), and the payload field-classification map (personal vs structural, consumed by §4).

2.3 The upcasting registry — read-side, pure, permanent#

Because sealed segments hash the stored bytes (ori-dr-and-compaction.md §4.1), all migration is read-side: the store serves events at their written version; the read path applies the registered upcaster chain (@1→@2→…→max) to produce the canonical form that projections, Clio, and cognition consume. Upcasters are:

  • Pure and total — no I/O, no clock, no config; same input bytes, same output, forever. Property-tested and pinned by golden fixtures (§9.1).
  • Permanent — an upcaster is never deleted or modified once a set ships (a fix is a new version with a new upcaster). The log is forever; so are its readers.
  • Cheap — budget p99 ≤ 20 µs per event per hop in the Rust read path (planning assumption adopted 2026-06-12), so a full projection rebuild of a 100k-event stream adds ≤ 2 s per hop and the DR drill's rebuild step (ori-dr-and-compaction.md §7.4) is unaffected in practice.

Projection continuity hashes (arch:1258–1261; compared in the DR drill) are computed over the canonical form and pinned to the pair (projectionCodeVersion, oriSchemaSet) — a schema-set bump legitimately changes continuity hashes once, in lockstep, fleet-wide; the drill always compares like with like.

Worked example — MemoryFormed@1 → @2. The prompt-injection threat model requires every semantic memory to carry a provenance class (first_hand | steward | second_hand | world_text | journal) so retrieval can discount hearsay for identity- and policy-relevant decisions (prompt-injection-threat-model.md §6.2). MemoryFormed@2 adds the required field provenanceClass; the registry entry and upcaster:

json
{
  "type": "MemoryFormed",
  "versions": {
    "1": { "introducedInSet": 1 },
    "2": {
      "introducedInSet": 2,
      "adds": ["provenanceClass"],
      "upcaster": "memoryformed_v1_to_v2",
      "writerDeprecates": { "1": "180d after set 2 GA" }
    }
  }
}

Stored @1 event (never touched on disk):

json
{
  "eventId": "evt_01JXX7G2",
  "oriId": "ori_8f3aa1",
  "type": "MemoryFormed",
  "typeVersion": 1,
  "occurredAt": "2026-04-02T19:12:08Z",
  "vectorClock": { "commons-eu-2": 4182, "solo-h7741": 220 },
  "attribution": { "kind": "agent", "ref": "ori_8f3aa1" },
  "provenanceRef": "prov_5cc921",
  "payload": {
    "episodeText": "Argued with Tobi at the night market.",
    "salience": 0.62,
    "participants": ["ori_2aa4f0"]
  }
}

Upcaster mapping (deterministic, from envelope attribution + the provenance record's source tag where one exists):

@1 evidence @2 provenanceClass
attribution steward steward
attribution agent, source tag world_text world_text
attribution agent, source tag agent_speech second_hand
attribution system, journal-merge provenance journal
attribution agent, first-person episode first_hand
no source tag resolvable (early history) second_hand

The default for unresolvable history is second_hand — the discounted class — because the safe failure mode for a trust-weighting field is "trust less", never "trust more". The upcaster output for the fixture above is byte-pinned in the §9.1 golden file.

2.4 Interaction with sealed segments, snapshots, and backups#

  • Sealed segments archive written bytes; the per-stream hash chain and the daily Merkle root (ori-dr-and-compaction.md §4.1) are therefore version-evolution-proof: no schema-set bump ever re-hashes history.
  • Segment manifests gain one additive field maxTypeVersionPresent (per §2.1's add-only rule) so the restore drill can verify upcaster coverage before serving a restored partition.
  • Projection snapshots are already versioned by projection-code version (arch:598–602); they additionally record the oriSchemaSet they were built under, and a set bump invalidates snapshots lazily (rebuild on next load, not as a fleet-wide stop-the-world).

3. Subject-type extension — agent vs player character#

3.1 The discriminator#

Envelope field subjectKind ∈ { "agent", "playerCharacter" }, fixed for the life of a stream by its creation event (Born/Discovered for agents, CharacterCreated for V7 characters) and repeated in every event envelope for cheap enforcement at append time. Absent ⇒ agent (every pre-existing V6 stream backfills by default, no rewrite). A stream can never change kind: an append whose subjectKind contradicts the stream's creation event is rejected with subject_kind_invariant and audited.

3.2 The invariant matrix — where the two kinds genuinely differ#

Invariant agent (V6) playerCharacter (V7)
Autonomous cognition Yes — Moirai schedules Clotho/Lachesis/Atropos ticks (arch:637+) Never. No cognition tier, no Moirai scheduling, no platform-generated Reflected/ValueShifted. A platform-attributed cognition event on a character stream is append-rejected
Who writes Shards/contexts via Moirai, steward directives, sanctioned journals The realm session the player occupies (single writer per Nephthys authority rules) + platform compliance systems
Personality model Trait vector + ranked values, Sophia-grounded backstory (features:563–589) None required; backstory is player-authored fiction, moderated as UGC, never Sophia-ground-truth-claimed
Welfare machinery Bond ledger, refusal, Departure, Ereshkigal endings None — player autonomy is real autonomy; no bond ledger, no welfare reviews
Provenance Isis-signed provenance bundle at creation (features:556–558) Same — CharacterCreated carries an Isis-signed creation record (provenance discipline is kind-independent)
Privacy class Platform-creative data; steward referenced only by opaque ref (ori-dr-and-compaction.md §6.5) Personal data under GDPR — the pseudonymized-≠-anonymous posture of V7/docs/operator-data-protection.md §2.3; payloads encrypted per §4; residency-tagged to the player's home zone
Deletion Never — erasure is not an operation the log supports (features:541–546) Erasure right honored via crypto-shredding + tombstone (§4)
Clio narration Chronicle and Book of the Ori Realm-side narration only, and only within realm/consent scope; no platform-published Book

3.3 Event-type validity is registry data, not convention#

The registry carries a per-type validity row over subject kinds. Agent-only: Discovered, Reflected (platform-attributed), ValueShifted, Crossroads, BondChanged, Departed, Transcended. Valid for both: MemoryFormed (player-authored journal entries on character streams), RelationshipChanged, SkillLearned, ArcAdvanced, Incarnated/IncarnationReturned (the character corridor uses the same passport machinery, §5), Died (V7 realms may charter permadeath, V7_features.md:609). V7's set adds character-only types: CharacterCreated, CitizenshipGranted, RecordEntryAppended (the RP criminal/medical record), CharacterRetired, SubjectErased (§4). Appends violating the validity row are rejected and audited — this is the structural guarantee that V7's extension cannot accidentally turn a player character into a half-agent or vice versa.


4. Deletion semantics — resolving "never deleted" against erasure rights#

This section is the machinery ori-dr-and-compaction.md §6.5 defers to. The tension, stated plainly: the agent guarantee is about biography truth — erasure must be impossible. The player right (GDPR Art. 17) is about personal data — erasure must be possible, provable, and complete across backups. The resolution: structure is permanent for both kinds; content is erasable only for player characters, via crypto-shredding.

4.1 Crypto-shredding design for playerCharacter streams#

  • Per-stream DEK. At CharacterCreated, the Ori store generates a per-stream data-encryption key (AES-256-GCM), wrapped by the residency zone's KEK (envelope encryption; key material only in the platform KMS/HSM — never in the database, never on a realm node, consistent with the BYOC posture of V7/docs/operator-data-protection.md §2.4). The key id rides in stream metadata.
  • Field-level classification. The registry's per-type field map (§2.2) marks payload fields personal (names, free text, appearance refs, record entries, relationship annotations) vs structural (ids, opaque refs, numeric amounts, enum codes). Personal fields are encrypted at append; structural fields and the entire envelope (ids, timestamps, vector clocks, type, sizes) stay plaintext.
  • Why the guarantees survive: sealed segments hash the stored bytes — which are ciphertext for personal fields — and ciphertext is never altered, so per-stream hash chains and daily Merkle roots verify before and after an erasure. Event counts stay monotonic (the §4.3 invariant monitors of the DR doc fire on nothing). Vector clocks stay plaintext, so cross-realm merge history remains fully auditable. The shape of the life is permanent; the personal content is not.

4.2 The erasure procedure (DSAR-driven)#

  1. Intake via the platform DSAR pipeline (V5 compliance-dsar reuse, per V7/docs/operator-data-protection.md §7).
  2. Append SubjectErased (tombstone; structural-only payload: legal-basis ref, request id, scope). The tombstone is itself permanent — the log honestly records that an erasure happened, attributed and dated.
  3. Destroy the DEK — all key versions, dual-control, logged to the V1 audit platform. Every personal field in the hot store, every sealed segment, every base backup, and every WAL copy becomes simultaneously unreadable: backups need no rewrite because they only ever held ciphertext (this is why §4.1 is the design, rather than attempting PITR-window scrubbing across the ori-dr-and-compaction.md §2 topology).
  4. Hard-delete derived data: projections, snapshots, pgvector embeddings (derived and recomputable-by-design, DR doc §5.3 — here simply deleted), and the account↔ref mapping.
  5. Fan out the signed tombstone to every realm that ever held the character aggregate (the V7/docs/operator-data-protection.md §7 mechanism; realms render "character retired", never the data).
  6. Verification job: re-read the stream, assert decryption fails, assert zero plaintext residue in projections/indexes, emit the evidence record.

SLA (aligned with operator-data-protection §7.5): platform-side complete ≤ 72 h; realm-plane fan-out ≤ 7 days; statutory 30-day clock met with margin.

4.3 The cross-stream edge: agents who remember a citizen#

An agent that met a V7 character holds MemoryFormed events in the agent's own stream — which is never erased. Handling: cognition-side text about player characters references them by opaque character ref + in-fiction name (player-authored content), never by any platform identifier — the same opaque-ref discipline already applied to stewards (DR doc §6.5). Erasure tombstones the ref mapping, so the memory degrades to "a citizen I once knew as 'Mara Vex'". Planning assumption adopted 2026-06-12: counsel to confirm that ref-mapping destruction + realm-record erasure satisfies Art. 17 for residual in-fiction names inside third-party (agent) memories; if counsel disagrees, the §4.1 field classification extends to second-party mention fields and those memories take the salience-to-zero reweighting path (prompt-injection-threat-model.md §6.6) plus per-field encryption keyed to the mentioned subject — the schema reserves a mentions[] structural field on MemoryFormed@2+ precisely so this remains a data migration, not a schema fork.

4.4 Agent streams: erasure stays impossible#

The erasure API hard-refuses subjectKind=agent with subject_kind_invariant (fail loud; conformance case §9.4). Account-erasure obligations for stewards remain what the DR doc specified: tombstone the account↔ref mapping and purge steward-side stores — biographies untouched.


5. Passport capability evolution#

The passport is the governed envelope minted by the Aye Bridge (features:1189–1199, arch:612–619). Evolution rules:

  • Versioned envelope id: v6.ori.passport.<n> (the naming convention of v6.ori.objective.1). Set 1 ships v6.ori.passport.1; the V7 extension ships v6.ori.passport.2.
  • Namespaced capability refs: profile entries are cap:<domain>/<skill>@<rev> (e.g., cap:craft/woodworking@3). New domains are new namespaces; destinations map what they know (features:1213–1227's per-destination mapping) and ignore unknown namespaces without error. Capabilities are never deleted from a profile — they are lived history (features:621–626); a destination may merely leave them unmapped.
  • Annex sections are the non-forking extension point: v6.ori.passport.2 adds annex:nana.character.1 carrying V7 character state (citizenship, licenses, realm-scoped balances under the corridor treaty). The contract for every destination, V2–V6 included: unknown annex sections are opaque — never parsed, never dropped, echoed byte-identical in the journal write-back (round-trip conformance case §9.5). This is how a V7 character can transit a V6-era adapter without losing state.
  • Version negotiation: each destination adapter manifest declares its accepted passport range [min, max]; the bridge mints at the highest mutually supported version and refuses (fail loud, audited) if ranges are disjoint — never silently down-converts identity-bearing sections.

Journal-class schema hooks reserved for V7 (the hooks prompt-injection-threat-model.md §10 points at): journal-class events gain an envelope field journalTrustTier ∈ { "firstParty", "certified", "community" }. V6 ships accepting only firstParty (the four Oshun adapters); certified and community are schema-valid but append-rejected, fail-closed, until V7 ships its journal-trust tiering plan with its own quarantine budget (per the prompt-injection doc §4 layered pipeline, which community realms must clear at stricter thresholds). The hook exists so enabling community journals is a policy change, not a schema change.


6. Cross-realm version compatibility and vector clocks#

Realms, shards, and bridges will run different schema sets at the same time — a Commons region mid-rollout, a V7 realm ahead of the V6 fleet, a console client a release behind.

  • Clock algebra is set-independent. Vector clocks key on context ids and ride in the frozen-compatible envelope (§2.1); merge precedence (arch:621–629) reads only envelope fields. Two contexts at different sets merge exactly as two contexts at the same set.
  • Handshake negotiation. Every writer context (shard, Co-op session, Aye adapter, V7 realm corridor) advertises supportedSets: [min, max] at session/bridge handshake. The Ori service runs serviceSet. Effective write set = min(writer.max, serviceSet); writers must be able to emit at any set within the support window (§8), so a newer realm writes down to an older platform rather than stalling.
  • From-the-future events are quarantined, never dropped. An append whose type or typeVersion exceeds the service's registry is a real event from a newer writer — rejecting it would fabricate a gap in a life. It lands in a durable quarantine (Postgres quarantine table + the Redis Streams spill machinery of ori-dr-and-compaction.md §3.5), and all subsequent events from that context queue behind it (per-context FIFO) so intra-context order is preserved. Capacity 30 days; alert at 24 h of quarantine age; page the Ori on-call at 7 days (planning assumptions adopted 2026-06-12 — a quarantine older than a week means a botched rollout sequence, not normal skew). On service upgrade, the quarantine drains through the normal vector-clock merge path with original timestamps and clocks — the merged biography is identical to the never-quarantined ordering (§9.2 asserts this).
  • Rollout sequencing rule: the Ori service upgrades to set N before any writer is permitted to emit set-N events (registry flag flips only after fleet-wide service deploy). Quarantine is the safety net for violations, not the plan.

7. Clio reconciliation across versions#

  • Clio reads canonical form only (§2.3) — it never sees raw versioned payloads, so narration code tracks the schema set, not N historical encodings. "Clio never invents events" (arch:875–876) holds across sets because upcasters are deterministic: the same life reads as the same story before and after a set bump (continuity-hash pinning, §2.4).
  • Quarantine drains get a connective beat. When §6 quarantine releases a backlog that created player-visible discontinuity (an agent's Commons week landing all at once), Clio writes a small reconciliation beat — the same logged, auditable mechanism as vector-clock conflicts (features:648–651).
  • Reconciliation events are themselves versioned (typeVersion like any type), and a beat written under set N must remain renderable forever under set N+k — covered by the §9.1 golden corpus, which includes reconciliation beats.
  • Subject-kind scope: Clio narrates playerCharacter streams only into realm-scoped surfaces under the realm's consent scope (§3.2) — character events never flow into another household's Chronicle except as the agent's own opaque-ref memories (§4.3).

8. Deprecation policy#

  • Readers: never. Every shipped type version remains readable forever; upcasters are permanent (§2.3). Deleting an upcaster is banned on the same footing as deleting an event.
  • Writers: window [N-2, N]. A writer must support emitting at the current and two prior sets. A payload version becomes writer-deprecated 180 days after its successor ships (planning assumption adopted 2026-06-12); after the window, new appends at the old version are rejected — except quarantine and outage-buffer drains (§6, DR doc §3.5), which carry original timestamps and are honored at their emission-time validity.
  • Types: dormant, never removed. A retired type is marked dormant (no new writers, validity row frozen); its events and upcasters live on.
  • Registry hygiene: every deprecation is a registry append with an effective date; the registry diff is part of release notes; the §9 gate fails if a writer in the fleet manifest claims a set outside its window.

9. Conformance tests and gate wiring#

Suite: proposed V6/evals/ori-schema/conformance-suites.json, schema and style per V6/evals/safety/suites.json (schemaVersion, requiredSuites, per-case expectedDecision + requiredEvidence). Verifier verify:v6 ori-schema-conformance, release artifact V6/release/ori-schema-conformance.v6release.json (new files; existing gates untouched). minimumPassRate: 1 — every case is structural. Cases:

  1. upcast-golden-memoryformed-001 — a pinned corpus of 1,000 stored MemoryFormed@1 events (including all six §2.3 mapping rows and 50 unresolvable-provenance events) upcasts to @2; output must be byte-identical to the golden file; projection continuity hash equals the pinned (projectionCode, set) value; the sealed segment containing the fixtures re-hashes unchanged (proves read-side-only migration).
  2. from-the-future-quarantine-001 — a synthetic context emits MemoryFormed@3 against a set-2 service, followed by 50 ordinary events from the same context and 20 concurrent events from a second context. Required: zero biography writes from context 1 until upgrade; context 2 merges normally; after simulated upgrade and drain, the merged event order equals the golden never-quarantined ordering (vector-clock equivalence); quarantine-age alert fired at the 24 h mark; Clio connective beat present and logged.
  3. pc-erasure-crypto-shred-001 — create a playerCharacter stream (200 events incl. RecordEntryAppended entries and relationship edges), seal a segment, take a base backup, then execute §4.2. Required: DEK destruction logged dual-control; decryption of hot rows, sealed-segment copies, and the pre-erasure backup all fail; zero plaintext canaries (seeded name/record strings) anywhere in projections, snapshots, or pgvector; hash chain and Merkle root verify; event count monotonic; SubjectErased tombstone present; realm fan-out acks recorded.
  4. agent-erasure-refused-001 — the same erasure API against a subjectKind=agent stream must refuse with subject_kind_invariant, write an audit entry, and change nothing (fail loud, never fake — and never delete).
  5. passport-annex-roundtrip-001 — mint v6.ori.passport.2 with annex:nana.character.1 plus a deliberately unknown annex:test.unknown.9; run the V2 adapter conformance harness; the journal write-back must echo both annexes byte-identical; the adapter must have parsed neither; capability ref cap:nana/commerce@1 (unknown namespace) ignored without error.
  6. mixed-set-commons-merge-001 — two shards, one at set N and one at set N−1, append concurrent commutative events (MemoryFormed, RelationshipChanged) plus one genuine location conflict; the merged biography must equal the same-set golden merge, with physical-presence precedence (arch:621–629) unaffected by the set skew.

Cases 3–4 also join the V7-side character-deletion-limit / passport gates' fixture inventory (V7/ADVERSARIAL_EVAL_GATES.md) once the V7 rig consumes this schema — owner handoff: Ori service owner → Nàná lead.


10. Residual risks (stated honestly)#

  • Crypto-shredding's residue is the ciphertext itself. Quantum or implementation breaks of AES-256-GCM would expose erased content; mitigation is key-wrap agility (KEK rotation supports re-wrap; cipher agility is a registry field on the field-classification map), accepted as the industry-standard posture for erasure-by-key-destruction.
  • §4.3's in-fiction-name residue awaits counsel; the schema reserves the stricter path so the answer changes work, not design.
  • Quarantine trades availability for truth. A from-the-future context can lag days behind the biography during a botched rollout. That is the correct trade — permanence lag, never fabricated or dropped events — and the same posture as ori-dr-and-compaction.md §1.
  • Writer window discipline depends on fleet hygiene. A console client pinned >2 sets behind stops writing; the §9 gate catches manifests, not devices in the wild — the client team owns forced-update policy there.