A gate that tests whether the gate discriminates

OmniMemory

A default-deny AST check with positive and negative controls over its own checker — added after a verifier demonstrated the laundering hole live — sitting above a lifecycle runtime that is a logged no-op.

Carries 0 of 7 rubric mechanisms. Most systems here carry none or one (41%), and a dash means the mechanism was not found at this commit — not that the system needed it. Each mark is one LLM reviewer's reading of the code at this commit rather than a run of it — known limits.

  • Tombstone
  • Trust state
  • Bi-temporal
  • Scope enforced
  • Mutation audit
  • Human review
  • Negative evals

1. Executive Summary

OmniMemory is the memory domain of the ONEX platform — 119,000 lines of MIT Python, mypy strict, Pydantic models throughout, Kafka-dispatched nodes, and adapters for Qdrant, Memgraph, Valkey and Postgres.

The thing worth reading is a test file.

tests/gates/test_consumer_group_name_authorization.py is 519 lines enforcing that no Kafka consumer-group name in src/ is a string literal. It is a default-deny AST walk: every group_id= / consumer_group= / kafka_group_id= keyword and every default on a matching field "must be a call to (or a local binding of) one of the canonical derivation helpers. A newly added string literal FAILS by default — that is the whole point of the gate."

Then it does the part almost nobody does. It tests the checker:

"A default-deny gate whose discrimination is never exercised is indistinguishable from a gate that always passes; these prove it catches literals, f-strings, and laundering while admitting real derivations."

Six test_checker_* cases run positive and negative controls over the AST walk against inline fixtures, and the docstring records why they exist: they were "added in the OMN-15639 round-2 remediation after a verifier demonstrated the apply_instance_discriminator laundering hole live."

And then it does something rarer still — test_unauthorized_env_token_is_not_authorized asserts, in executable form, what the suite does not prove: the kernel's default environment token is local, local.omnimemory.* is not in the IAM-granted pattern set, and therefore "a green suite here… does NOT prove the deployed name is authorized; the deployment must set the env token to onex-dev."

A test whose subject is the boundary of its own evidence. No other report in this atlas records one.

The second mechanism is a real compare-and-set on lifecycle. handler_memory_expire.py transitions a memory with a WHERE clause carrying both the expected revision and the expected source state:

UPDATE memories
SET lifecycle_state = 'expired', expired_at = :now,
    lifecycle_revision = lifecycle_revision + 1, updated_at = :now
WHERE id = :memory_id
  AND lifecycle_revision = :expected_revision
  AND lifecycle_state = 'active'
-- If rows_affected == 0 -> conflict (another process updated first)

_VALID_FROM_STATES = frozenset({EnumLifecycleState.ACTIVE}) with a comment explaining the exclusion: "Including EXPIRED here would cause handle_with_retry() to keep retrying on already-expired memories."

And then the finding that governs the report. The Kafka handler that receives expire-memory and archive-memory commands does none of this:

"No-op handler: lifecycle orchestrator not yet wired."

create_lifecycle_dispatch_handler — the name without noop — returns create_lifecycle_noop_dispatch_handler. Its docstring records the change: "Previously a fail-fast handler that raised RuntimeError; now a no-op with structured logging so that upstream lifecycle commands… are gracefully acknowledged instead of crashing the service (OMN-2437)."

A RuntimeError was replaced by an acknowledgement. Expire and archive commands now succeed and do nothing.

2. Mental Model

There are two OmniMemories in this repository and they do not meet.

The specified one is excellent: typed enums, a frozen transition map, a compare-and-set adapter with a conflict result and a retry helper, an audit metadata model with old_value/new_value, and a gate suite that tests itself.

The running one acknowledges lifecycle commands without acting, builds retrieval from in-memory test doubles unless OMNIMEMORY_USE_STUB_HANDLERS is explicitly set to "false", and never constructs the audit model at all.

Diagram — the specified half and the running half of the same system: a frozen transition table and a compare-and-set exercised only by tests, dispatched to a handler that logs and acknowledges
Diagram source
%% caption: the specified half and the running half of the same system: a frozen transition table and a compare-and-set exercised only by tests, dispatched to a handler that logs and acknowledges
flowchart TD
    subgraph SPEC["specified"]
      LS["EnumLifecycleState:<br/>ACTIVE → STALE → EXPIRED → ARCHIVED → DELETED"]
      VT["VALID_TRANSITIONS, frozen<br/>STALE→ACTIVE and ARCHIVED→ACTIVE allowed<br/>DELETED terminal, no outbound"]
      CAS["UPDATE … WHERE revision = expected<br/>AND lifecycle_state = 'active'<br/>rows_affected == 0 → conflict"]
      AM["ModelAuditMetadata:<br/>old_value, new_value, ip_address, user_agent"]
      LS --> VT --> CAS
    end
    subgraph RUN["running"]
      K["Kafka: expire-memory, archive-memory, runtime-tick"]
      NO["create_lifecycle_dispatch_handler<br/>→ noop: log and acknowledge"]
      RET["retrieval config:<br/>OMNIMEMORY_USE_STUB_HANDLERS defaults 'true'"]
      K --> NO
    end
    CAS -. "exercised only by tests" .-> NO
    AM -. "no constructor anywhere in src" .-> X["never written"]
    G["tests/gates: default-deny AST walk<br/>+ checker-of-the-checker<br/>+ a test naming what the suite does not prove"]

