Skip to content

Concepts

Memory model

Sigil stores memory in three layers. Each answers a different kind of question, and retrieval reaches into all of them.

Chunks

~512-token blocks of ingested documents. Contextualized, embedded at 1024 dimensions. What you fall back to when the facts aren’t enough.

Facts

Atomic statements. The primary retrieval unit — what gets injected into prompts. Category, confidence, importance, provenance, supersession.

Entity graph

Typed nodes and relations extracted from facts. Answers “what do I know about X” and “what’s connected to X”.

A fact is one atomic statement that stands alone. It’s the unit because it’s the unit that can be retrieved, ranked, superseded, and injected independently.

uid 9fK2a1b0-…
content Retries run through the Postgres job table with SKIP LOCKED, not Redis
category decision
confidence high
importance supplementary
namespace default
visibility shared
embedding vector(1024)

Fourteen, split into personal and knowledge. The extractor picks one; you can filter by it.

Personal
preferenceLikes, dislikes, favourites, preferred tools and methods
opinionViews and assessments of tools, concepts, approaches
personalBiographical — workplace, location, birthday
experienceProjects built, tools used, skills acquired
Knowledge
business_ruleOrganizational rules, policies, constraints
workflowProcess flows, state transitions, procedures
architectureSystem design, service interactions, infrastructure
conventionCoding patterns, naming rules, team standards
decisionWhy a choice was made, and the trade-offs weighed
domain_knowledgeDomain-specific terminology and concepts
key_insightNotable takeaways and explanations
metricQuantitative data, measurements, statistics
issueKnown problems, bugs, limitations, risks
action_itemTasks, follow-ups, assignments, deadlines
Terminal window
sigil facts --category=decision --limit=30

The split isn’t cosmetic. Personal categories plus convention are what the directive kind draws on for its always-injected slots — because “how to work with me” needs a query-independent channel, and topical facts don’t.

high, medium, or low, assigned by the extractor. Low-confidence facts are stored but rank lower, and search can filter them out entirely.

vital or supplementary.

Vital facts surface globally through the vital pod kind — six always-on hot-context slots, independent of pod membership and independent of what you’re currently asking about. They also survive lifecycle promotion without ever needing to be accessed.

Use it sparingly. Six slots is the entire global budget; everything marked vital competes for it.

shared, agent, or device. Which agents and machines can retrieve this fact. Default is shared. See Provenance and visibility.

When a new fact contradicts an old one, the old one is superseded, not deleted. It stops being retrieved, but the link survives — so “what did we believe before the migration” remains answerable, and provenance stays intact.

That’s also why point-in-time queries work: query routing can detect a temporal constraint and restrict retrieval to what was valid then.

Ingested documents are split into ~512-token blocks with overlap at the seams, then contextualized — each chunk gets a short generated preamble situating it in the whole document.

That step matters more than it sounds. A raw chunk reading “this is capped at 30 days” is useless in isolation. Contextualized, it reads “In the session-retention section of the payments-api design doc: this is capped at 30 days” — and now it embeds into a space where a query can actually find it.

Chunks aren’t normally injected. They’re the fallback layer: search can include them with --chunks, and answer synthesis pulls them in so the synthesizer has raw material rather than only the extracted atoms.

The document’s full text is also stored on its row, separately from the chunks. Chunks overlap at every seam, so they can’t reconstruct an original — sigil docs <uid> reads the stored text.

Facts mention things. Sigil extracts those things as typed entities and connects them:

[person: Priya] ──works_on──► [project: payments-api]
│ │
knows depends_on
│ ▼
[person: Anmol] [service: stripe-webhooks]

Entity extraction runs as a durable background job by default rather than blocking your write. Relation discovery is off by default (ingest.extractRelations) because a coalesced background job handles cross-document edges more cheaply than doing it per-ingest.

The graph earns its place in three ways:

  • Entity-first search. Short, name-like queries (under 60 characters) detect an entity and search from it, which beats similarity alone for “what do I know about X”.
  • Graph expansion. --graph pulls in facts one edge away from your results, then re-ranks the merged set. It surfaces things never named in your query.
  • Traversal. MCP clients walk it directly with traverse_graph and get_entity_context.
Terminal window
sigil search "Priya" # short + name-like → entity-first
sigil search "auth" --graph # expand through relations

Every fact records where it came from and who wrote it:

Source documentWhich document and section it was extracted from, if any
AgentWhich agent wrote it — claude-code, mcp, cli
DeviceWhich machine
TimestampsCreated, last accessed, stage entered
SupersessionWhat replaced it, or what it replaced

This is what makes a shared brain debuggable. When something wrong is in memory, you can find out which agent on which machine put it there. Provenance covers it in full.

Facts move through three stages:

StageMeaning
freshJust captured. Not yet trusted, not yet in the canonical ranking pool.
stableThe canonical pool that retrieval ranks against.
editingRecently retrieved, and receptive to AUDM updates and contradictions.

fresh → stable needs one hour and either vital importance or at least one access. A low-importance fresh fact nobody ever touched stays fresh forever — effectively forgotten without any explicit deletion, and with no LLM call needed to decide that.

stable → editing happens inline on a search hit. editing → stable closes after 30 minutes of no access. See Memory decay.

Every fact belongs to zero or more pods and exactly one namespace.

Pods are a soft retrieval boundary — they steer ranking and own hot-context slots. Namespaces are a hard partition: facts in one are invisible from another.

Fixed at 1024 dimensions, for every provider and every model. The database’s vector(N) columns are pinned to it, so the schema and the embedding contract can never drift apart. A model that can’t emit or truncate to 1024 is rejected at setup rather than failing deep in the pipeline with a dimension-mismatch error.

Changing this is a schema break, not a configuration tweak.