Frequently Asked Questions
Twenty questions and answers covering SOMA's architecture, operations, and implementation details.
General
What is SOMA?
SOMA (System of Organizational Memory and Awareness) is a knowledge management layer that sits between AI agent execution and organizational governance. It ingests what agents do via execution traces, synthesizes what the organization should know through machine-powered pattern detection, and enforces what agents must follow by feeding ratified policies back through the Policy Bridge. The system is built around a four-layer knowledge vault that separates raw data, team context, machine proposals, and ratified organizational truth, ensuring that confidence levels are never conflated with authority levels.
Why four layers instead of a flat vault?
At scale with many agents, confidence does not equal consensus, and a machine-inferred pattern is not the same as organizational truth. The four layers separate raw data in L1 (Archive), team context in L2 (Working Memory), machine proposals in L3 (Emerging Knowledge), and ratified policy in L4 (Canon). Without this separation, every synthesized insight gets treated as equally authoritative, which is dangerous when it constrains agent behavior — for example, a statistical pattern about retry behavior could be automatically enforced as policy even when the business context makes retries harmful due to duplicate transactions.
What problem does the governance gate solve?
Auto-generated policies have blast radius across all agents in the system. If the Synthesizer identifies "agents should always retry 3 times" from pattern data, but the business context says retries cause duplicate transactions, blind enforcement is actively harmful. The governance gate between L3 and L4 ensures a human reviews proposals before they become constraints, using the promote() and reject() operations through the Governance API. No auto-promotion path exists — every piece of L4 canon was explicitly approved by a reviewer, and the evidence chain traces back through L3 proposals to L1 raw traces.
Can I use SOMA without AgentFlow?
Yes, SOMA's vault and workers are framework-agnostic. You can create entities directly via vault.create(), use writeToLayer() for layer-aware writes with permission enforcement, and query knowledge via the Policy Bridge with intent-based routing (enforce, advise, brief, route, all). AgentFlow integration is optional — it provides trace ingestion via ExecutionEvent and PatternEvent types, guard enforcement through the PolicySource interface, and the dashboard governance page, but SOMA works as a standalone knowledge system with its own CLI.
How does SOMA stay framework-agnostic for decision extraction?
Decision extraction works from ExecutionGraph structure — nodes, edges, and metadata — not from adapter-specific events or framework internals. Any framework that produces a graph with tool nodes, branch edges, retry edges, or subagent nodes gets decision extraction automatically through extractDecisionsFromGraph(), with no adapter changes needed. The function maps graph structures to decision types: tool nodes become tool_choice, branched edges become branch, retried edges become retry, subagent nodes become delegation, and failed nodes become failure. Frameworks that want to provide richer data beyond what the graph structure reveals can optionally emit decision trace events with the DecisionTraceData type.
Technical
How does the file locking work?
A lock file (_vault.lock) is created with the O_EXCL flag, which is atomic at the filesystem level — the operation fails if the file already exists, preventing race conditions. The lock file contains the PID of the lock holder, enabling stale lock detection on startup when the PID is no longer running. Lock acquisition times out after 5 seconds with 50ms retry intervals, and all vault mutations (create, update, remove) acquire the lock before proceeding.
What happens if the vault index gets corrupted?
On load, the index is validated by spot-checking 10% of entries (minimum 1, maximum 50) against the actual files on disk. If more than half of the checked entries are missing their corresponding files, the entire index is discarded and rebuilt by scanning all .md files in type directories. If the _index.json file contains invalid JSON, it is rebuilt immediately without spot-checking. The rebuild logs a warning so operators know the recovery occurred.
What happens if the disk fills up?
Before every write operation, statfsSync checks the available disk space on the vault's filesystem. If available space is below 10MB, the write is rejected with a clear error message before any file operations begin. If writeFileSync fails mid-write (for example, the disk fills between the check and the write), the temporary file is cleaned up so no partial .md files are left on disk — the vault never contains half-written entities.
How does queryByLayer perform on large vaults?
queryByLayer uses index-level filtering rather than scanning all files. The layer field is stored in _index.json alongside type, name, and tags, so the function reads only the index to determine which entities belong to the requested layer. On a vault with 100K entities where 1K are in L3, an emerging query reads only the ~1K matching files from disk, not all 100K. This design keeps query performance proportional to the result set size, not the total vault size.
What prevents feedback loops between workers?
Three guards prevent infinite recursion. First, the Reconciler checks for existing merges with the same reconciled_from sources before creating a new merge entity, so it never re-merges already-merged content. Second, the Synthesizer excludes entities tagged synthesized from its candidate pool, so it never synthesizes patterns from its own output. Third, the Cartographer skips relationship proposals between entities it tagged with cartographer, preventing self-referential relationship chains. Additionally, all workers have a circuit breaker that stops processing after 100 creates per pipeline run.
How does the decay processor prevent broken evidence links?
Before removing a decayed entry from its current layer, the processor scans all L3 and L4 entries for evidence_links arrays containing the old entity ID. It updates those references to point to the new decayed-{oldId} ID in L1, ensuring the evidence chain remains intact. The standalone checkDanglingReferences() function can be run independently at any time to audit all evidence links across the vault and report any that point to non-existent entities.
What is the difference between vault.update() and writeToLayer()?
vault.update() patches an existing entity's fields (status, tags, metadata, body, etc.) but explicitly rejects any attempt to change the layer field — this prevents circumventing the governance pipeline by directly moving entities between layers. writeToLayer() creates a new entity in a specific layer with full permission enforcement — it checks that the calling worker is authorized to write to the target layer via the permission matrix and validates all layer-specific required fields (team_id for L2, confidence_score and evidence_links for L3, ratified_by and origin_l3_id for L4). They serve fundamentally different purposes: update modifies existing entities, writeToLayer creates new ones with layer guarantees.
How does worker state fingerprinting work?
Each worker stores a vault fingerprint — an MD5 hash of the _index.json file — in its state file alongside processed event IDs and content hashes. On startup, if the stored fingerprint does not match the current vault's index fingerprint, the worker discards all cached state and reprocesses from scratch. This mechanism detects vault resets, manual edits, or external modifications that would make the worker's cached state inconsistent with reality, at the cost of one full reprocessing cycle.
Can entities change layers?
Not directly — vault.update() rejects the layer field in the update payload to prevent bypassing the governance pipeline. The only two supported layer transitions both create new entities rather than modifying in place. Governance promote() creates a new L4 entry from an L3 entry (marking the original L3 as promoted), and decay moves expired L2/L3 entries to L1 by creating a new L1 entity with decayed_from metadata and removing the original. The original entity's layer field is never modified.
How does the YAML parser handle complex values?
Nested objects and arrays of objects are serialized as inline JSON within YAML frontmatter (e.g., metadata: {"author":"alice","version":2}). The parser detects values starting with { or [{ and parses them as JSON during deserialization. Simple values — strings, numbers, booleans, and flat string arrays — use standard YAML syntax. This trade-off preserves round-trip integrity (write then read produces identical data) without adding an external YAML library dependency, though it means complex nested structures appear as JSON strings in the raw Markdown files.
How does the dashboard governance page communicate with SOMA?
The dashboard server reads soma-report.json for the GET endpoints, providing layer counts, governance statistics, and pending proposals to the browser. For mutations (promote and reject), the server calls the SOMA CLI via child_process.execSync with sanitized arguments — command injection is prevented by argument sanitization. This architecture keeps the write path through SOMA's permission-enforced system, meaning the browser never touches the vault directly and all writes go through the same writeToLayer permission checks and validation that the CLI uses.
What is the mutation log for?
_mutations.jsonl is an append-only log of all vault operations — create, update, and delete — with timestamps, affected entity IDs, and changed fields. It is auto-rotated when it reaches 10MB, with the old log archived. The vault does not read the mutation log during normal operations; it exists for future audit and replay capabilities. Currently it is write-only, providing a complete audit trail of every change made to the vault over time.
How does the migration work?
The soma migrate-layers command scans all entities in the vault and adds layer: 'archive' and source_worker: 'migration' to entities that lack a layer field. It is non-destructive — it only adds fields and never modifies existing data or removes entities. It is also idempotent — re-running the command skips already-migrated entities that have a layer field. All entities start in L1 (Archive), and governance reviewers can later promote valuable ones to L4 Canon through the standard governance pipeline.
What are the confidence score thresholds?
L3 proposals from the Synthesizer carry confidence scores ranging from 0.0 to 1.0, with specific rules governing the calculation. Cross-agent patterns corroborated by 5 or more agents score at 0.8 or above, while single-agent patterns are hard-capped at 0.5 regardless of evidence quantity. Evidence count contributes incrementally at +0.02 per trace and +0.15 per additional agent. The governance API's list_pending() sorts proposals by confidence descending, so reviewers see the most statistically confident proposals first.
What are the seven invariants?
These seven properties must hold after any sequence of vault operations. They are verified by property-based tests running 200-operation random sequences:
- Index-disk consistency — Every entity on disk has a matching index entry, and vice versa.
- Layer uniqueness — No entity exists in two layers simultaneously.
- Evidence integrity — Every
evidence_linksentry resolves to an existing entity. - Canon traceability — Every L4 entry has a valid
origin_l3_idpointing to an existing L3 entry. - Working memory completeness — Every L2 entry has both
team_idanddecay_at. - Permanence guarantee — L1 and L4 entries never have
decay_at(they do not expire). - Worker provenance — Every layered entity has a known
source_worker.
These invariants are tested in invariants.test.ts with 19 tests that exercise random create, update, delete, promote, and decay operations in arbitrary order.
How many tests does SOMA have?
SOMA has 197 tests across 16 test suites, covering the full system from unit-level vault operations to end-to-end pipeline integration. The suites include: vault, vector-store, harvester, synthesizer, reconciler, cartographer, policy-bridge, integration, feedback-loop, four-layer, hardening, invariants, fault-injection, decision-intelligence, ops-intel, and langchain-adapter. The hardening suite alone has 27 tests covering locking, disk safety, index recovery, and YAML round-trip integrity. The invariant suite runs property-based tests with 200-operation random sequences validating all 7 system invariants.