flowchart TD
NOTE["Memlink's LIVE type system, taken from the code — memlink_hybrid/domain/enums.py + the world.* migrations.<br/>Three CLOSED vocabularies (entity kinds · relation types · fact slots), plus what every object carries.<br/>The bottom shows how memory is WRITTEN — and the one place the vocabularies still drift from each other."]
ROOT["Memory object · the world.* graph<br/>every durable object carries: kind · lane · status · confidence · provenance"]
NOTE ~~~ ROOT
subgraph KINDS["Object kinds · MemoryObjectKind (6)"]
direction TB
K1["episode"]
K2["fact · world.facts"]
K3["entity · world.entities"]
K4["relation · world.relations"]
K5["consolidation"]
K6["semantic_support"]
K1 ~~~ K2 ~~~ K3 ~~~ K4 ~~~ K5 ~~~ K6
end
ROOT --> K1
subgraph ENT["Entity kinds · EntityKind (24) · domain/enums.py"]
direction TB
E1["People + contact · person · housemate · friend · contact_method"]
E2["Places + orgs · place · employer · work_site · team"]
E3["Time · schedule · calendar_item"]
E4["Work / coding · project · task · issue · decision · repo · service · environment · deployment · dependency · milestone"]
E5["Knowledge · document · incident · lesson · rule"]
E1 ~~~ E2 ~~~ E3 ~~~ E4 ~~~ E5
end
K3 --> E1
subgraph REL["Relation types · RelationType (31) · ONE source of truth — every accept-list DERIVED from it"]
direction TB
R1["supplemental — text-grounded, the LLM + family extractors emit these (19)<br/>housemate_of · friend_of · lives_with · works_for · works_at · member_of · contact_for · sibling_of · parent_of · spouse_of · lives_in · works_with · partner_of · knows · uses · located_in · operated_by · provider_of · associated_with (last resort)"]
R2["email — minted deterministically from headers, never text-grounded (9)<br/>sent · addressed_to · in · about · has_attachment · labeled · subscribed_to · replied_to · forwarded"]
R3["system / reserved — no extractor emits these (3) · supersedes · attends · shares"]
RB["19 supplemental + 9 email + 3 system = 31 = the world.relations.relation_type CHECK<br/>bitemporal: status + supersedes link · widening needs a migration first (HARD RULE 4)"]
R1 ~~~ R2 ~~~ R3 ~~~ RB
end
K4 --> R1
subgraph FACT["Fact slots · CanonicalSlot · WRITABLE = every slot EXCEPT relation_type"]
direction TB
F1["Legacy · birthday · location · timezone · employer · role · work_site · roster · rnr_location · housemate · friend · phone_number · email · important_event · contact_name · relation_type"]
F2["Identity · legal_name · pronouns · past_location · nationality · language_spoken · marital_status"]
F3["Contact · contact_method"]
F4["Relationships · partner · family_member · pet"]
F5["Professional · past_employer · past_role · skill · certification · education · business_entity"]
F6["Health · medical_condition · medication · allergy · blood_type"]
F7["Financial · financial_institution · obligation_recurring · income_source"]
F8["Life / prefs · interest_hobby · dietary_preference · vehicle · property"]
FC["Cardinality · SINGULAR (14, one current value per owner+lane) · birthday · location · timezone · employer · work_site · roster · rnr_location · contact_name · legal_name · pronouns · nationality · marital_status · partner · blood_type<br/>everything else is MULTI-VALUE — role is multi on purpose (ADR 0163)"]
F1 ~~~ F2 ~~~ F3 ~~~ F4 ~~~ F5 ~~~ F6 ~~~ F7 ~~~ F8 ~~~ FC
end
K2 --> F1
subgraph CROSS["Cross-cutting · carried on every object"]
direction TB
LANE["Lane · owner / shared / system / agent"]
STAT["Status · MemoryStatus · current / superseded / contradicted / archived"]
CONF["Confidence · ConfidenceState · grounded / provisional / contradicted / superseded"]
PROV["Provenance · world.object_provenance · object → evidence_quote → source_event_id"]
SRC["Ingest profile · chat_realtime / email_import / document_import / calendar_ingest / api_ingest"]
LANE ~~~ STAT ~~~ CONF ~~~ PROV ~~~ SRC
end
ROOT -.on every object.-> LANE
GATE["WRITABLE_FACT_SLOTS = every CanonicalSlot EXCEPT relation_type<br/>RFC 0099 RC3 / ADR 0159 — the ONE writable fact vocabulary (drift-proof, DERIVED)<br/>validate_fact_candidate: not in set ⇒ slot_not_writable · relation_type lives in world.relations, never as a fact"]
FACT -.governed by.-> GATE
subgraph WRITE["How memory is WRITTEN · world.events_raw → derivation → extract → SANITIZE → world.*"]
direction TB
EVT["world.events_raw — the ONE source every derivation reads"]
EXR["extractors — LLM supplemental extractor + deterministic family P11–P14<br/>each emits candidates, then a SANITIZER re-checks every candidate against an accept-set"]
OSL["fact slots — OFFER WRITABLE_FACT_SLOTS == ACCEPT WRITABLE_FACT_SLOTS · both DERIVED from one set · IN SYNC"]
ORL["relations — OFFER 21 (19 supplemental + owned_by / service_provider sub-kind remaps) == ACCEPT 19 supplemental · DERIVED · IN SYNC · test_relation_predicate_vocab_sync locks it"]
OEN["entities — OFFER all 24 (derived from EntityKind) → ACCEPT only 8 HAND-CODED · person · place · employer · work_site · contact_method · housemate · friend · decision<br/>the other 16 entity kinds are SILENTLY DROPPED at sanitize"]
EVT --> EXR
EXR --> OSL
EXR --> ORL
EXR --> OEN
end
OSL -.writes facts.-> K2
ORL -.writes relations.-> K4
OEN -.writes entities.-> K3
DRIFT["⚠ the ontology's ONE un-derived copy — the entity-kind accept-set is HAND-TYPED in dispatch_derivation_sanitization.py, NOT derived from EntityKind.<br/>Fact-slots and relation-types are both derived from a single source and test-locked, so they cannot drift — entities can. A new EntityKind is dropped at capture until this set is derived too."]
OEN --> DRIFT
style KINDS fill:#f4f1ff,stroke:#7c5cff,stroke-width:2px,color:#7c5cff
style ENT fill:#f4f1ff,stroke:#7c5cff,stroke-width:1px,color:#7c5cff
style REL fill:#f4f1ff,stroke:#7c5cff,stroke-width:1px,color:#7c5cff
style FACT fill:#f4f1ff,stroke:#7c5cff,stroke-width:2px,color:#7c5cff
style CROSS fill:#f1f5f9,stroke:#475569,stroke-width:2px,color:#475569
style WRITE fill:#f1f5f9,stroke:#475569,stroke-width:2px,color:#475569
classDef pN fill:#ffffff,stroke:#7c5cff,color:#27272a,stroke-width:1px
classDef slN fill:#ffffff,stroke:#475569,color:#27272a,stroke-width:1px
classDef gN fill:#fffaf0,stroke:#b45309,color:#27272a,stroke-width:2px
classDef gnN fill:#ecfdf3,stroke:#1f9d63,color:#27272a,stroke-width:1px
classDef rdN fill:#fdecec,stroke:#e5484d,color:#27272a,stroke-width:2px
class ROOT,K1,K2,K3,K4,K5,K6,E1,E2,E3,E4,E5,R1,R2,R3,RB,F1,F2,F3,F4,F5,F6,F7,F8,FC pN
class LANE,STAT,CONF,PROV,SRC,NOTE,EVT,EXR slN
class GATE gN
class OSL,ORL gnN
class OEN,DRIFT rdN