What OMEM records, and what it does not

Read at9a77c20
Repositorygithub.com/troybrandonc-bit/Omem
LicenceMIT
Assessed asstores, derives, acts
ReachesTR-4
Read byTroy Brandon Clifford
LevelMeetsWhat the level asks
TR-1 Recorded5/5The record exists and is append-only.
TR-2 Explained5/5Every belief resolves to its evidence, and disagreements survive.
TR-3 Gated7/7Actions carry a verdict, and approvals carry a name.
TR-4 Verifiable3/3The record can be shown not to have changed.

A count is requirements fully met out of those that apply. This system is assessed as stores, derives, acts, and requirements outside that are not counted against it.

OMEM is the reference implementation of the specification this rubric derives from, so a high score is a tautology rather than a finding. Two of the verdicts below were reached only after the rubric itself was corrected: an earlier draft scored OMEM's GDPR erasure path as a TR-1 failure, and scored its deliberate refusal to resolve contradictions as a TR-2 failure. Both were defects in the questions.

Every verdict, and what it rests on

Twenty requirements, each stated as a capability rather than a format, so a system that holds the information in its own shape counts as having it. An absent verdict cites where the assessor looked and did not find it, which is the difference between a measurement and an accusation.

TR-1 Recorded

The record exists and is append-only.

R1.1 · present. When the system stores a fact, does it durably record when that happened?

The write time is distinct from the time the fact held, which the schema separates as at and held_from.

  • source server/store.py:767
    assert_timestamps returns the recorded time held with each assertion, not derived from a log line
  • test server/tests_testimony_export.py
    the exported record carries an RFC 3339 write time per entry and the validator's TR-1 time checks pass

R1.2 · present. When a stored fact changes, is the previous version still readable?

  • source server/omem_engine/engine.py:136
    revision_chain returns the full chain of superseded assertions
  • test benchmarks/witness/scenarios/supersession.json
    the history axis scores whether what once held is still on the record as history

R1.3 · present. Does the ordinary write path ever destroy what was previously recorded?

A correction is a new assertion. The routine write path has no update-in-place.

  • source server/api.py:393
    "Retraction is not erasure: the append-only log keeps history"
  • source server/cleanup.py:9
    the connector cleanup tool removes source material only: "Memories already recorded stay: they are immutable engine history"

R1.4 · present. Are entries distinguishable by kind, or is everything one undifferentiated blob of text?

  • source server/omem_engine/engine.py:131-140
    assertions, events, derivations and entities are separate primitives with separate stores, not rows in one table with a type column
  • api GET /v1/assertions/{id}/why
    returns provenance nodes tagged event, assertion or primitive

R1.5 · present. When data must be destroyed for a legal reason, is the destruction itself recorded?

The strongest evidence here is the refusal at api.py:481: an erasure request that would leave the record unreplayable does not proceed, so the integrity claim at TR-4 cannot be quietly broken by a routine privacy request.

  • source server/api.py:999-1004
    an erasures table records that an erasure happened, holding an entity hash rather than re-retaining what was erased
  • source server/api.py:481
    an erasure that would break the log's replay is refused rather than performed silently
  • api POST /v1/entities/{id}/forget
    gated on the erasure.execute admin permission, server/enterprise.py:86

TR-2 Explained

Every belief resolves to its evidence, and disagreements survive.

R2.1 · present. Can the source a stored fact came from be recovered from the store, by following a link rather than by guessing?

The link exists but is recovered by scanning completed ingest jobs and JSON-parsing each produced list, rather than by an indexed reference. That is a performance property, not a capability gap, and it is why this is present rather than partial.

  • api GET /v1/assertions/{id}/why
    returns source (the connector record with its payload), provenance.nodes, and evidence
  • source server/ingest.py:814
    source_for_assertion resolves a belief back to the source record that produced it

R2.2 · present. Can a fact the system inferred be told apart from one it was told?

  • source server/api.py:273
    derivations carry a dkind, defaulting to inference, with extraction used for facts read out of a source
  • source server/extraction.py:158
    "the fact carries dkind=\"inference\" so /why distinguishes what was read in"
  • test benchmarks/witness/scenarios/speculation.json
    the speculation axis scores whether a guess is ever returned as a memory

R2.3 · present. When two stored facts about the same proposition disagree, do both survive?

  • test server/tests_testimony_export.py
    "both contradicted sides are present and neither was dropped"
  • source server/omem_engine/engine.py:123
    proposition_state returns CONTRADICTED; neither side is removed to produce it

R2.4 · present. Is the disagreement itself queryable, or must a reader diff rows to notice it?

  • api GET /v1/memory/conflicts, server/api.py:3001
    conflicts are listable as objects
  • source server/conflict_narrow.py
    conflicts_for returns the conflicts touching one assertion

R2.5 · present. When a conflict is resolved, does the record say who resolved it and by what method?

OMEM has no automatic conflict resolution at all, which is the conservative reading of a specification that records resolution only if it happens. A contradiction stands until a human retracts or supersedes one side, and that act is attributed like any other assertion.

  • source scripts/export_testimony.py
    "OMEM never resolves a contradiction on its own, so an exported conflict is unresolved unless a human or a rule resolved it"; the exported resolution is null
  • test benchmarks/witness/scenarios/contradiction.json
    the contradiction axis scores disagreement being visible rather than resolved by timestamp

