Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Database Schema

Single SQLite DB for everything: ~/.vis/vis.mdb/rlm.db.

Schema source of truth: resources/db/migration/V1__schema.sql.

Flyway migration location: classpath:db/migration.

Entity Tree

graph TD
    CS[conversation_soul] --> CST[conversation_state]
    CST --> QS[query_soul]
    CST --> ES[expression_soul<br/>var / call / literal]
    QS --> QST[query_state]
    QST --> IT[iteration]
    IT --> EST[expression_state<br/>code execution results]
    ES --> ED[expression_dependency<br/>directed edges]
    LOG[log] -.->|optional FKs| CS
    LOG -.-> QS
    LOG -.-> IT
    LOG -.-> ES
    SEARCH[search FTS5] -.->|triggers| QS
    SEARCH -.-> EST

Tables

1) conversation_soul

Conversation identity.

ColumnTypeNotes
idTEXT PK
metadataTEXTJSON-encoded
created_atINTEGER

Index: idx_conv_soul_created(created_at DESC)

2) conversation_state

Forkable mutable state for a conversation soul.

ColumnTypeNotes
idTEXT PK
conversation_soul_idTEXT FKconversation_soul.id, cascade delete
parent_state_idTEXT FKconversation_state.id, cascade delete
titleTEXT
versionINTEGER>= 0
metadataTEXTJSON-encoded
created_atINTEGER

Constraints: UNIQUE(conversation_soul_id, version)

3) query_soul

Immutable identity of a user ask (branch-local).

ColumnTypeNotes
idTEXT PK
conversation_state_idTEXT FKconversation_state.id, cascade delete
titleTEXT
queryTEXT
metadataTEXTJSON-encoded
created_atINTEGER

Index: idx_query_soul_state(conversation_state_id, created_at)

4) query_state

One run/retry state for query_soul.

ColumnTypeNotes
idTEXT PK
query_soul_idTEXT FKquery_soul.id, cascade delete
forked_from_query_state_idTEXT FKquery_state.id, set null on delete
versionINTEGER>= 0
llm_providerTEXT
llm_root_modelTEXT
prompt_enrichmentTEXT
subtitleTEXT
run_labelTEXT
statusTEXTrunning|done|error|interrupted
metadataTEXTJSON-encoded
created_atINTEGER

Constraints: UNIQUE(query_soul_id, version)

5) iteration

One LLM round-trip inside a query_state.

ColumnTypeNotes
idTEXT PK
query_state_idTEXT FKquery_state.id, cascade delete
positionINTEGER>= 0
statusTEXTrunning|done|error|interrupted
llm_system_promptTEXT
llm_user_promptTEXTmultimodal JSON envelope
llm_providerTEXT
llm_modelTEXT
llm_responseTEXTfinal selected LLM response
llm_tracesTEXTall LLM attempts/traces
llm_full_duration_msINTEGERnullable, >= 0
llm_thinkingTEXT
llm_errorTEXT
llm_returned_empty_expressionsINTEGER0/1, default 0
metadataTEXTJSON — active extensions, etc.
created_atINTEGER
finished_atINTEGERnullable

Constraints: UNIQUE(query_state_id, position)

Iteration Metadata

The metadata column stores per-iteration context as JSON:

{"extensions": [
  {"namespace": "com.blockether.vis.ext.editing",
   "version": "0.1.0"},
  {"namespace": "com.acme.ext.git",
   "version": "2.3.0"}
]}

Records which extensions (with full source namespace and version) were active when the iteration ran, enabling post-mortem analysis and reproducibility.

6) expression_soul

Branch-local identity for var/call/literal expression nodes.

ColumnTypeNotes
idTEXT PK
conversation_state_idTEXT FKconversation_state.id, cascade delete
kindTEXTvar|call|literal
state_modeTEXTstateless|stateful
nameTEXTnullable
metadataTEXTJSON-encoded
created_atINTEGER

Constraints: CHECK(kind <> 'literal' OR state_mode = 'stateless')

Unique partial index: uq_expression_soul_state_name(conversation_state_id, name) WHERE name IS NOT NULL

7) expression_dependency

Directed dependency edges between expression souls.

ColumnTypeNotes
idTEXT PK
conversation_state_idTEXT FKconversation_state.id, cascade delete
downstream_expression_soul_idTEXT FKexpression_soul.id, cascade delete
upstream_expression_soul_idTEXT FKexpression_soul.id, cascade delete
metadataTEXTJSON-encoded
created_atINTEGER

Constraints: CHECK(downstream <> upstream), UNIQUE(downstream, upstream)

Triggers enforce same conversation_state_id across endpoints.

8) expression_state

Versioned expression output snapshots emitted per iteration.

ColumnTypeNotes
idTEXT PK
expression_soul_idTEXT FKexpression_soul.id, cascade delete
iteration_idTEXT FKiteration.id, cascade delete
versionINTEGER>= 0
successINTEGER0/1, default 1
exprTEXTnullable, non-blank when set
resultBLOBNippy-encoded
errorBLOBNippy-encoded
stdoutTEXT
stderrTEXT
duration_msINTEGERnullable, >= 0
metadataTEXTJSON-encoded
created_atINTEGER

Constraints: UNIQUE(expression_soul_id, version), CHECK((success=1 AND error IS NULL) OR (success=0 AND error IS NOT NULL))

Triggers enforce: first version = 0, stateless expressions only get version 0.

9) log

Structured logs with optional scope references.

ColumnTypeNotes
idTEXT PK
levelTEXTtrace|debug|info|warn|error|fatal
eventTEXTmachine-stable event key
dataTEXTJSON-encoded
conversation_soul_idTEXT FKnullable
conversation_state_idTEXT FKnullable
query_soul_idTEXT FKnullable
query_state_idTEXT FKnullable
iteration_idTEXT FKnullable
expression_soul_idTEXT FKnullable
expression_state_idTEXT FKnullable
created_atINTEGER

Scoped partial indexes for each nullable FK.

10) search (FTS5)

Full-text search virtual table.

ColumnNotes
owner_tableunindexed
owner_idunindexed
fieldunindexed
textindexed

Tokenizer: porter unicode61 remove_diacritics 2

Indexed sources via triggers:

  • query_soul.query
  • expression_state.expr

Persistence Rules

  1. All DB code lives under persistance/* — nowhere else.
  2. HoneySQL only — no raw SQL outside persistance/sqlite/*.clj.
  3. Callers use persistance/core.clj — never import sqlite/core.clj directly.
  4. Schema changes in V1__schema.sql MUST update this page.
  5. If this doc and SQL diverge, V1__schema.sql is authoritative — fix the doc.