-
Notifications
You must be signed in to change notification settings - Fork 0
ONTOLOGY
Complete data model for MONITOR: canonical graph, narrative documents, and semantic indices.
MONITOR uses three complementary data layers:
- Neo4j (Graph) - Canonical truth: what is objectively true in the universe
- MongoDB (Documents) - Narrative artifacts: scenes, turns, proposals, memories
- Qdrant (Vectors) - Semantic index: derived from MongoDB content
This ontology defines the complete data model across all three layers.
Key principle: Neo4j is the single source of truth. MongoDB proposes, Neo4j canonizes.
┌─────────────────────────────────────────────────────┐
│ QDRANT (Semantic Index - Derived) │
│ Embeddings → point to Neo4j IDs + MongoDB docs │
└─────────────────────────────────────────────────────┘
↑
indexes from
│
┌─────────────────────────────────────────────────────┐
│ MONGODB (Narrative Layer - Proposals + Artifacts) │
│ Scenes, Turns, ProposedChanges, Memories │
│ References canonical IDs from Neo4j │
└─────────────────────────────────────────────────────┘
│
proposes to
↓
┌─────────────────────────────────────────────────────┐
│ NEO4J (Canonical Layer - Truth) │
│ Universe, Entities, Facts/Events, Relations │
│ WITH provenance, confidence, canon_level │
└─────────────────────────────────────────────────────┘
(:Omniverse {
id: UUID,
name: string,
description: string,
created_at: timestamp
})Purpose: Top-level container for all multiverses. Cardinality: Usually 1 per MONITOR instance.
(:Multiverse {
id: UUID,
omniverse_id: UUID,
name: string,
system_name: string, // e.g., "D&D 5e", "Marvel 616"
description: string,
created_at: timestamp
})Purpose: Collection of related universes (e.g., all Marvel universes). Relations:
(:Omniverse)-[:CONTAINS]->(:Multiverse)
(:Universe {
id: UUID,
multiverse_id: UUID,
name: string,
description: string,
genre: string,
tone: string,
tech_level: string,
created_at: timestamp,
canon_level: enum["proposed", "canon", "retconned"]
})Purpose: A specific fictional universe where stories occur. Relations:
(:Multiverse)-[:CONTAINS]->(:Universe)
(:Source {
id: UUID,
universe_id: UUID,
doc_id: string, // MinIO/MongoDB reference
title: string,
edition: string,
provenance: string, // URL, ISBN, etc.
source_type: enum["manual", "rulebook", "lore", "session"],
created_at: timestamp,
canon_level: enum["proposed", "canon", "authoritative"]
})Purpose: Canonical reference to source materials.
Note on canon_level: Source nodes use a different enum than other canonical nodes:
-
proposed- Source uploaded but not yet verified -
canon- Source accepted as valid reference -
authoritative- Official/primary source (highest trust level, e.g., D&D PHB)
Sources don't use retconned because sources themselves aren't revised—only facts derived from them can be retconned.
Relations:
(:Universe)-[:HAS_SOURCE]->(:Source)(:Fact)-[:SUPPORTED_BY]->(:Source)(:Axiom)-[:SUPPORTED_BY]->(:Source)
(:Axiom {
id: UUID,
universe_id: UUID,
statement: string, // "Magic exists", "FTL travel impossible"
domain: string, // "physics", "magic", "society"
confidence: float, // 0.0-1.0
canon_level: enum["proposed", "canon", "retconned"],
authority: enum["source", "gm", "system"],
created_at: timestamp
})Purpose: Fundamental truths about how the universe works. Relations:
(:Universe)-[:HAS_AXIOM]->(:Axiom)-
(:Axiom)-[:SUPPORTED_BY]->(:Source)or(:Snippet)
Entity Hierarchy:
Entity (abstract)
├─ EntityArchetype (archetypes, concepts)
└─ EntityInstance (specific instances)
(:EntityArchetype {
id: UUID,
universe_id: UUID,
name: string, // "Wizard", "Orc", "Magic Sword"
entity_type: enum["character", "faction", "location", "object", "concept", "organization"],
description: string,
properties: map, // type-specific properties
canon_level: enum["proposed", "canon", "retconned"],
confidence: float,
created_at: timestamp
})Purpose: Archetypes, templates, universal concepts. Examples: "Wizard" (archetype), "The Force" (concept), "Orc" (species).
(:EntityInstance {
id: UUID,
universe_id: UUID,
name: string, // "Gandalf", "The One Ring", "Mordor"
entity_type: enum["character", "faction", "location", "object", "concept", "organization"],
description: string,
properties: map, // type-specific properties
state_tags: list, // ["alive", "wounded", "hostile"]
canon_level: enum["proposed", "canon", "retconned"],
confidence: float,
created_at: timestamp,
updated_at: timestamp
})Purpose: Specific instances that exist in the universe. Examples: "Gandalf the Grey", "The One Ring", "Mordor".
Relations:
-
(:EntityInstance)-[:DERIVES_FROM]->(:EntityArchetype)// optional (:EntityInstance)-[:LOCATED_IN]->(:EntityInstance)(:EntityInstance)-[:MEMBER_OF]->(:EntityInstance)(:EntityInstance)-[:ALLY_OF]->(:EntityInstance)(:EntityInstance)-[:ENEMY_OF]->(:EntityInstance)(:EntityInstance)-[:OWNS]->(:EntityInstance)
Canonical Reference: See ENTITY_TAXONOMY.md for complete specifications of:
- Type-specific properties for each
entity_type(character, faction, location, object, concept, organization) - State tag conventions and domains
- Example property values and validation rules
The properties map contains type-specific fields defined in ENTITY_TAXONOMY.md.
(:Story {
id: UUID,
universe_id: UUID,
title: string,
story_type: enum["campaign", "arc", "episode", "one_shot"],
theme: string,
premise: string,
status: enum["planned", "active", "completed", "abandoned"],
start_time_ref: timestamp, // in-universe time
end_time_ref: timestamp,
created_at: timestamp,
completed_at: timestamp
})Purpose: Canonical record of a story/campaign. Relations:
(:Universe)-[:HAS_STORY]->(:Story)-
(:Story)-[:PARENT_STORY]->(:Story)// arcs within campaigns -
(:Story)-[:HAS_SCENE]->(:Scene)// optional (scenes can be MongoDB-only)
(:Scene {
id: UUID,
story_id: UUID,
title: string,
purpose: string,
order: int,
time_ref: timestamp, // when it occurred in-universe
created_at: timestamp
})Purpose: Canonical scene marker (optional - can be MongoDB-only). When to create: Only if scene needs to be part of timeline/continuity. Relations:
(:Story)-[:HAS_SCENE]->(:Scene)-
(:Scene)-[:NEXT]->(:Scene)// ordering (:EntityInstance)-[:PARTICIPATED_IN]->(:Scene)
(:Fact {
id: UUID,
universe_id: UUID,
statement: string, // "Gandalf defeated the Balrog"
time_ref: timestamp, // when it became true
duration: int, // how long it was true (optional)
confidence: float, // 0.0-1.0
canon_level: enum["proposed", "canon", "retconned"],
authority: enum["source", "gm", "player", "system"],
created_at: timestamp,
replaces: UUID // if this retcons another fact
})Purpose: Canonical facts about the universe. Examples: "PC took 5 damage", "Door is broken", "NPC is hostile".
Relations:
(:Fact)-[:INVOLVES]->(:EntityInstance)-
(:Fact)-[:SUPPORTED_BY]->(:Source)or(:Scene)or MongoDB Turn ref (as property) -
(:Fact)-[:REPLACES]->(:Fact)// retcons
(:Event {
id: UUID,
scene_id: UUID,
title: string,
description: string,
time_ref: timestamp,
severity: int, // 0-10
confidence: float,
canon_level: enum["proposed", "canon", "retconned"],
authority: enum["source", "gm", "player", "system"],
created_at: timestamp
})Purpose: Causal moments in narrative (more specific than Fact). Examples: "Orc attacks PC", "PC casts fireball", "Building collapses".
Relations:
-
(:Event)-[:CAUSES]->(:Event)// causal DAG (:Event)-[:INVOLVES]->(:EntityInstance)-
(:Event)-[:SUPPORTED_BY]->(:Source)or(:Scene)or Turn ref
(:PlotThread {
id: UUID,
story_id: UUID,
title: string,
thread_type: enum["main", "side", "character", "mystery"],
status: enum["open", "advanced", "resolved", "abandoned"],
created_at: timestamp
})Purpose: Cross-scene narrative threads. Relations:
(:Story)-[:HAS_THREAD]->(:PlotThread)(:PlotThread)-[:ADVANCED_BY]->(:Scene)(:PlotThread)-[:INVOLVES]->(:EntityInstance)
MongoDB stores narrative artifacts, proposals, and working memory.
Collection: scenes
{
_id: ObjectId,
scene_id: UUID, // canonical ID (may or may not exist in Neo4j)
story_id: UUID, // references Neo4j Story
universe_id: UUID, // references Neo4j Universe
title: string,
purpose: string,
status: enum["active", "finalizing", "completed"],
// Canonical references
order: int, // optional ordering within the Story
location_ref: UUID, // optional EntityInstance ID
participating_entities: [UUID], // EntityInstance IDs
// Narrative content
turns: [
{
turn_id: UUID,
speaker: enum["user", "gm", "entity"],
entity_id: UUID, // if speaker is entity
text: string,
timestamp: ISODate,
resolution_ref: UUID // optional
}
],
// Canonization workflow
proposed_changes: [UUID], // references proposed_changes collection
canonical_outcomes: [UUID], // Fact/Event IDs written to Neo4j
// Summary for retrieval
summary: string,
created_at: ISODate,
updated_at: ISODate,
completed_at: ISODate
}
Index: { story_id: 1, order: 1 }
Index: { status: 1 }
Index: { scene_id: 1 } uniqueEmbedded in scenes, or can be separate collection for very long scenes:
Collection: turns (optional separate collection)
{
_id: ObjectId,
turn_id: UUID,
scene_id: UUID,
speaker: enum["user", "gm", "entity"],
entity_id: UUID, // if in-character
text: string,
proposed_changes: [UUID], // optional proposed_changes refs
resolution_ref: UUID, // optional resolution record
timestamp: ISODate
}
Index: { scene_id: 1, timestamp: 1 }Collection: proposed_changes
{
_id: ObjectId,
proposal_id: UUID,
scene_id: UUID,
turn_id: UUID, // which turn proposed this (optional for ingest/system proposals)
type: enum["fact", "entity", "relationship", "state_change", "event"],
content: {
// Structure depends on type
// Examples:
// fact: { statement: "...", entity_ids: [...] }
// entity: { name: "...", entity_type: "...", properties: {...} }
// relationship: { from: UUID, to: UUID, rel_type: "...", properties: {...} }
// state_change: { entity_id: UUID, state_tag: "...", value: ... }
},
evidence: [
{
type: enum["turn", "snippet", "source", "rule"],
ref_id: UUID
}
],
confidence: float, // 0.0-1.0
authority: enum["source", "gm", "player", "system"],
status: enum["pending", "accepted", "rejected"],
rationale: string, // why accepted/rejected
canonical_id: UUID, // if accepted, the Neo4j node/edge ID
created_at: ISODate,
evaluated_at: ISODate
}
Index: { scene_id: 1, status: 1 }
Index: { status: 1 }
Index: { proposal_id: 1 } uniqueCollection: resolutions
{
_id: ObjectId,
resolution_id: UUID,
turn_id: UUID,
scene_id: UUID,
action: string, // player intent
resolution_type: enum["dice", "narrative", "deterministic"],
mechanics: {
// dice: { formula: "1d20+5", roll: 18, target: 15 }
// narrative: { outcome: "success" }
},
success_level: enum["critical_success", "success", "partial", "failure", "critical_failure"],
effects: [
{
type: string, // "damage", "state_change", "discovery"
description: string,
magnitude: float
}
],
created_at: ISODate
}
Index: { scene_id: 1 }
Index: { resolution_id: 1 } uniqueCollection: character_memories
{
_id: ObjectId,
memory_id: UUID,
entity_id: UUID, // whose memory (references Neo4j EntityInstance)
text: string, // "I remember you saved my life"
linked_fact_id: UUID, // optional anchor to canonical Fact
scene_id: UUID, // where memory originated
emotional_valence: float, // -1.0 to 1.0
importance: float, // 0.0-1.0
certainty: float, // 0.0-1.0 (can misremember)
created_at: ISODate,
last_accessed: ISODate,
access_count: int
}
Index: { entity_id: 1, importance: -1 }
Index: { memory_id: 1 } uniqueCollection: documents
{
_id: ObjectId,
doc_id: UUID,
source_id: UUID, // references Neo4j Source
universe_id: UUID,
minio_ref: string, // MinIO object ID
title: string,
filename: string,
file_type: string, // "pdf", "epub", etc.
extraction_status: enum["pending", "extracting", "completed", "failed"],
created_at: ISODate,
extracted_at: ISODate
}
Index: { doc_id: 1 } unique
Index: { source_id: 1 }Collection: snippets
{
_id: ObjectId,
snippet_id: UUID,
doc_id: UUID,
source_id: UUID,
text: string,
page: int,
section: string,
chunk_index: int,
created_at: ISODate
}
Index: { doc_id: 1, chunk_index: 1 }
Index: { snippet_id: 1 } uniqueCollection: character_sheets
{
_id: ObjectId,
character_sheet_id: UUID,
entity_id: UUID, // references Neo4j EntityInstance
stats: map, // system-specific stats
resources: map, // HP, MP, etc.
history_log: [
{
timestamp: ISODate,
change: string,
scene_id: UUID
}
],
created_at: ISODate,
updated_at: ISODate
}
Index: { entity_id: 1 } uniqueCollection: story_outlines
{
_id: ObjectId,
story_id: UUID, // references Neo4j Story
theme: string,
premise: string,
constraints: [string],
beats: [
{
title: string,
description: string,
order: int
}
],
open_threads: [string],
created_at: ISODate,
updated_at: ISODate
}
Index: { story_id: 1 } uniqueQdrant stores embeddings with metadata pointing to canonical sources.
Collection: scene_chunks
{
"id": "uuid",
"vector": [float],
"payload": {
"scene_id": "uuid",
"story_id": "uuid",
"universe_id": "uuid",
"text": "summary or turn text",
"type": "scene_summary | turn",
"timestamp": "iso8601"
}
}Collection: memory_chunks
{
"id": "uuid",
"vector": [float],
"payload": {
"memory_id": "uuid",
"entity_id": "uuid",
"text": "memory text",
"importance": float,
"timestamp": "iso8601"
}
}Collection: snippet_chunks
{
"id": "uuid",
"vector": [float],
"payload": {
"snippet_id": "uuid",
"doc_id": "uuid",
"source_id": "uuid",
"universe_id": "uuid",
"text": "snippet text",
"page": int,
"section": "string"
}
}Critical: Qdrant is never authoritative. All IDs point to Neo4j or MongoDB.
| Layer | Can Reference | Cannot Reference |
|---|---|---|
| Neo4j | Neo4j nodes only | MongoDB _id, Qdrant IDs |
| MongoDB | Neo4j UUIDs (as properties) | Neo4j internal IDs |
| Qdrant | Neo4j UUIDs + MongoDB UUIDs | - |
| Entity Type | Primary Store | Proposal Store | Who Writes |
|---|---|---|---|
| Universe | Neo4j | MongoDB (proposals) | CanonKeeper |
| Fact/Event | Neo4j | MongoDB (ProposedChange) | CanonKeeper |
| Entity | Neo4j | MongoDB (ProposedChange) | CanonKeeper |
| Scene | MongoDB | - | Orchestrator |
| Turn | MongoDB | - | Narrator |
| ProposedChange | MongoDB | - | Resolver, Narrator |
| Memory | MongoDB | - | MemoryManager |
| Embedding | Qdrant | - | Indexer |
Narrative → ProposedChange → CanonKeeper → Neo4j
(MongoDB) (MongoDB) (evaluates) (commits)
Every canonical node in Neo4j MUST link to evidence:
-
(:Fact)-[:SUPPORTED_BY]->(:Source)or -
(:Fact {evidence_refs: ["scene:uuid", "turn:uuid"]})stored as property
Data flow:
- MongoDB: create story_outline
- Neo4j: create Story node
- MongoDB: create first Scene (status=active)
- Neo4j: optionally create Scene node (if canonical)
Data flow:
- MongoDB: append Turn to scene.turns
- MongoDB: optionally create ProposedChange records
- No Neo4j writes (deferred to canonization)
Data flow:
- CanonKeeper: evaluate ProposedChanges
- Neo4j: write accepted Facts/Events/Relations
- Neo4j: create SUPPORTED_BY edges
- MongoDB: mark scene.status = "completed"
- MongoDB: update proposal.status = "accepted"/"rejected"
- Qdrant: embed scene summary + memories
Data flow:
- MinIO: store document
- MongoDB: create document record
- MongoDB: create snippet records
- Qdrant: embed snippets
- MongoDB: create ProposedChanges (axioms/entities)
- User review → CanonKeeper → Neo4j (canonize accepted)
Data flow:
- Qdrant: semantic search → candidate IDs
- Neo4j: fetch canonical nodes by ID
- MongoDB: fetch narrative details (if needed)
- Present results
- Separated Neo4j from MongoDB - v1.0 assumed single graph model
- Added canonization metadata - confidence, canon_level, authority
- Added Source nodes - provenance tracking
- Split Entity → EntityArchetype + EntityInstance
- Added Fact (distinct from Event)
- Added ProposedChange flow (staging before canon)
- Scene can be Neo4j or MongoDB-only - not always canonical
- Added evidence/provenance - SUPPORTED_BY relations
- Add new entity_types
- Add new relationship types
- Add properties to existing nodes (backward compatible)
- Change canonization_level enum values
- Restructure Entity hierarchy
- Change primary keys
To implement this ontology:
- Neo4j schema constraints (UUIDs, required properties)
- MongoDB schema validation (JSON Schema)
- Qdrant collection creation with proper indices
- Cross-reference validation (ensure UUIDs exist)
- Migration scripts from v1.0 (if applicable)
- API contracts per layer
- Agent read/write authority enforcement
- DATABASE_INTEGRATION.md - Data layer architecture
- CONVERSATIONAL_LOOPS.md - Loop state machines
- AGENT_ORCHESTRATION.md - Agent roles and coordination