Architecture
System overview — the pipeline, tiers, data model, and sync design
System Overview
Kural is a five-stage pipeline that transforms source code into structural health scores:
Parse — Walk the filesystem, extract functions, types, and descriptions from the AST
The system is split into two tiers:
- CLI — scan, score, local snapshots
- Dashboard (web UI) — reactive views over snapshot data
Tech Stack
| Layer | Tool | Role |
|---|---|---|
| CLI framework | Gunshi | Command routing, plugin system |
| AI | Multiple providers (Vercel, OpenAI, OpenRouter, Ollama) | Embeddings, advise |
| Local persistence | TanStack DB + node-sqlite-persistence | Snapshot storage |
| Cloud backend | Turso (database-per-project) | active.db + history.db per project |
| Dashboard | TanStack DB (React) | Reactive UI over collections |
Data Model
KuralUnit (base for all)
Every parsed unit carries:
name,path,descriptionidentityEmbedding— name + description vectorleafEmbedding— name + description + structural signature vectorfacetHash— SHA256 for cache invalidation
Unit Types
- KuralFile — source files with functions, types, imports
- KuralType — interfaces, classes, type aliases with fields and references
- KuralFunction — functions with params, return types, purity, call graph
- KuralDirectory — directory hierarchy with children, descriptions from KURAL.md
Scores
Every node (types, functions, files, directories) gets a ScoreCard:
fit— how well the node's content matches its parent's identityuniqueness— mean distance to siblings (2.0 = N/A)score— harmonic mean of fit and uniquenesschildrenFit/childrenUniqueness/childrenScore— direct children quality (containers only)subtreeFit/subtreeUniqueness/subtreeScore— aggregate descendant health (containers only)overallScore— leaf: score. Container: harmonic mean of score and subtreeScoreworstPair— the most similar child pairbestUncle— the uncle node where this unit would fit better (name + score)
Snapshot System
Snapshot = Isolated SQLite Database
Each snapshot is a self-contained SQLite database file. This enables clone-and-mutate workflows — copy a snapshot, run analysis/simulation on the clone, discard when done. The original stays untouched.
Snapshot ID
Format: <timestamp>-<short-commit-hash>
1711700400-a3f8b2c- Sortable by time for history ordering
- Tied to code state via commit hash
- Deduplicable — same commit produces same hash suffix
Local Storage Layout
.kural-db/
<branch>/
active.db # current snapshot, clone-able
advise.db # ephemeral clone for AI simulation
history/ # rotated snapshots (max 10)
<snapshot-id>.dbServer Storage Layout
Turso (<user-id>.kural.io):
<project>/
<branch>/active.db # live, clone-able snapshot
history.db # consolidated, all branches + timestampsSnapshot Lifecycle
- CLI generates a snapshot for the current branch
- Active snapshot is rotated to
history/<snapshot-id>.db; a freshactive.dbis created - Oldest unpinned history snapshots are evicted when count exceeds 10
- On the server, only CI writes (via API); developers keep snapshots locally
Schema Design
Database Tables
| Table | Purpose |
|---|---|
files | Source files with embeddings, imports, descriptions |
types | Type/interface/class declarations with fields, refs |
functions | Functions with params, return types, purity, call graph |
directories | Directory hierarchy with children, descriptions |
scores | Structural health metrics per file/directory |
metadata | Key-value store (created_at, model_id, schema_version, axes) |
Schema Versioning
schema_versionis stored in every snapshot's metadata from day one- Additive-only evolution is the default — new nullable columns only, no removals or renames
- Old snapshots have NULLs for new columns; dashboard handles gracefully
- For rare breaking changes, a one-time migration is applied to
history.db
Embedding Model Versioning
model_idis stored in snapshot metadata for context- Scores are precomputed numbers — no cross-snapshot vector comparison needed
- If the model changes and scores shift, the dashboard shows the shift;
model_idexplains why
Sync Architecture
Write Path (CI Only)
CI pipeline -> POST <user-id>.kural.io/api/snapshots
|
v
API validates, deduplicates, writes to Turso:
- active.db (replace if newer commit)
- history.db (append)- Developers never push to the server — local only
- CI is the sole writer via authenticated API
- API is the single writer to Turso — no direct client access
Concurrency Handling
- Parallel CI runs for different branches: no conflict
- Parallel CI runs for same branch, same commit: idempotent (skip duplicate)
- Parallel CI runs for same branch, different commits: latest commit wins, stale rejected (409)
CI Failure Policy
Fail silently with a warning in CI logs. The snapshot exists locally; next CI run generates a fresh one. No retries or queuing — simplicity over completeness.
Dashboard
Branch-Aware Views
The dashboard supports multi-branch, multi-project views:
| View | Value |
|---|---|
| Single branch | Current structural health |
| Branch comparison | Health diff between branches (e.g., alpha vs release) |
| Branch timeline | Trend over history snapshots |
| Pre-merge gate | Feature branch health vs main |
Data Flow
- TanStack DB (
@tanstack/react-db) provides reactive live queries over collections - Collections are backed by Turso via QueryCollection
- Live queries update the UI when new snapshots arrive