Research-Stack/2-Search-Space/tardygrada/docs/plans/2026-04-04-frames-crdt-design.md

236 lines
7.6 KiB
Markdown

# 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