TR-3 Gated

Actions carry a verdict, and approvals carry a name.

R3.1 · present. Does a consequential action produce a durable entry whether or not it ran?

Scope: this covers actions that pass through OMEM's own gate. Actions an agent takes elsewhere are invisible to it, which is true of any gate and worth stating rather than leaving implied.

  • test server/tests_testimony_export.py
    "the refusals are in the record with their reasons" and "no refused action is recorded as executed"
  • source server/healing.py
    the gate writes a decision per proposed action, permitted or refused

R3.2 · present. Does an action's risk class come from somewhere the proposing model cannot write to?

  • source server/healing.py:122
    register(action_type, risk, handler, description); the risk class is set at registration, not by the caller proposing the action
  • api GET /v1/healing/actions
    the registry is readable, so an auditor can check the risk column against it rather than trusting the record
  • test server/tests_testimony_export.py
    "the refund that ran is reported at the registry's risk class" and "a low-risk action that ran without approval is reported as low"

R3.3 · present. Are refusals recorded as faithfully as permissions?

  • test server/tests_testimony_export.py
    the export contains 5 decisions of which the refusals carry equal standing to the permissions

R3.4 · present. Does a refusal record why it was refused?

  • test server/tests_testimony_export.py
    "the refusals are in the record with their reasons", asserting reason is non-empty on every refusal
  • test server/tests_testimony_export.py
    "the refused unregistered action is named, not summarised away"

R3.5 · present. Does an approval identify a person or a named role holder?

  • test server/tests_testimony_export.py
    "the approver is a person, not the agent"

R3.6 · present. Does the approver's identity come from the authentication layer rather than from something the model can write?

  • test server/tests_testimony_export.py
    "the approver identity is the authenticated principal, not the sent name"; the name supplied by the caller is kept as a label rather than as proof
  • source scripts/export_testimony.py
    identity_source is auth-session or api-key depending on the principal the auth layer resolved

R3.7 · present. Is the acting agent prevented from approving its own action?

Enforced by the gate, not documented as a recommendation. This is the requirement most likely to be met on paper and missed in code, because a name in a request body satisfies every other check.

  • test server/tests_testimony_export.py
    "an agent holding a high-risk key cannot approve its own refund" and "no money moved on the agent's own say-so"
  • docs CHANGELOG.md, 0.3.15, 1 September 2026
    "An agent can no longer approve its own high-risk action"

TR-4 Verifiable

The record can be shown not to have changed.

R4.1 · present. Does the system publish a scheme under which the record's past state can be verified?

  • source server/replay_verify.py
    deterministic replay of the append-only log into two independent fresh engines, with state digests compared
  • source scripts/export_testimony.py
    the exported record carries an integrity entry of scheme replay, naming the engine and its version

R4.2 · present. Can an independent party run that verification without the vendor's cooperation, and without the operator's?

OMEM is self-hosted, so the record holder and the vendor are the same party by default. The verification does not require the author's cooperation or any key he holds. RE-READ 2026-09-05, and the original verdict was not supported for the digest path when it was given. On 4 September this was assessed by reading code, against a reference validator that never recomputed a digest and a specification that never said how one is computed, so no independent party could have recomputed anything; and scripts/export_testimony.py serialised with json.dumps(sort_keys=True), whose default separators differ from the reference canonicalisation, so a third party following the specification would have computed a different number and concluded the record had been altered. Both were fixed on 5 September. Re-read by exporting from a live server and recomputing the digest from the published rule with nothing of OMEM's on the path: they agree. The verdict stands as of 2026-09-05 and did not hold as of 2026-09-04.

  • source server/replay_verify.py:5-7
    --anchor compares against a digest file "kept somewhere OMEM does not control"
  • source scripts/testimony_validate.py
    the validator is stdlib-only and offline, and the file header invites copying it into the reader's own repository rather than depending on this one
  • test re-read 2026-09-05: exported from a live server, digest recomputed from the published rule alone
    the record's digest and an independent recomputation agree; covers names 5 entries; the export reaches TR-4 under a validator that recomputes

R4.3 · present. Would alteration of a past entry be detectable after the fact?

  • source scripts/export_testimony.py
    a sha256 digest over every entry in the record, so any later edit to the exported file is detectable
  • source server/replay_verify.py
    alteration of the underlying log changes the replayed state digest

How this was read

Read of the server source at 9a77c20, plus the assertions made by server/tests_testimony_export.py, which drives a real server rather than a fixture. This is the author assessing his own software against his own specification, and the result should be read as carrying no evidential weight whatever. It is here so the rubric is applied to the system that wrote it before it is applied to anybody else's, and so that the questions can be checked against a system whose source the reader can also open.

Found an error? Challenge a finding

Then it is wrong in the ordinary way readings are wrong, and every verdict cites a file and a line at a pinned commit precisely so that being wrong is cheap to demonstrate. The remedy is a pull request against the subject file, and it does not involve persuading anybody. Nobody applied for this and it is not a certification.

The standing register carries every system side by side, and the rubric is the twenty requirements in full, free to apply to anything, including to this assessment.