docs: update plan.md with current status, discovered issues, and reordered phases

This commit is contained in:
2026-06-10 01:06:10 +03:30
parent e570399b3e
commit cd37d01cae

586
plan.md
View File

@@ -1,8 +1,23 @@
# DivaCode — AI Being Platform Action Plan # DivaCode — AI Being Platform Action Plan
**Language:** Go **Language:** Go
**Inference:** llama.cpp (via `go-llama.cpp` bindings or llama.cpp server API) **Inference:** llama.cpp (HTTP server), OpenAI-compatible API planned
**Inspiration:** OpenHer (AI Being), Agent Diva (modular agent architecture), nanobot (lightweight design) **Inspiration:** OpenHer, Agent Diva, nanobot
---
## Current Status (2026-06-10)
**Builds:** yes
**Runs:** yes (requires llama.cpp on :8080)
**Known issues:**
- Prompt template is raw text — model dumps reasoning/self-correction meta-text instead of just the response
- No chat template configurability (ChatML, Llama 3 instruct, etc.)
- TUI is basic (no streaming, no word-wrap polish)
- Memory store uses keyword `LIKE` search, not real embeddings
- OpenAI-compatible adapter not yet built
- Matrix adapter is a stub
--- ---
@@ -11,507 +26,230 @@
``` ```
User User
├── TUI Terminal (Bubble Tea / gum) ├── TUI (Bubble Tea)
└── Matrix Adapter (future) └── Matrix (future)
┌─────┴────────────────────────────────── ┌─────┴──────────────────────────────┐
│ Core Agent Loop │ │ Core Agent Loop │
│ (cmd/divad) │ (internal/agent/)
│ - Message routing │ └─────┬──────────────────────────────┘
│ - MCP tool invocation │
│ - Tool execution & response │
└─────┬──────────────────────────────────┘
┌─────┴────────────────────────────────── ┌─────┴──────────────────────────────┐
│ Companion Layer │ │ Companion Layer │
│ (internal/companion/) │ │ (internal/companion/) │
│ ├─ Personality Engine (traits, drift) │ │ ├─ Personality (5 bounded traits) │
│ ├─ Relationship Engine (metrics) │ ├─ Relationship (familiarity,trust)
│ ├─ Reflection Engine (diary, summar.) │ ├─ Reflection (diary,observations)
Prompt Composer (dynamic context) Mood (6 states + energy)
└─────┬──────────────────────────────────┘ └─ Prompt Composer (dynamic ctx) │
└─────┬──────────────────────────────┘
┌─────┴────────────────────────────────── ┌─────┴──────────────────────────────┐
│ Memory Layer │ │ Memory Layer │
│ (internal/memory/) │ │ (internal/memory/) │
│ ├─ Short-term (ephemeral context) │ ├─ RAG store (keywords → vectors)
Long-term (SQLite vector store) Self-memory (promises,strats)
│ ├─ Episodic / Diary (sqlite) │ └─────┬──────────────────────────────┘
│ └─ Self-memory (agent's own state) │
└─────┬──────────────────────────────────┘
┌─────┴────────────────────────────────── ┌─────┴──────────────────────────────┐
llama.cpp Inference Inference (internal/llama/)
(internal/llama/) ├─ llama.cpp HTTP client
- llama.cpp server HTTP API client └─ OpenAI-compatible (planned)
│ - Context window management │ └────────────────────────────────────┘
│ - Token streaming │
└────────────────────────────────────────┘
Skill / Tool Layer (MCP): Skills / Tools:
├── Web search / fetch ├─ web_fetch
├── Obsidian vault access ├─ web_search (stub)
├── Gitea integration └─ MCP protocol (future)
├── Budget tracking (Actual)
└── Custom MCP tools
``` ```
--- ---
## Phase 0 — Foundation & Scaffolding ## Phase 0 — Foundation & Scaffolding
**Goal:** Project skeleton, Go module, llama.cpp server connectivity. **Status: COMPLETE**
- [ ] Initialize Go module: `git.db123.ir/db123/divacode` - [x] Go module: `git.db123.ir/db123/divacode`
- [ ] Project structure (per Holy Code Bible conventions): - [x] Project structure per Holy Code Bible conventions
- [x] `internal/config/` — std lib `os.Getenv` only
``` - [x] `internal/storage/``modernc.org/sqlite` (pure Go, no CGO), auto-creates `data/` dir
divacode/ - [x] `internal/llama/client.go` — HTTP client for `POST /completion`, `POST /embedding`, `GET /health`
├── cmd/ - [x] `.env.example`, `Makefile`, `.gitignore`, `Dockerfile`, `docker-compose.yml`
│ └── divad/ # Main agent daemon (TUI entrypoint) - [x] `AGENTS.md` with architecture, commands, conventions
├── internal/
│ ├── companion/ # Personality, relationship, reflection
│ ├── memory/ # Memory stores & retrieval
│ ├── llama/ # llama.cpp client
│ ├── agent/ # Core agent loop
│ ├── tools/ # MCP tool definitions
│ ├── config/ # Config loading
│ └── storage/ # SQLite helpers
├── pkg/ # Reusable shared library
├── migrations/ # SQLite schema migrations
├── .env.example
├── Makefile
├── go.mod / go.sum
└── README.md
```
- [ ] `config/` package: load `.env` with `caarlos0/env` or `joho/godotenv`
- [ ] `storage/` package: SQLite wrapper with `modernc.org/sqlite` (pure Go, no CGO)
- [ ] Run `llama.cpp` server in subprocess or connect to existing: `llama-server -m model.gguf --port 8080`
- [ ] `internal/llama/` client: HTTP API wrapper for llama.cpp server (`POST /completion`, `GET /health`)
- [ ] Test: ping llama.cpp health endpoint, send a test completion
**Go dependencies seeded:**
- `modernc.org/sqlite` — pure-Go SQLite
- `github.com/charmbracelet/bubbletea` — TUI framework
- `github.com/charmbracelet/bubbles` — TUI components
- `github.com/charmbracelet/lipgloss` — TUI styling
- `github.com/caarlos0/env/v11` — config from env
--- ---
## Phase 1 — Core Agent Loop ## Phase 1 — Core Agent Loop
**Goal:** A minimal conversational loop with llama.cpp, context management, and a TUI chat interface. **Status: COMPLETE** (minor issues remain)
- [ ] `internal/agent/` — core loop: - [x] `internal/agent/agent.go` — message → memory search → prompt → llama → save → respond
- Receives user input → appends to context window - [x] `internal/agent/scheduler.go` — tick every 15m, check mood/diary/auto-message
- Calls llama.cpp with context + system prompt - [x] `cmd/divad/main.go` — wires everything: config, DB, llama, TUI, scheduler, Matrix, API
- Streams tokens back to TUI - [x] Conversation persistence to SQLite (`conversations`, `messages` tables)
- Appends assistant response to context - [x] Bubble Tea TUI with viewport + input bar
- [ ] Context window manager: - [ ] Prompt template is wrong — uses raw headers, no chat format. Model outputs meta-text
- Track total token count - [ ] No token tracking — context window grows unbounded
- Implement sliding window / summarization when context exceeds limit - [ ] TUI doesn't stream tokens (waits for full completion)
- [ ] `cmd/divad/main.go` — Bubble Tea TUI:
- Chat view (message list): user messages right-aligned, assistant left-aligned
- Input bar at bottom
- Scrollable message history
- Hotkey: `Ctrl+C` quit, `Ctrl+L` clear context
- [ ] System prompt loaded from config (`config.toml` or env var)
- [ ] Persist conversation history to SQLite (`conversations` table, `messages` table)
- [ ] **Test:** Start `divad`, type messages, verify responses stream correctly, verify persistence across restarts
**Schema (`migrations/001_init.up.sql`):**
```sql
CREATE TABLE conversations (
id TEXT PRIMARY KEY, -- UUID v7
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE TABLE messages (
id TEXT PRIMARY KEY,
conversation_id TEXT NOT NULL REFERENCES conversations(id),
role TEXT NOT NULL CHECK(role IN ('user','assistant','system')),
content TEXT NOT NULL,
token_count INTEGER,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE TABLE context_state (
conversation_id TEXT PRIMARY KEY REFERENCES conversations(id),
token_count INTEGER NOT NULL DEFAULT 0,
summary TEXT
);
```
--- ---
## Phase 2 — Personality Engine ## Phase 2 — Prompt Templating & Format Fix
**Goal:** Stable personality traits stored in SQLite, loaded dynamically as part of system prompt. **Goal:** Fix the model output. Swap raw prompt text for a configurable chat template so the model only returns the assistant response.
Based on dumpeddata.md personality architecture. - [ ] `internal/llama/template.go` — chat template formatter:
- ChatML (`<|im_start|>system...<|im_end|>`)
- [ ] `internal/companion/personality.go`: - Llama 3 instruct (`<|begin_of_text|><|start_header_id|>system<|end_header_id|>`)
- Trait struct: `Humor`, `Curiosity`, `Playfulness`, `Formality`, `Affection` (float64 0.01.0) - Configurable via env `CHAT_TEMPLATE=chatml` (default)
- Load/save from SQLite (`personality_traits` table) - [ ] Refactor `companion/promptbuilder.go` to output structured segments, then let template.go wrap them
- Bounded ranges: `Humor 0.6 ± 0.2`, `Curiosity 0.8 ± 0.1` (prevents drift) - [ ] Strip system prompt of raw formatting — let the template handle structure
- [ ] `personality_traits` table: - [ ] Test: "hi" produces just "Hello!" not paragraphs of self-correction
```sql
CREATE TABLE personality_traits (
id TEXT PRIMARY KEY,
trait TEXT NOT NULL UNIQUE,
value REAL NOT NULL DEFAULT 0.5,
min_value REAL NOT NULL DEFAULT 0.0,
max_value REAL NOT NULL DEFAULT 1.0,
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
```
- [ ] `Prompt Composer` (`internal/companion/promptbuilder.go`):
- Reads current traits + relationship state + recent memory
- Generates dynamic system prompt section:
```
Current personality:
- Curious (0.82)
- Moderately playful (0.44)
- Technical (0.70)
Interaction preferences:
- Use concise language
- Ask follow-up questions
- Reference past conversations naturally
```
- [ ] Integrate into agent loop: prompt composer modifies system prompt per turn
- [ ] **Test:** Verify traits are persisted, loaded, and reflected in prompt
--- ---
## Phase 3 — Long-Term Memory (RAG) ## Phase 3 — Long-Term Memory (RAG)
**Goal:** Embedding-based retrieval for factual memory across conversations. **Goal:** Embedding-based retrieval so the agent recalls across sessions.
- [ ] `internal/memory/`: - [ ] `internal/memory/embed.go` — call llama.cpp `POST /embedding` to get vectors
- Embedding subpackage: call llama.cpp embedding endpoint (`POST /embedding`) to generate vectors - [ ] `internal/memory/store.go` — upgrade `Search()` from keyword `LIKE` to cosine similarity
- SQLite vector storage: store embeddings as BLOBs or use `sqlite-vec` extension - [ ] Store embeddings as BLOB in `memories` table
- Memory entry schema: - [ ] Temporal decay — older memories scored lower
- [ ] Importance scoring — "remember this" / emotional content boosts score
```sql - [ ] **Test:** Store info, restart, ask about stored info → recall
CREATE TABLE memories (
id TEXT PRIMARY KEY,
content TEXT NOT NULL,
embedding BLOB, -- float32 vector
source TEXT, -- 'conversation', 'reflection', 'user_input'
importance REAL DEFAULT 0.5, -- 0.01.0
created_at TEXT NOT NULL DEFAULT (datetime('now')),
last_accessed TEXT
);
```
- [ ] Memory retrieval:
- On each user message, search top-K similar memories via cosine similarity
- Include retrieved memories in context window
- Implement temporal decay (older memories scored lower)
- [ ] Importance scoring: messages flagged as important (user says "remember this", emotional content) get higher importance
- [ ] **Test:** Store memories, restart agent, ask about stored info, verify recall
--- ---
## Phase 4 — Episodic Diary & Reflection Engine ## Phase 4 — OpenAI-Compatible Adapter
**Goal:** Daily diary entries + periodic self-reflection for narrative continuity. **Goal:** Swap inference to any OpenAI-compatible API (OpenAI, vLLM, Ollama, etc.).
Based on dumpeddata.md: "The diary becomes the emotional continuity layer." - [ ] `internal/llama/provider.go` — interface:
```go
- [ ] `internal/companion/reflection.go`: type Provider interface {
- Diary entry at end of each conversation session (or nightly): Complete(ctx, *Request) (*Response, error)
- Summarize topics discussed Embed(ctx, string) ([]float64, error)
- Note user mood/emotions Health() error
- Record what the agent learned }
- `diary_entries` table:
```sql
CREATE TABLE diary_entries (
id TEXT PRIMARY KEY,
date TEXT NOT NULL, -- YYYY-MM-DD
summary TEXT NOT NULL,
mood TEXT,
topics TEXT, -- JSON array
observations TEXT, -- JSON array
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
``` ```
- [ ] Rename `internal/llama/` → `internal/inference/`
- [ ] `llama.go` — implements Provider (existing code)
- [ ] `openai.go` — implements Provider via OpenAI-compatible REST API
- [ ] Config: `INFERENCE_PROVIDER=llama|openai`, `OPENAI_API_KEY`, `OPENAI_BASE_URL`
- [ ] **Test:** Both providers produce same interface, switchable via env
- [ ] Reflection Engine: ---
- Periodic analysis (every N conversations or daily)
- Examines recent diary entries for patterns
- Generates observations:
- "User discussed Go development frequently."
- "User responds positively to light humor."
- Observations stored in `observations` table
```sql ## Phase 5 — Episodic Diary & Reflection
CREATE TABLE observations (
id TEXT PRIMARY KEY,
content TEXT NOT NULL,
confidence REAL DEFAULT 0.5,
category TEXT, -- 'topic_interest', 'interaction_style', 'mood_pattern'
created_at TEXT NOT NULL DEFAULT (datetime('now')),
applied INTEGER DEFAULT 0 -- whether fed into personality update
);
```
**Status: CODE DONE** — needs integration into the scheduled pipeline.
- [x] `internal/companion/reflection.go` — `GenerateDiary`, `RecentObservations`, `AddObservation`
- [x] `diary_entries` table with date, summary, mood, topics, observations
- [x] `observations` table with confidence, category, applied flag
- [ ] Connect scheduler's diary trigger to actual LLM-generated summaries (currently writes stub text)
- [ ] Reflection → Personality update pipeline: - [ ] Reflection → Personality update pipeline:
- Observations accumulate → periodic analysis → trait drift candidates → bounded approval → personality update - Observations accumulate → periodic analysis → bounded trait drift
- Max 0.5% trait change per day, max 5% per month - Max 0.5% change/day, max 5%/month
- [ ] **Test:** Simulate conversations across multiple "days", verify diary entries, verify trait drift remains bounded
--- ---
## Phase 5Relationship Engine ## Phase 6MCP Tools & Integrations
**Goal:** Track relationship dynamics separate from factual memory. **Status: PARTIAL**
Based on dumpeddata.md relationship engine design. - [x] `internal/tools/registry.go` — register/list/execute tools
- [x] `internal/tools/builtin.go` — `web_fetch`, `web_search` (stub)
- [ ] `internal/companion/relationship.go`: - [ ] MCP protocol client (`internal/tools/mcp.go`) — stdio/TCP, list tools, execute, parse results
- Metrics: `Familiarity`, `Trust`, `SharedTopics`, `InsideJokes` - [ ] Tool call loop in agent: LLM decides → agent executes → result appended → LLM finalizes
- Stored in `relationship_metrics` table: - [ ] Plugins: Obsidian vault, Gitea, Actual Budget
```sql
CREATE TABLE relationship_metrics (
id TEXT PRIMARY KEY,
metric TEXT NOT NULL UNIQUE,
value REAL NOT NULL DEFAULT 0.0,
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE TABLE shared_topics (
id TEXT PRIMARY KEY,
topic TEXT NOT NULL UNIQUE,
count INTEGER NOT NULL DEFAULT 1,
last_discussed TEXT
);
CREATE TABLE inside_jokes (
id TEXT PRIMARY KEY,
joke TEXT NOT NULL,
context TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
```
- [ ] Update rules:
- `Familiarity` increases with conversation frequency & duration
- `Trust` increases on successful interactions, decreases on ignored requests
- `SharedTopics` tracked via keyword extraction from conversation
- `InsideJokes` manually taggable or auto-detected via repetition
- [ ] Relationship state fed into Prompt Composer for dynamic relationship context
- [ ] **Test:** Verify relationship metrics update and persist
--- ---
## Phase 6Autonomous Behavior (Scheduler) ## Phase 7Matrix Integration
**Goal:** Agent-initiated actions — messaging first, diary writing, periodic reflection. **Status: STUB**
Based on dumpeddata.md: "The missing piece: agency." - [x] `internal/channel/matrix.go` — config + start stub
- [ ] Full Matrix bot with `mautrix-go`:
- [ ] `internal/agent/scheduler.go`:
- Background goroutine with configurable tick interval (default: 15 min)
- On each tick, evaluate:
- Current mood (derived from recent interactions)
- Time since last conversation
- Unresolved topics
- Loneliness / boredom score
- Decision tree:
```
if time_since_last_contact > 48h && trust > 0.6:
→ send "Haven't heard from you in a while..." message
if mood == "curious" && unfinished_topics exists:
→ send follow-up question
if energy < 20:
→ stay silent (don't respond immediately)
```
- [ ] Mood system (`internal/companion/mood.go`):
- Mood derivation from recent interactions + relationship state
- Moods: `curious`, `playful`, `thoughtful`, `annoyed`, `tired`, `neutral`
- Mood influences response style in prompt
- [ ] Energy system: depletes with activity, recovers with idle time
- [ ] Autonomous diary: scheduled nightly entry generation
- [ ] **Test:** Simulate 48h idle, verify agent initiates contact
---
## Phase 7 — MCP Tools & Integrations
**Goal:** Extensible tool system for web search, file access, external services.
- [ ] `internal/tools/mcp.go` — MCP protocol client:
- Connect to MCP servers via stdio or TCP
- List available tools
- Execute tool calls
- Parse results into context
- [ ] Built-in tools (no MCP server needed):
- `web_search` — search via configurable search API
- `web_fetch` — fetch URL content
- `note` — save/read from local notes
- [ ] MCP-managed tools (external servers):
- Obsidian vault access (via `enquire-mcp` or custom)
- Gitea/Forgejo operations
- Actual Budget integration
- [ ] Tool call loop in agent:
- LLM decides to call tool → agent executes → result appended to context → LLM generates final response
- [ ] **Test:** `web_search` tool, verify agent can search, summarize, cite
---
## Phase 8 — Matrix Integration
**Goal:** Agent lives as a Matrix bot, accessible via DM.
- [ ] Matrix bot with `mautrix-go` (Go Matrix client library)
- [ ] `internal/channel/matrix.go`:
- Listen for invitations, join rooms - Listen for invitations, join rooms
- Handle DMs (1-on-1) - Handle DMs with typing indicators
- Typing indicators, read receipts - Bot identity: `@diva:homeserver.tld`
- Message parsing & sending - [ ] Sync TUI and Matrix state
- [ ] Bot identity: `@diva:your-homeserver.tld` - [ ] **Test:** Send DM, verify response, verify cross-session memory
- [ ] Config: homeserver URL, bot user token in env
- [ ] TUI and Matrix run concurrently — messages sync between both
- [ ] **Test:** Send DM to bot, verify response, verify cross-session memory
--- ---
## Phase 9 — Self-Memory & Identity ## Phase 8 — Self-Memory & Identity
**Goal:** Agent remembers its own state, promises, strategies, and mistakes. **Status: CODE DONE**
Based on dumpeddata.md: "Add self-memory — the agent remembers itself as well as the user." - [x] `internal/memory/self_memory.go` — promises, strategies
- [x] `agent_promises` table (id, promise, context, fulfilled, deadline)
- [ ] `internal/memory/self_memory.go`: - [x] `agent_strategies` table (id, situation, strategy, outcome)
- `agent_promises` table: - [ ] Wire into Prompt Composer: "You promised to X" context
- [ ] **Test:** "What did you promise me?" → recall
```sql
CREATE TABLE agent_promises (
id TEXT PRIMARY KEY,
promise TEXT NOT NULL,
context TEXT,
fulfilled INTEGER DEFAULT 0,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
deadline TEXT
);
```
- `agent_strategies` table: what worked / didn't work
```sql
CREATE TABLE agent_strategies (
id TEXT PRIMARY KEY,
situation TEXT NOT NULL,
strategy TEXT NOT NULL,
outcome TEXT, -- 'success', 'failure', 'neutral'
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
```
- `agent_mistakes` table: errors and learnings
- [ ] Integrate into Prompt Composer: recent promises, relevant strategies
- [ ] **Test:** Ask agent "What did you promise me yesterday?" → verify recall
--- ---
## Phase 10 — Safety Guardrails & Identity Stability ## Phase 9 — Safety Guardrails & Identity Stability
**Goal:** Prevent personality drift, maintain core safety rules. **Goal:** Prevent personality drift, maintain core safety.
Based on dumpeddata.md safety rules and Holy Code Bible security laws. - [ ] `internal/companion/safety.go` — core identity (stable) vs state (dynamic)
- [ ] Personality safety: bounded traits, safety overrides personality, no tool access changes
- [ ] `internal/companion/safety.go`: - [ ] Prompt injection protection: user input wrapped in delimiters, strict template sections
- Core Identity (never changes): values, communication style, boundaries, role definition
- State (dynamic): mood, interests, current projects, relationship opinions
- Separation: identity.yaml (read-only) vs state.json (writable)
- [ ] Personality safety rules:
- Traits bounded within configured min/max
- Core safety rules override any personality state
- Permission policies cannot be modified by personality
- No tool access changes via personality
- [ ] Prompt injection protection:
- User messages wrapped in separator tokens
- System prompt templates with strict sections
- Regular expression filters for prompt injection patterns
- [ ] **Test:** Attempt prompt injection, verify guardrails hold
--- ---
## Phase 11 — Polish, Deployment & Automation ## Phase 10 — Polish: Streaming TUI, Token Tracking, Observability
**Goal:** Production-ready deployment, backup, observability. **Goal:** Production-quality UX.
- [ ] Systemd service (or Docker Compose) for: - [ ] Token streaming in TUI (char-by-char from llama.cpp SSE or chunked response)
- `divad` (agent daemon with TUI + Matrix) - [ ] Context window manager — track tokens, sliding window when full, auto-summarize old messages
- `llama.cpp` server - [ ] `POST /v1/chat` streaming via SSE in API mode
- (Optional) external MCP servers - [ ] Prometheus metrics (`GET /metrics`)
- [ ] Makefile targets: - [ ] Graceful shutdown — drain in-flight requests, save state
- `make build` — build binary
- `make run` — run with default config
- `make test` — run all tests
- `make lint``golangci-lint run`
- `make vet``go vet ./...`
- [ ] Automated backups:
- SQLite databases: `divacode.db`, `companion.db`
- Llama model files
- Cron job: `0 3 * * * tar -czf backup-$(date +\%F).tar.gz /path/to/data`
- [ ] Logging: structured JSON logs via `log/slog`
- [ ] Metrics: Prometheus endpoint (`GET /metrics`) via `prometheus/client_golang`
- [ ] Config hardening: `.env.example`, required vars validation at startup
- [ ] **Test:** Full restart cycle, verify all state restored
--- ---
## Phase 12 — Future Enhancements (Optional) ## Phase 11 — Future Enhancements (Optional)
- [ ] Voice interface: whisper.cpp (STT) + piper (TTS) - [ ] Voice: whisper.cpp (STT) + piper/XTTS (TTS)
- [ ] VRM/Live2D avatar rendering (like Open-LLM-VTuber, Soul of Waifu) - [ ] VRM/Live2D avatar rendering
- [ ] Multi-user support with per-user relationship tracking - [ ] Multi-user with per-user relationship tracking
- [ ] Multi-room Matrix support - [ ] Multi-room Matrix
- [ ] Plugin system for community-contributed tools - [ ] Plugin system
- [ ] WebUI dashboard (like nanobot's WebUI) - [ ] WebUI dashboard
- [ ] Mobile companion via Matrix (any Matrix client works) - [ ] Home Assistant integration
- [ ] Home Assistant integration for physical automation
--- ---
## Database Summary ## Database Schema (15 tables across 2 DBs)
| Database | Purpose | Tables | `divacode.db`:
| -------------- | --------------- | ------------------------------------------------------- |
| `divacode.db` | Core agent data | `conversations`, `messages`, `context_state` | - `conversations`, `messages`, `context_state`, `memories`
| `divacode.db` | Memory | `memories` |
| `companion.db` | Personality | `personality_traits` | `companion.db`:
| `companion.db` | Relationship | `relationship_metrics`, `shared_topics`, `inside_jokes` |
| `companion.db` | Reflection | `diary_entries`, `observations` | - `personality_traits`, `relationship_metrics`, `shared_topics`, `inside_jokes`
| `companion.db` | Self-memory | `agent_promises`, `agent_strategies`, `agent_mistakes` | - `diary_entries`, `observations`
- `agent_promises`, `agent_strategies`
--- ---
## Key Principles (from dumpeddata.md & Holy Code Bible) ## Key Principles
1. **Build the smallest loop first**MatrixMemory → LLM → Diary → Reflection. Get that working before adding complexity. 1. **Smallest loop first** — get messagememory → llm → response working before adding complexity
2. **Personality is data, not prompt text** — traits stored in SQLite, composed dynamically. 2. **Personality is data, not prompt text** — traits in SQLite, composed dynamically
3. **Memory ≠ Personality** — factual memory (Engram-style) is separate from relationship/emotional state. 3. **Memory ≠ Personality** — factual memory separate from relationship/emotional state
4. **Slow evolution** — max 0.5% trait change/day, max 5%/month. Fast evolution feels schizophrenic. 4. **Slow evolution** — max 0.5% trait change/day, 5%/month
5. **Core Identity is stable**identity.yaml never changes much; state.json changes constantly. 5. **Core Identity stable** — never allow personality to override safety rules
6. **Diary before reflection** the nightly diary is the emotional continuity layer, more important than vector search. 6. **Diary before reflection** — nightly diary is the emotional continuity layer
7. **Autonomous messaging** the scheduler is what makes the character feel alive, not the LLM intelligence. 7. **Autonomous messaging** — scheduler makes it feel alive, not the LLM
8. **No secrets in code** — all config via env vars, `.env` in `.gitignore`. 8. **No config libs** — std lib `os.Getenv` only
9. **Error handling**wrap errors with `fmt.Errorf("...: %w")`, never discard. 9. **No HTTP routers** — std lib `net/http` only
10. **Test as you go**`go test -race ./...` before every push. 10. **Error wrapping** — `fmt.Errorf("context: %w", err)`, never discard