3. Architecture

ONEX-compliant nodes, each with a contract.yaml, dispatched from Kafka topics derived by helper functions rather than literals. PluginMemory registers at the onex.domain_plugins entry point, wires message types, verifies handler imports and subscribes to topics.

The README is unusually clear about ownership boundaries — domain models, protocol interfaces, storage adapters and the runtime plugin stay; "the 15 runnable ONEX node handler implementations (those with contract.yaml) are moving to omnimarket". A repository that publishes which half of itself is leaving is doing its readers a service.

339 Python modules outside tests, 99 test files.

4. Essential Implementation Paths

Transitionnodes/node_memory_lifecycle_orchestrator/handlers/handler_memory_expire.py (_EXPIRE_SQL, _READ_STATE_SQL, _VALID_FROM_STATES, handle_with_retry).

Validatevalidators/validator_lifecycle_transition.py (VALID_TRANSITIONS as a MappingProxyType of frozensets, ModelTransitionValidationResult frozen with extra="forbid" and a reason).

Sweephandlers/handler_memory_tick.py: lifecycle_state = 'active' AND expires_at IS NOT NULL AND expires_at <= :now, then lifecycle_state = 'expired' AND archived_at IS NULL.

Dispatchruntime/dispatch_handlers.py create_lifecycle_noop_dispatch_handler and its aliasing wrapper.

5. Memory Data Model

lifecycle_state and lifecycle_revision on a memories table, with expired_at, archived_at and updated_at.

EnumLifecycleState is five states with the transitions written down: STALE can return to ACTIVE ("refreshed / promoted"), ARCHIVED can return to ACTIVE ("promoted / restored"), and DELETED has an empty frozenset — terminal, and described as "soft delete marker for audit trail". Revival paths named explicitly rather than left implicit is the right way to write a lifecycle.

EnumTrustLevel looks like a trust state and is not one. Its six levels are defined by score ranges — "UNTRUSTED: Score below 0.2, data should not be used" — and model_trust_score.py derives the level from the float in _score_to_level(). It is a confidence number with labels printed on it, which is the distinction this atlas's trust_state mark exists to draw.

6. Retrieval Mechanics

build_retrieval_config_from_env() reads OMNIMEMORY_USE_STUB_HANDLERS, default "true": "When the value is anything other than "false"… in-memory test doubles are used. Otherwise a ModelHandlerQdrantConfig is constructed."

So the default configuration retrieves from test doubles. That is a defensible choice for a platform component whose deployments configure themselves, and it is a fact a reader evaluating this as a memory system needs stated plainly.

lifecycle_state never appears in a retrieval query. It is read and written only by the lifecycle orchestrator's own sweep and transition SQL, so an EXPIRED or ARCHIVED memory is not filtered out on the read path by anything found here.

The composed read path

handler_memory_retrieval.py fans out to a semantic leg and a full-text leg and fuses their rankings rather than their scores, with the reason written down: fusing first "would let a leg that matched" on a stray term carry a document into the answer on one leg's strength alone. fuse_rrf_scores is reciprocal-rank fusion at RRF_K_DEFAULT = 60, weighted rather than plain, because on a paraphrase query the lexical leg can match a stray token and unweighted RRF would promote it.

handler_decay.py then modulates the fused score multiplicatively — A(t) = A_0 * exp(-lambda * dt) with DECAY_LAMBDA_PER_DAY = 0.015 — and the ordering is deliberate: decay is applied after fusion, "not as a third ranked list inside the fusion."

Two details are worth copying. The freshness input degrades per instance rather than per model: the plan specifies max(created_at, last_accessed_at), but the retrieval path returns a frozen ModelMemorySnapshot from a shared core package that carries only created_at, and the models that do declare last_accessed_at type it datetime | None, so freshest_timestamp falls back per record instead of assuming the field is populated. The docstring states the policy that follows from it — whether the created_at-only path costs ranking quality "is a question to answer with the fixture harness, not by assumption", and until that is measured no change to the shared core model is justified.

STALE_ACTIVATION_THRESHOLD = 0.3 is exported from the same module, with the stated reason that "ranking and the lifecycle orchestrator must agree on what 'stale' means". The agreement is not yet wired: rg -n "is_stale" src/ finds the name only in the module's own __all__, and the six committed cases that exercise it are the only callers. So staleness is a defined, tested predicate that nothing filters or expires on.

