Grandbo: memory architecture
Grandbo is Landfall's institutional memory: a per-organization lake of what's been learned across past incidents, extracted only from corroborated findings (never raw chatter), plus whatever an admin adds by hand. Beacon, the investigator agent, delegates to it before raising a hypothesis, the same way it delegates metric and log queries to a telemetry connector rather than reasoning about storage itself. This page describes that memory lake's shape and the hybrid retrieval mechanism behind how it decides what's relevant: lexical plus vector search, fused into one ranking.
System overview
Two independent pipelines meet at the same store. Incidents write memory entries as they close, and investigations read them back by relevance. Neither pipeline talks to storage directly; both delegate through one repository, the only component that knows the store's actual schema.
Delegation: agent code never touches storage
Beacon's own instructions name one step: call query_org_memory with a topic before raising a hypothesis. Everything past that call is delegated through a chain of narrow, single-purpose seams. The agent has no schema knowledge, no SQL, and no awareness of which embedding provider is even in play:
The read seam is deliberately singular: chat's "has this happened before?" lookup, the Edge Bridge, and Beacon's own tool call all delegate through the same one method rather than each growing its own query logic. A change to how relevance is computed changes once, for every caller, by construction.
A vector search is only as good as the text it's given, so the delegation contract also constrains what the agent hands down: a real sentence describing what's actually been found so far, the suspected mechanism, the affected component, the symptom pattern, never the incident's own title or a bare slug. A two-or-three-word identifier carries far less embeddable signal than a genuine description, even before accounting for the fact that two incidents describing the same problem rarely share the same slug at all.
Why hybrid retrieval
Matching purely on shared vocabulary can't relate two incidents that describe the same real problem in different language: a query about "checkout-api-slow-queries" and an entry about database connection pool exhaustion share no tokens, even though they're plausibly the same root cause. No amount of tokenizer tuning closes that gap on its own. Grandbo closes it by combining lexical matching with a second, independent signal: semantic similarity over an embedding of the same text. Exact-identifier matches keep working exactly as before; genuinely related but differently-worded entries now also surface.
The read path: two rankings, fused
- Lexical: the query topic is tokenized into tags and matched against each entry's own tag set (word overlap, exact-identifier-friendly), the mechanism Grandbo has always used. Ranked widest-first, kept to the top 25 candidates.
- Vector: the query topic is embedded, and compared against every active entry's own stored embedding by cosine distance (Postgres's pgvector extension). Only entries closer than a fixed distance cutoff are eligible; Grandbo never returns "the nearest entry regardless of how unrelated it is." Also kept to the top 25 candidates.
- Fusion: the two ranked id lists are combined with Reciprocal Rank Fusion (k=60): each list contributes 1 / (60 + rank) per entry it contains, summed across both lists, then sorted descending. An entry ranked highly on either signal, or both, rises to the top; an entry absent from a list simply contributes nothing from it. The fused, wide list is only sliced down to the caller's real result count after fusion, so a candidate that ranked outside the top 5 on one signal but highly on the other still gets a fair combined score instead of being discarded before it has a chance to combine.
The write path: what gets embedded, and when
Every place an entry's title and summary are written or revised (the automatic write-behind after an incident closes, an admin's manual entry, and a correction) embeds that text and stores the resulting vector on the entry, alongside the existing tag/lexical fields (never instead of them).
Embedding is delegated after the entry's own write has already durably committed, as a separate, fire-and-forget step, never inside the same transaction. A slow embedding call must never risk losing the entry itself: an entry is real and citable the moment it's written, with or without a vector.
The embedding layer
The repository delegates the actual embedding computation to a provider interface rather than calling any one model directly, so the retrieval mechanism itself has no dependency on which model answers. In production, that's Amazon Bedrock's Titan Embed v2 (amazon.titan-embed-text-v2:0), called through the same per-instance Bedrock credential path already used for Beacon's own diagnosis model, so connecting Grandbo requires no separate vendor or credential.
Every embedding, whatever produced it, is a fixed 1024-dimensional vector. The stored column and the retrieval query are both built against that one contract, not against a specific model, so a dimension mismatch between what's written and what's queried would fail every comparison silently, in a way that's easy to miss in review and only shows up as "nothing ever matches" later.
The memory lake: data model and isolation
The vector lives on the existing entry, not a separate table: a vector(1024) column on org_memory_entries, using Postgres's vector extension (pgvector). No approximate-nearest-neighbor index is built for it: a plain sequential scan is fast enough at the actual scale Grandbo's memory lake operates at (a few thousand entries per organization, queried a handful of times per investigation), and adding index-maintenance complexity ahead of a real need would be exactly the kind of premature infrastructure this platform avoids building.
org_memory_entries is itself a projection: an append-only org_memory_events log is the source of truth, and the entry row Grandbo actually queries is kept in sync inline, never mutated independently. Tenant isolation is enforced twice for every relevance query. It runs inside the same row-level-security-scoped transaction as every other tenant-scoped operation, and the query itself states an explicit tenant_id predicate directly; RLS is defense-in-depth here, never the sole isolation mechanism.
This is Grandbo's own dedicated store, distinct from the platform's other context-sharing subsystem (the live, in-incident ContextFrame the war room itself runs on): a separate, event-sourced Postgres store built for a different shape of data, durable cross-incident memory, not a single incident's live state.
In one sentence, per signal
| Signal | Mechanism | Width / cutoff | Strength |
|---|---|---|---|
| Lexical | Tag overlap on tokenized text | Top 25 candidates | Exact identifiers, shared vocabulary |
| Vector | Cosine distance over a text embedding | Top 25 candidates, distance < 0.5 | Same problem, different wording |
| Fusion | Reciprocal Rank Fusion, k=60 | Sliced to caller's limit after fusion | Neither signal alone has to be right |