# Frames + CRDT + Datalog: Internal Consistency World Model > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. **Goal:** Replace probabilistic grounding with algebraic consistency checking. Frames define structure, CRDTs guarantee merge correctness, Datalog derives consequences. All deterministic, no external data. **Architecture:** Three layers that compose: 1. **Frames** (schema): what relationships exist and their constraints 2. **CRDT** (algebra): merge function derived from frame constraints 3. **Datalog** (inference): derive new facts from existing ones (already built) A new fact arrives -> frame match -> dry-run CRDT merge -> Datalog derivation -> CONSISTENT / CONFLICT / DERIVED. **Tech Stack:** C11, extends existing Datalog engine, ~300 lines new code. --- ## Data Model ```c // A frame slot: one relationship in a frame typedef struct { char name[64]; // slot name: "city", "country", "creator" char type[64]; // type constraint: "City", "Country", "Person" int functional; // 1 = one value per key, 0 = many values ok int required; // 1 = must be filled, 0 = optional } tardy_frame_slot_t; // A frame: structured expectation about a type of relationship typedef struct { char name[64]; // "Capital", "Creation", "Location" char predicate[64]; // which predicate this frame covers tardy_frame_slot_t slots[8]; // subject slot at [0], object slot at [1] int slot_count; int transitive; // 1 = locatedIn(X,Y) + locatedIn(Y,Z) -> locatedIn(X,Z) int symmetric; // 1 = relatedTo(X,Y) -> relatedTo(Y,X) int inverse; // 1 = has an inverse relationship char inverse_pred[64]; // inverse predicate name } tardy_frame_t; // Frame registry #define TARDY_MAX_FRAMES 64 typedef struct { tardy_frame_t frames[TARDY_MAX_FRAMES]; int count; } tardy_frame_registry_t; ``` ## CRDT Merge Semantics ```c typedef enum { TARDY_MERGE_OK, // merges cleanly, no conflict TARDY_MERGE_DUPLICATE, // already exists, no change TARDY_MERGE_CONFLICT, // violates functional dependency TARDY_MERGE_DERIVED, // already derivable from existing facts } tardy_merge_result_t; // Dry-run merge: would this fact conflict with existing state? // Does NOT modify state. Pure query. tardy_merge_result_t tardy_crdt_dry_merge( const tardy_frame_registry_t *frames, const tardy_dl_program_t *datalog, const tardy_dl_atom_t *new_fact); // Actual merge: add fact if consistent, reject if conflict tardy_merge_result_t tardy_crdt_merge( const tardy_frame_registry_t *frames, tardy_dl_program_t *datalog, const tardy_dl_atom_t *new_fact); ``` ## Synthetic Backbone Frames Pre-loaded at startup: ``` Frame: Capital predicate: capitalOf slots: [(city, City, functional=1), (country, Country, functional=1)] inverse: capitalCity -- One capital per country. One country per capital. Frame: Location predicate: locatedIn slots: [(entity, Thing, functional=0), (place, Place, functional=0)] transitive: yes inverse: contains -- Many things in one place. Containment is transitive. Frame: Creation predicate: creator slots: [(creation, Thing, functional=0), (agent, Agent, functional=0)] inverse: createdBy -- Things can have multiple creators. Frame: Founding predicate: founder slots: [(organization, Organization, functional=0), (agent, Agent, functional=0)] inverse: foundedBy Frame: Temporal predicate: dateCreated slots: [(thing, Thing, functional=1), (date, Date, functional=1)] -- Each thing has one creation date. Frame: Type predicate: type slots: [(instance, Thing, functional=0), (class, Class, functional=0)] transitive: no -- Multiple types per thing. Frame: Description predicate: description slots: [(thing, Thing, functional=0), (text, Text, functional=0)] Frame: KnownFor predicate: knownFor slots: [(agent, Agent, functional=0), (achievement, Thing, functional=0)] ``` ## Type Learning Types are NOT pre-defined. They emerge from usage. When the system sees `creator(Python, GuidoVanRossum)`: - Python is assigned type ?a - GuidoVanRossum is assigned type ?b - creator frame says slot[0] type is "Thing", slot[1] type is "Agent" - Unify: Python : Thing, GuidoVanRossum : Agent After 10 creator facts: - All slot[0] values are typed as Thing - All slot[1] values are typed as Agent - New claim `creator(Linux, LinusTorvalds)`: both slots type-check immediately Types are stored as Datalog facts: ``` type(Python, Thing). type(GuidoVanRossum, Agent). type(Paris, City). -- from capitalOf frame type(France, Country). -- from capitalOf frame ``` ## Verification Flow ``` Claim: "Berlin is the capital of Germany" Decomposed: capitalOf(Berlin, Germany) 1. FRAME MATCH: capitalOf -> Capital frame slots: (city=Berlin, country=Germany) 2. TYPE CHECK: - Germany: seen before? Yes, type=Country. Slot expects Country. OK. - Berlin: seen before? No. Slot expects City. UNRESOLVED. - Result: PARTIAL TYPE MATCH (1/2 slots confirmed) 3. DRY-RUN MERGE: - Capital frame is functional on country: one capital per country. - Does Germany already have a capital in the Datalog? No. - No conflict. Merge would succeed. - Result: MERGE_OK 4. DATALOG DERIVE: - capitalOf(Berlin, Germany) would trigger rule: locatedIn(Berlin, Germany) [from backbone rule] - No contradictions with existing facts. - Result: DERIVATION_CLEAN 5. FINAL VERDICT: - Frame: matched - Types: 1/2 confirmed (Germany=Country), 1/2 unresolved (Berlin=?City) - Merge: no conflict - Derivation: clean -> CONSISTENT (structurally valid, no conflicts, one unresolved type) ``` Compare with a FALSE claim: ``` Claim: "Tokyo is the capital of Germany" Decomposed: capitalOf(Tokyo, Germany) 1. FRAME MATCH: capitalOf -> Capital frame. OK. 2. TYPE CHECK: - Germany: type=Country. OK. - Tokyo: type=City (from capitalOf(Tokyo, Japan)). OK. 3. DRY-RUN MERGE: - If capitalOf(Berlin, Germany) was previously verified: Capital frame is functional on country. Germany already has Berlin. CONFLICT. - If NO capital for Germany exists: No conflict. But Tokyo already is capitalOf Japan. Capital frame is functional on city too (one country per capital). CONFLICT: Tokyo is already Japan's capital. 4. FINAL VERDICT: CONFLICT (functional dependency violation) ``` ## Integration with Existing Code The frame registry and CRDT merge replace the grounding confidence scoring: ``` Old: ground triples -> count evidence -> compute confidence -> threshold check New: ground triples -> frame match -> CRDT merge check -> CONSISTENT/CONFLICT/DERIVED ``` No probabilities. No thresholds. No confidence scores. Three deterministic outcomes. The pipeline layers still run but the grounding layer (Layer 2) changes from "count matching triples" to "check CRDT merge result." ## Implementation Tasks ### Task 1: Frame data structures + registry + backbone frames - Create: `src/ontology/frames.h`, `src/ontology/frames.c` - Load 8 backbone frames at startup ### Task 2: CRDT merge function - Implement `tardy_crdt_dry_merge` and `tardy_crdt_merge` - Check functional dependencies, type compatibility ### Task 3: Type learning from verified facts - When a fact is added, extract types from frame slot constraints - Store types as Datalog facts ### Task 4: Wire into verification pipeline - Replace confidence-based grounding with frame + CRDT check - Pipeline outputs: CONSISTENT / CONFLICT / DERIVED / UNRESOLVABLE ### Task 5: Integration test - Test chain reasoning, functional dep conflicts, type learning