No memory-level scope key — tenant, user, project or agent — was found as a read filter. agent_id exists on persona and coordinator models, not as a retrieval predicate.

7. Write Mechanics

Writes go through ONEX nodes. Lifecycle changes go through the compare-and-set described above, which is a genuine lost-update guard: the WHERE clause carries the expected revision and the expected state, and zero affected rows is reported as conflict rather than swallowed. deactivate_with_retry re-reads the current revision and retries up to max_retries.

This is the pattern the atlas finds in only a handful of systems, and this implementation is the most carefully documented of them — including the reason EXPIRED is excluded from the valid source states.

Nothing calls it outside the test suite.

8. Agent Integration

Not an agent-facing memory in the usual sense: no MCP server, no CLI for a coding agent. It is a domain package inside a distributed platform, consumed by other ONEX services over Kafka, with a docker-compose.yml owning Qdrant, Memgraph, Valkey and Kreuzberg.

9. Reliability, Safety, and Trust

No marks.

Trust state — withheld. EnumTrustLevel is a binning of a float. EnumLifecycleState is a lifecycle with a real state machine, but it is not an epistemic status and it does not reach the read path.

Audit log — withheld, and this is the sharpest instance. models/foundation/model_audit_metadata.py defines AuditEventDetails with operation_type, resource_id, old_value, new_value, request_parameters, response_status, error_details, ip_address and user_agent, plus ResourceUsageMetadata and SecurityAuditDetails beside it. ModelAuditMetadata has no constructor anywhere in src. The schema is complete and the writer does not exist.

Scope, bitemporal, tombstone, human review, negative eval — no.

The no-op is the risk to name. Converting a fail-fast handler into a logged acknowledgement changes an upstream service's experience from a crash to a success. The commented reason — not crashing the service while integration is incomplete — is a real operational pressure, and the code is honest about it in its docstring. But the honesty lives in a docstring; the wire protocol says the expire succeeded.

10. Tests, Evals, and Benchmarks

No paper. No retrieval benchmark. 99 test files, mypy strict, ruff, pre-commit.

tests/gates/test_consumer_group_name_authorization.py is the artifact worth the visit, and it is worth it even if you never touch ONEX. Ten tests:

  • test_no_unmigrated_group_literals_in_src — the default-deny AST walk.
  • test_legacy_unmigrated_allowlist_is_empty — the escape hatch, asserted empty, so the exemption list cannot quietly grow.
  • Six test_checker_* cases proving the walk "catches literals, f-strings, and laundering while admitting real derivations", including test_checker_rejects_laundered_literal and test_checker_rejects_rebound_derived_name.
  • test_derived_memory_group_is_iam_authorized — positive and negative control against a pinned IAM pattern set vendored from Terraform, because "without the negative half a permissive translator would pass".
  • test_seam_scope_field_name_is_ephemeral_tag — pins a field name across a package boundary the seam table does not cover, "so a core-lane rename fails here loudly instead of surfacing as a runtime TypeError while both lanes' own suites stay green".
  • test_unauthorized_env_token_is_not_authorized — the test that states the suite's own limit.

I ran nothing.

11. For Your Own Build

Steal

  • Test the checker, not just the thing checked. "A default-deny gate whose discrimination is never exercised is indistinguishable from a gate that always passes." Every invariant you assert deserves a fixture proving it fails when violated — the atlas has read repositories with tens of thousands of lines of tests whose documented mutants are never executed.
  • Write a test for what your tests do not prove. Naming the residual in executable form — here, that a green suite says nothing about the deployed environment token — is worth more than a paragraph in a PR body, because the paragraph is not run.
  • Assert the escape hatch is empty. _LEGACY_UNMIGRATED "MUST stay empty at end state", enforced by its own test. An allowlist nobody checks is an allowlist that grows.
  • Put both the revision and the source state in the WHERE clause. WHERE id = ? AND lifecycle_revision = ? AND lifecycle_state = 'active', with rows_affected == 0 surfaced as a conflict, gives you optimistic concurrency and an illegal-transition guard in the same statement.
  • Write down why a state is excluded from a retry set. "Including EXPIRED here would cause handle_with_retry() to keep retrying on already-expired memories" is the comment that stops a future contributor from widening it.
  • Name the revival paths in the transition map. STALE→ACTIVE and ARCHIVED→ACTIVE spelled out, DELETED with an empty frozenset. Someone reading the map learns the policy.
  • Publish which half of your repository is leaving. The migration boundary document is a courtesy most large refactors skip.

