This document summarizes the new SQLite/OPFS persistence helpers delivered for PLAN.md §6 so other agents can integrate with them quickly.
layout_snapshots (introduced with schema version 2) persists force-layout
positions so users can resume a project without recomputing the graph.
| Column | Type | Notes |
|---|---|---|
snapshot_id |
INTEGER | Primary key |
graph_key |
TEXT | Unique identifier for the graph (e.g. helios:v1:layout:{hash}) |
graph_hash |
TEXT | Optional secondary hash used to detect incompatible snapshots |
layout_json |
TEXT | JSON array of node positions (id, x, y, z, fx, fy, fz) |
layout_version |
INTEGER | Defaults to 1 for forward compatibility |
node_count |
INTEGER | Stored node count at save time |
metadata_json |
TEXT | Optional JSON blob (viz-specific status, perf info, etc.) |
created_at |
TEXT | ISO-8601 timestamp |
updated_at |
TEXT | ISO-8601 timestamp |
Indexes:
UNIQUE(graph_key)– ensures one active snapshot per graph key.idx_layout_snapshots_graph_hash– accelerates lookups by hash.
StorageWorkerClient (see src/storage/client.js) now exposes:
saveLayoutSnapshot({ graphKey, graphHash?, layout, metadata?, layoutVersion?, nodeCount? })loadLayoutSnapshot(graphKey)deleteLayoutSnapshot(graphKey)listLayoutSnapshots({ graphKey?, limit?, order? })
Snapshots are sanitized by src/storage/layout-persistence.js:
import {
saveLayoutSnapshot,
loadLayoutSnapshot,
deleteLayoutSnapshot,
listLayoutSnapshots
} from './src/storage/layout-persistence.js';Use normalizeLayoutSnapshot(nodes) before saving to ensure coordinates are
finite and only id, x/y/z, fx/fy/fz are stored.
GraphVisualization accepts an async persistence provider via
setLayoutStorageProvider(provider). The provider should implement:
{
save({ key, snapshot, metadata }): Promise<void>
load({ key }): Promise<{ layout, layoutVersion?, graphHash?, metadata? } | null>
delete({ key }): Promise<void>
has({ key }): Promise<boolean>
}The viz currently calls:
saveafter stable layouts (debounced, auto-freeze aware).loadon visualization start; expects metadata includinggraphHash.hasto show UI state for existing snapshots.deletewhen the user resets layouts.
If OPFS is unavailable, the provider should be null, causing the viz to fall
back to the original localStorage implementation.
Persistent embeddings now support incremental reuse. The relevant exports live
in src/embeddings/persistence.js.
- Global run fingerprint (
embeddings.fingerprint) - Run metadata (
embeddings.metadata) - Per-function fingerprints (
embeddings.functionFingerprints)
Helpers:
computeFunctionFingerprint(functions)
computeFunctionFingerprintMap(functions)
loadFunctionFingerprintMap(options?) // returns stored per-function mapPersist updates via:
await persistEmbeddingRun({
functions,
chunks,
embeddings,
similarityEdges,
metadata,
fingerprint, // string from computeFunctionFingerprint(...)
functionFingerprints // optional map (otherwise auto-generated)
});persistEmbeddingRun now runs inside a single transaction (see commit
3cb0233). Do not wrap it with another BEGIN/COMMIT; the storage worker
already ensures atomicity.
Typical client sequence (also implemented in index.html):
-
Compute fingerprint for the full function list.
-
Call
tryLoadEmbeddingRun({ functions, chunks, fingerprint }).- Returns
{ embeddings, similarityEdges, metadata, functionFingerprints }when the stored fingerprint matches, otherwisenull.
- Returns
-
If the run is partially reusable, call:
const reuse = await loadEmbeddingsForFunctions({ functions, chunks, targetFunctionIds: unchangedIds });
reuse.embeddingscontains vectors for unchanged functions.reuse.missingFunctionslists IDs that require re-embedding.
-
Embed the changed functions and merge the new vectors with reused ones.
-
Call
persistEmbeddingRunwith the combined result and updated fingerprints.
The storage suite exercises the new flows:
tests/storage/client.test.mjs– client ↔ worker message coverage.tests/storage/layout-persistence.test.mjs– layout helper sanitization.tests/storage/sqlite-ensure.test.mjs– schema + migration guarantees.
Embeddings-specific tests:
tests/embeddings/persistence.test.mjs(run vianode --test ...)tests/chunker.test.mjstests/similarity.test.mjs
HELIOS automatically cleans up old data to respect privacy and manage storage usage.
- Default retention: 24 hours
- Cleanup runs: Automatically on app bootstrap (if
config.retention.enabled = true) - What gets cleaned:
- Layout snapshots older than 24 hours (
updated_at < cutoff) - Resume flow payloads (keys prefixed with
resume::) older than 24 hours - Uses
updated_attimestamp, so active snapshots stay fresh
- Layout snapshots older than 24 hours (
Retention window is configurable via kv table:
// Change retention to 48 hours
await storageClient.setKv('retention.maxAgeHours', '48');
// Manual cleanup trigger
await storageClient.send('retention:enforce');Default is 24 hours if not set. See docs/retention-policy.md for full details.
| Feature | Entry Points | Notes |
|---|---|---|
| Layout persistence | src/storage/layout-persistence.js, StorageWorkerClient.saveLayoutSnapshot |
JSON node snapshots, metadata & graph hash |
| Viz integration | GraphVisualization.setLayoutStorageProvider, VisualizationControls |
Handles async save/load, auto perf modes |
| Embedding reuse | computeFunctionFingerprint, loadEmbeddingsForFunctions, persistEmbeddingRun |
Fingerprint map allows per-function delta updates |
| KV keys | embeddings.fingerprint, embeddings.metadata, embeddings.functionFingerprints |
Updated alongside each successful run |
For questions or extension requests, reach out on agent mail – storage-agent remains available to support Section 6 integrations.
For import-map hardening, CDN fallback procedures, and vendor mirror guidance see
docs/dependency-packaging.md. Follow that audit when adjusting runtime
dependencies (graphology, 3d-force-graph, transformers, etc.) to avoid breaking
the OPFS/worker pipeline.