Files
openhuman/docs/tinycortex-api-gap-audit.md
T

12 KiB

TinyCortex API Gap Audit (Phase 0.2)

Status (2026-07-22): Historical gap inventory for the completed engine cutover. Counts and source anchors below describe the audit base; unresolved host consolidation work, including G1 re-homing and sync dedupe, is tracked in tinycortex-migration-plan-2026-07-22.md.

Purpose. Map every host call site into the engine-mapping modules (memory_store, memory_tree, memory_queue, and the long tail) onto a TinyCortex public API. Where no crate API covers a host need, record a gap — each gap becomes a tinycortex issue/PR (engine-side) or an explicit host-adapter decision, and gates the workstream that touches it.

Anchors: host 7850cf363 · tinycortex d1a8c7be (v0.1.1). Crate API surface inventoried from vendor/tinycortex/src/memory/** (pub traits/fns/types, verbatim signatures).

Call-site volume (host → engine, external references only)

External refs = uses of memory_<mod>::… from host code outside that module.

Engine module External refs Maps to crate Coverage
memory_store 454 store/, chunks/ High — see per-API below
memory_tree 169 tree/, retrieval/, score/ High (gaps G3, G5)
memory_queue 42 queue/ High (drift D2)
memory_sources 35 sources/ Full (local readers only)
memory_conversations 24 conversations/ Full (drift D1)
memory_tools 12 tool_memory/ Full
memory_diff 11 diff/ Full
memory_archivist 9 archivist/ Full
memory_goals 6 goals/ Full
memory_search 1 retrieval/, score/ tools stay host
memory_entities 0 entities/ Full
memory_graph 0 graph/ Derive-on-read (gap G2)

Highest-traffic memory_store sub-APIs (external): chunks::store (152), chunks::types (93), trees::types (54), create_memory (31), trees::store (27), profile (26), types (19), content (19), content::compose (14), content::paths (10), content::read (9), content::atomic (8), content::raw (7). All of chunks::*, trees::*, content::*, profile map to crate re-exports (chunks::…, tree::…, store::content::…, config::WeightProfile). create_memory is host glue (see below).

Seam-trait roster (the W1 adapter targets — all present in crate)

The crate exposes every seam the plan expects, plus more. Host implements these:

Trait Path Host adapter (W1)
EmbeddingBackend store/vectors/embedding.rs:32 tinycortex/embeddings.rs over openhuman::embeddings (amended: bridged to tinyagents::harness::embeddings::EmbeddingModel in W-EMB, plan §8.2)
Embedder (retrieval/summarise) score/embed.rs:28 same seam file
ChatProvider score/extract/llm.rs:70 tinycortex/chat.rs over memory::chat / inference
Summariser (bucket-seal, structured) tree/summarise.rs:70 tinycortex/chat.rs
Summariser (time-tree, string) tree/runtime/engine.rs:29 divergent — see G6
EntityExtractor (regex + LLM) score/extract/composite.rs:16 tinycortex/chat.rs (LLM variant)
QueueDelegates queue/handlers.rs:96 tinycortex/queue_driver.rs
TreeJobSink ingest/types.rs:15 tinycortex/ingest.rs
TreeLeafSink archivist/sink.rs:44 tinycortex/ingest.rs
SnapshotItemSource diff/source.rs:28 host diff adapter
EntityOccurrenceIndex graph/types.rs:44 host graph adapter
SelfIdentity store/entity_index/store.rs:25 host identity registry
GoalsGenerator goals/reflect.rs:57 tinycortex/chat.rs
SourceReader sources/readers/mod.rs:1732 local readers only (amended: live-sync readers join the crate in W-SYNC, plan §8)
SyncEventSink (new, W-SYNC) sync/traits.rs (planned) tinycortex/sync_sink.rsMemorySyncStage bus events
SkillDocSink (new, W-SYNC) sync/traits.rs (planned) forwards to MemoryClient::store_skill_sync (host-retained unified tier)
ConversationEventBus / ChannelEventHandler conversations/bus.rs:106/95 host memory_conversations/bus.rs (translate to DomainEvent)

Config seam: MemoryConfig{workspace, embedding: EmbeddingConfig{dim,model,strict}, tree: TreeConfig{input_token_budget,output_token_budget,summary_fanout,flush_age_secs}, retrieval: RetrievalConfig{default_profile}, sync_budget: SyncBudgetConfig} + WeightProfile{graph,vector,keyword,freshness} — all pub with the exact fields the W1 config.rs adapter maps Config onto.


Gaps (each → tinycortex issue/PR or explicit host-adapter decision)

G1 — Raw SQLite connection escape hatch (sqlite_conn()) — HARD, high blast radius

  • Host: two trait methods return a live handle: memory/traits.rs:277 and memory_store/memory_trait.rs:458 fn sqlite_conn(&self) -> Option<Arc<Mutex<Connection>>>. ~312 references to sqlite_conn/raw_conn/with_connection across the host.
  • Crate: deliberately omits it (traits.rs:3-6). Offers instead a scoped accessor chunks::with_connection(config, |conn| …) (chunks/mod.rs:217) over the cached handle; conn_cache() is private.
  • Decision (W2): do not upstream a handle-getter (it breaks the crate's connection-cache invariants). Instead: (a) migrate host internal raw-SQL sites to chunks::with_connection during W3; (b) keep sqlite_conn() as a host-side extension trait over the crate store for the transition, shrinking to zero as sites migrate. Track residual call sites in the deletion ledger. Gates W2/W3.

G2 — Graph relation-edge persistence (E2GraphRAG accumulation) — HARD

  • Host: memory_store/namespace_store/{graph,query}.rs persist and query co-occurrence / LLM-triple relations; NamespaceMemoryHit.supporting_relations: Vec<GraphRelationRecord> is populated at retrieval.
  • Crate: persists the entity occurrence index at persist time (score::persist_score/persist_score_tx, score/mod.rs:847-906) but does not write co-occurrence graph edges — docstring score/mod.rs:843-846: "Co-occurrence graph edges (OpenHuman's E2GraphRAG accumulation) are not written here." The graph module derives edges on read from the occurrence index (graph/query.rs, "owns no state, performs no writes"). GraphRelationRecord exists as a type but nothing in the crate writes it.
  • Decision (W6/W7): verify whether derive-on-read reproduces the host's persisted-relation retrieval (supporting_relations) with parity. If yes → host relation store stays host (namespace doc/graph store is explicitly host per plan §1); if the crate must own accumulation → upstream a relation-persist path. Gates W6/W7. Parity-critical for retrieval output.

G3 — Seal-time embedding — MEDIUM (parity timing)

  • Host: computes summary embeddings around seal (memory_tree/tree/bucket_seal.rs, memory_tree/score/embed/*, set_summary_embedding).
  • Crate: bucket_seal.rs:21 / tree/mod.rs:22 list "seal-time embedding" as not ported; seal_one_level computes labels but never embeds. Summary embedding runs later via the queue's reembed_batch delegate. The mem_tree_summary_embeddings sidecar + setters exist.
  • Decision (W5): confirm retrieval parity on freshly-sealed summaries — if the host embeds at seal but the crate defers to the queue, a summary is momentarily unembedded until the worker drains. Either drive an embed job at seal from the host queue driver, or upstream seal-time embedding. Gates W5.

G4 — Post-seal notification sink (for host wiki_git mirror) — SOFT

  • Host: 6395f642e wired a seal-time hook in bucket_seal.rs to commit summary markdown to the git-backed wiki_git mirror (host-retained, per the drift ledger).
  • Crate: no observer/sink trait for seal events — but append_leaf / cascade_all_from / TreeFactory::seal_now return Vec<String> of sealed summary ids. The host can drive wiki_git from the returned ids without a callback.
  • Decision (W3/W5): no crate change required; the host queue driver calls wiki_git after each seal using returned ids. Record as a seam-wiring task, not an upstream gap. Revisit only if a push-notification is later wanted.

G5 — Tree health / doctor (storage-degraded state machine) — SOFT (host-retained)

  • Host: memory_tree/health/* (mark_storage_degraded/clear_storage_degraded, degraded flag), consumed by memory/tools/doctor.rs and fed by the D2 host-FS classifier.
  • Crate: defers tree health/doctor entirely (tree/mod.rs:20); only Memory::health_check() -> bool. Queue uses its own JobFailure (no storage-degraded machine).
  • Decision (W5): host-retained (like wiki_git). memory_tree::health stays host; the D2 predicate (queue classifier) upstreams, its Sentry-once + degraded-flag consumers stay host.

G6 — Two divergent Summariser traits — MEDIUM (ambiguity)

  • Crate has two traits named Summariser: tree/summarise.rs:70 (structured batch fold — summarise(&[SummaryInput], &SummaryContext) -> SummaryOutput) used by bucket-seal, and tree/runtime/engine.rs:29 (string in/out — summarise(Option<&str>, &str) -> String) used by the markdown time-tree runtime. They are distinct contracts with the same name.
  • Decision (W1/W5): the host seam must implement both. Flag for possible upstream unification (rename one, e.g. TimeTreeSummariser) to reduce confusion — non-blocking. Note which tree path OpenHuman actually drives (bucket-seal SQLite trees vs markdown time-tree) so we don't wire a dead seam.

Non-gaps (expected host glue — no crate change)

Host API Refs Disposition
memory_store::create_memory / create_memory_with_local_ai (factories.rs:297,322) 31 Host glue building UnifiedMemory. Crate has no UnifiedMemory/MemoryClient (by design). W3 re-implements these factories over tinycortex::store + chunks, keeping the host-facing MemoryClient API stable. Not a crate gap.
memory_store::profile (unified::profile) 26 Maps to config::WeightProfile (fields graph/vector/keyword/freshness, by_name).
memory_store::fts / keyword search 7 Crate provides retrieval::keyword_relevance + hybrid_score (not SQLite FTS5). Verify keyword-recall parity in W5 (behavioral, not a missing API).
Namespace document/graph store Explicitly host-retained per plan §1 until/unless upstreamed (ties to G2).

Gap → workstream gate summary

Gap Severity Gates Resolution path
G1 escape hatch High W2/W3 Migrate to with_connection; host extension trait for transition
G2 graph relations High W6/W7 Parity check derive-on-read; upstream persist iff needed
G3 seal-time embedding Medium W5 Drive embed at seal from host queue driver, or upstream
G4 seal notification Soft W3/W5 Drive wiki_git from returned sealed ids (no crate change)
G5 tree health/doctor Soft W5 Host-retained
G6 two Summariser traits Medium W1/W5 Implement both; optional upstream rename

No hard gap blocks W1 (seam scaffolding) or W4 (queue, only drift D2). W3 is gated by G1; W5 by G3/G6; W6/W7 by G2. These are recorded here and mirrored in the spec's deletion ledger.

Amendment 2026-07-09 (plan §8): the crate's sync data model (SourceKind::Composio, SyncBudgetConfig, the TOML source registry with upsert_composio_source / memory_sync_defaults_for_toolkit) — previously host-consumed only — becomes engine-consumed by W-SYNC: the crate's own sync engine reads the registry and enforces the budget. New seam traits SyncEventSink / SkillDocSink land with W-SYNC.1 (see roster above); no other new gaps.