Avoid

  • Do not convert a fail-fast into a silent acknowledgement. If the orchestrator is not wired, an upstream expire-memory should not receive success. Reject, dead-letter, or return an explicit not-implemented — the crash was information.
  • Do not name a function create_lifecycle_dispatch_handler when it returns the no-op. The delegation is one line and the docstring is honest, but the call site reads as the real thing.
  • Do not let a score wear a state's clothes. Six labels derived from a float by threshold are a float. If UNTRUSTED means "should not be used", something on the read path has to refuse it.
  • Do not ship an audit schema with no writer. old_value, new_value, ip_address and user_agent, fully specified and never constructed, reads to a reviewer as an audit trail that exists.
  • Do not default a storage adapter to stubs. OMNIMEMORY_USE_STUB_HANDLERS defaulting to "true" means the failure mode of a misconfigured deployment is a memory that silently forgets rather than one that fails to start.

Fit

This is a platform component, not a library to adopt. If you are not building on ONEX there is nothing here to install.

Read the gate file anyway. It is the best-argued test file in this atlas, and its central claim — that an unexercised gate and an absent gate are the same gate — applies to every invariant any system in this atlas asserts.

12. Open Questions

  • Is the lifecycle wired anywhere outside this repository? The 15 runnable nodes are migrating to omnimarket, which was not read.
  • What does OMN-2437 say? The ticket that turned the fail-fast into a no-op is referenced and not public.
  • Does anything consume EnumTrustLevel? Only model_trust_score.py references it; no consumer was found that refuses an UNTRUSTED item.
  • Is ModelAuditMetadata written by a sibling package? No writer exists here; whether one exists in omnibase_core was not established.

Appendix: File Index

The gatetests/gates/test_consumer_group_name_authorization.py (the rationale :1-58, the AST walk :263, the empty-allowlist assertion :285, the six checker controls :293-390, the seam pin :446, the residual test :472)

Lifecyclesrc/omnimemory/enums/enum_lifecycle_state.py, src/omnimemory/nodes/node_memory_lifecycle_orchestrator/validators/validator_lifecycle_transition.py (VALID_TRANSITIONS :63-96), .../handlers/handler_memory_expire.py (the SQL pattern :23-34, _EXPIRE_SQL :381, _VALID_FROM_STATES :401-404), .../handlers/handler_memory_tick.py, .../adapters/adapter_postgres_deactivate_memory.py

Runtimesrc/omnimemory/runtime/dispatch_handlers.py (create_lifecycle_noop_dispatch_handler :360, create_lifecycle_dispatch_handler :408, build_retrieval_config_from_env :440), src/omnimemory/runtime/contract_topics.py, src/omnimemory/runtime/plugin.py

Trustsrc/omnimemory/enums/enum_trust_level.py, src/omnimemory/models/foundation/model_trust_score.py (_score_to_level :161)

Auditsrc/omnimemory/models/foundation/model_audit_metadata.py

DocumentationREADME.md (the three ownership roles), docs/migrations/MARKET_MIGRATION_BOUNDARY.md

History

2026-09-096d340f89… — second reading, 54 commits along the default dev branch. Screened before reading; nothing was installed and no suite was run.

Every path this report's appendix names is byte-identical to the previous pin — git diff --stat <old>..HEAD -- over the gate, the lifecycle node and the runtime returns nothing, and every cited line anchor still resolves to the symbol it named. The commit subjects are dependency bumps, sibling-lock refreshes, CI gates and doc scrubbing.

An empty appendix diff is weak evidence that nothing happened, and here it was wrong. Three handlers arrived beside the paths the appendix lists — handler_memory_retrieval.py, handler_fusion.py and handler_decay.py, 437 lines together — carrying a composed read path the report did not describe: rank-level reciprocal-rank fusion at k = 60, weighted so a stray lexical match cannot carry a document alone, then a multiplicative activation decay at lambda = 0.015 per day applied after fusion rather than as a third ranked list. Section 6 now covers it.

The most useful thing in it is a refusal. The freshness input degrades per instance rather than per model, because the retrieval path returns a frozen snapshot from a shared core package that carries only created_at, and the module says why it declines to change that model: whether the fallback costs ranking quality "is a question to answer with the fixture harness, not by assumption."

No mark moves, and one near-miss is worth stating. STALE_ACTIVATION_THRESHOLD = 0.3 is exported so that "ranking and the lifecycle orchestrator must agree on what 'stale' means", and is_stale has six committed cases — but rg -n "is_stale" src/ finds the name only in its own __all__. It is a defined, tested predicate with no caller, so nothing filters or expires on it and trust_state stays withheld.

Tests grew by roughly 1,700 lines across activation decay, decay ranking, hybrid retrieval fusion with a committed corpus fixture, and a PII detector adapter with live-call-path coverage.

2026-08-095dacb73c… — first reading. Screened before reading; the tree was read, never installed, and no test was run.