- Added core BUBBLE architecture with decision envelopes and policy store - Implemented bundle API with FastAPI skeleton and OpenAPI specification - Added Go-based storage implementation with SQLite and RocksDB support - Created integrations for peer sync, vector search, and N8N workflows - Added comprehensive testing framework and documentation - Implemented provenance walking and decision checking algorithms 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
40 KiB
I'd like to talk about (in terms of my CHORUS / BZZZ software) the ancestory and provenance of decisions. Im plagued by the idea that searching for relevant context in a tree structure of files, coupled with a virtual overlay of temporal metadata, and a virtual overlay of context, is a three dimensional problem. But the axes are not all equal. Let me explain, I think in one dimension (the actual folders/files, the logical structure is obviously a tree (even if underneath at block level its linear), but on the context layer the structure is a lot less obvious. With RAG, concepts are a number of conceptual 'hops' away in a graph structure of related entities, but on the decision axis, decisions are an ancestory of decisions from sometimes seemingly unrelated domains. I guess I'm looking for the logical structure that we use in the academic world of cross-referencing and citation, but having that navigable, and most importantly, a really fast lookup. Guide me here. I think you know what I'm looking for I just lack the language or framework to express it.
Yeah, I think I see exactly where your mental model is going — you’re trying to unify three different “organizing axes” that don’t naturally share the same geometry:
- File/Folder Structure (Logical Hierarchy) → rigid, tree-shaped, deterministic, and finite in branching.
- Context/Conceptual Graph → fuzzy, graph-shaped, possibly cyclic, with weighted edges representing relevance or conceptual proximity.
- Decision/Provenance Lineage → genealogical, DAG-shaped (directed acyclic graph), often spanning across unrelated domains and contexts.
In CHORUS/BZZZ terms, you’re basically describing a multiaxial, multi-topology knowledge space, where each axis has its own rules for navigation, storage, and indexing — but you still want a single, fast, unified lookup that lets you pivot seamlessly between them.
1. A Framework for Thinking About This
Here’s the language and model I’d use to describe it, which comes partly from knowledge graph theory, provenance modelling (like W3C PROV), and information retrieval research:
| Axis | Shape / Topology | Typical Navigation | Analogy |
|---|---|---|---|
| Structure | Tree (hierarchical) | Path traversal | File explorer / namespace |
| Context | Graph (weighted, fuzzy edges) | Semantic hops | RAG vector search / ontology browsing |
| Provenance | DAG (directed acyclic graph) | Lineage tracing | Git history / academic citations |
The trick is that a query might need to move orthogonally between these axes — e.g.,
Find the file that contains concept X (context axis) which was influenced by decision Y (provenance axis), and is stored under this folder (structure axis).
2. Why “3D” Is the Wrong but Helpful Metaphor
It feels like a 3D space because you have three distinct navigational modes, but the axes aren’t geometrically equivalent:
- Tree axis → deterministic, bounded depth, “hard” pointers.
- Graph axis → probabilistic, possibly infinite expansion, “soft” associations.
- Lineage axis → directional, causal, may connect distant or conceptually unrelated nodes.
If you imagine each node in the tree having:
- A set of semantic vector embeddings (for context hops),
- A set of provenance edges (for decision ancestry),
…then what you really have is a primary index (tree) overlaid with two orthogonal link layers that allow arbitrary lateral jumps.
3. The Academic Parallel
In the academic world, you’re basically talking about merging:
- Citation Graphs (papers citing other papers → decision ancestry)
- Topical Ontologies (subject headings, keyword indexing → context)
- Library Classification (Dewey, LOC → folder structure)
The “fast lookup” problem has already been partly solved in that domain with:
- Inverted indexes for topic/term search (think Elasticsearch or Lucene)
- Adjacency lists / compressed sparse graphs for citation lookups
- Triple stores / RDF + SPARQL for complex cross-axis queries
But academic search is slow compared to what you want — they prioritize completeness, not interactive navigation speed.
4. The Logical Structure You’re After
What you need in CHORUS/BZZZ is something like this:
Base Layer (Tree Index):
- Acts as a spatial/structural namespace.
- Stored in a prefix tree (trie) or B+ tree for ultra-fast path lookups.
Overlay Layer 1 (Context Graph):
- Each node in the tree has a list of vector IDs pointing into a vector store (FAISS, Milvus, Weaviate, Vespa, etc.).
- Context hops = nearest-neighbor search in embedding space.
Overlay Layer 2 (Decision Lineage DAG):
- Each node has a provenance edge list.
- Stored in adjacency list form, optimized with a bidirectional search index so you can go “upstream” (ancestors) or “downstream” (descendants) fast.
Unified Query Engine:
- Queries run as multi-stage pipelines: filter by tree → hop via graph → traverse via DAG (or in reverse).
- Optionally cache common traversals in materialized path indexes so “decision ancestry + context” queries become O(1) after first resolution.
5. Fast Navigation Tricks
- Multi-key indexing — e.g.,
(path, embedding_cluster_id, decision_id)as a compound index for ultra-quick cross-axis lookups. - Precomputed transitive closure for provenance edges (so “all ancestors” queries are constant-time).
- Contextual Bloom filters per node — to cheaply say “this file definitely doesn’t contain this concept”.
- Hybrid search — combine vector similarity with metadata filters in one step (e.g., Vespa or Elasticsearch + dense vectors).
6. Why This Matters for CHORUS/BZZZ
This would give you:
- Academic-style citation tracking for decisions (fast).
- RAG-style semantic proximity navigation (fast enough for interactive use).
- Traditional namespace navigation (instant).
- A pivoting query language — you could start in any axis and jump orthogonally.
The language for this in computer science would be:
“A hierarchical index augmented with semantic and provenance overlays, supporting multi-topology traversal and compound indexing.”
If you want, I can sketch the actual data structure layout for CHORUS/BZZZ that makes this navigable in <10ms per hop, even at scale — essentially a hybrid of Git’s object store, a vector DB, and a citation graph DB. That’s where this becomes truly practical.
Do you want me to go ahead and design that?
So to clarify, I currently have a combination of git (and all the functionality that brings), a 1:1 mapping between the filesystem paths and ucxl address space. The context is stored under each ucxl address as encrypted markdown wrapped in a JSON envelope, in our distributed p2p network as a DHT, and we can look that up reasonably fast. So when we get a new decision made or new content created, all that metadata about the who, what, where, when , & why is submitted to the 'Leader' BZZZ node (Effectively the Project Manager). Its at this point too that our system events are most informative (blockers, etc). That agent uses a well-crafted prompt to assess all the submitted data, and judiciously either embelishes or prunes the context, tailoring it for the many roles in the organisation. (As part of its toolset it has a RAG lookup, which it can use to find context around terms it might not understand) It then stores this tailored context under the ucxl address space (DHT) for each of those roles. (If its unsure, it calls upon human intervention) So we don't so much need a fast lookup time as a fast processing time for that LLM. This whole ingestion process I'm calling SLURPing. What I'm looking for here is its counterpart (I guess?) The decision justification tool, the provenance of authority, the trace of rationality, that strongly correlates with roles, responsibilites, and project goals and strategies. This is clearly an LLMs strength not just another DB / data structure, unless this is another MCP tool the SLURP LLM can use. Which leads me to the how: a decision tree walk-back-in-time (more precisely decision space n-hops), thereby displaying an ancestory of decision points would be informative (but is it sufficient?) How do we pass on to an agent things like "we tried this already", and "we can't pursue this line of reasoning due to budgetary constraints", or "this library is incompatible with our cloud infrastructure". My guess is that some context is global and ... [truncated]
Nice — this is a crisp brief and you’re already halfway to the right architecture. I’ll be blunt: you shouldn’t try to hand this whole job to a single LLM prompt forever. Instead make the LLM one powerful orchestration tool in a small suite of well-structured, audited primitives (structured decision records, a provenance graph, global policy/constraint store, and a fast vector/context store). Below is a practical, concrete plan you can implement incrementally.
What you need (conceptually)
-
Immutable Decision Records (DRs). Every decision (or proposal, experiment, rejection) becomes an immutable object stored in your DHT/git-backed UCXL address space.
-
Provenance Graph. DRs are nodes in a directed graph (edges = “influenced by”, “derived from”, “rejected in favour of”, “depends on constraint X”, “uses lib Y”, etc.). This is the thing you walk back n-hops.
-
Context Vectors + Metadata. Each DR and content node gets:
- embeddings for semantic similarity (RAG)
- structured metadata (who/when/why/alternatives/constraints/cost/confidence)
- role-exposure flags (which roles should see this as global vs. private)
-
Policy/Constraint Store (global). Single source of truth for global limits: budgets, infra incompatibilities, regulatory constraints, capability profiles.
-
Small toolset for SLURP + Decision Agent. SLURP ingests/normalizes into DRs; Decision Agent uses graph queries + RAG + constraint checks + human-in-loop to produce tailored context bundles.
Decision Record (DR) — concrete schema (example)
Store this as JSON wrapped and signed/encrypted; keep it immutable and append-only.
[BUBBLE-SNIPPET-01]
Where SLURP fits
SLURP keeps doing what it does:
- normalise incoming submit (who/what/where/when/why)
- create a DR with structured fields above
- generate an embedding and store
embeddings_id - append to UCXL/DHT and link into provenance graph
- produce role-specific bundles by filtering
role_exposure+ applying global policy filters
But add two new SLURP capabilities:
- Decision Summariser — produce a short machine-readable summary plus human-readable justification that becomes part of the DR. This is the LLM’s strength but done as a step, not the only truth.
- Constraint Annotator — match DR constraints against the Policy Store and tag the DR (e.g.,
violates:budget-l1).
The Decision Agent (counterpart to SLURP)
A small orchestration service (can be LLM-driven) that:
-
receives queries like “Can Agent-X attempt approach Q?”
-
fetches: relevant DRs (n-hop ancestry), relevant context via RAG, global constraints, and capability profiles
-
runs a pre-check pipeline:
- Provenance walk (bounded) to collect ancestors/descendants
- RAG similarity search to find “we tried this before” evidence
- Constraint check (policy store)
- Capability check (role/team/infra profiles)
-
produces a decision packet: yes/no/conditional + provenance trace + suggested next steps + who-to-ask (human)
-
if unsure, escalates to human review, and records that escalation as a DR
Provenance + RAG query strategy (practical)
You’ll often want to combine graph distance and semantic similarity. Use a scoring function:
score(node) = α * provenance_relevance(node) + β * semantic_similarity(query_embedding, node_embedding) + γ * recency_factor
- provenance_relevance: inverse of number of hops (or use weighted PageRank)
- semantic_similarity: cosine from vector DB
- recency_factor: decays older nodes unless they are highly-cited in graph
Algorithm for walk_back(n, query, role):
- Find anchor nodes (explicit doc or concept vector hits).
- BFS from anchors over provenance edges up to depth n, collecting nodes.
- For each node compute
score. - Return top-K nodes filtered by
role_exposureand policy checks. Cache results for repeated queries.
Passing "we tried this already" and constraints
Make “attempts” explicit DRs with lifecycle states:
experiment:started,experiment:failed,experiment:succeeded,experiment:abandonedTag experiments with outcome and reason. An agent checks for anyexperimentDRs matching sametagsor high semantic similarity before starting new work. That’s how “we tried this already” becomes a deterministic check, not a fuzzy “maybe.”
For constraints:
-
Put them in a Policy Store (UCXL path) with machine-friendly predicates:
policy:budget-l1 -> {"max_spend":10000,"applies_to":["project:foo"]}policy:cloud-compat -> {"disallowed":["gcp"], "allowed":["aws","azure"]}Decision Agent always runs constraint checks automatically and annotates the DR withviolationsfield.
Role-specific context hygiene
You’re right — some context is global and some is local. Two patterns:
-
Global flags + redaction rules.
- DR fields can be marked
global,project-only, orprivate-to-role. SLURP masks or omits private fields when producing bundles.
- DR fields can be marked
-
Concise role bundles.
- The Leader produces condensed bundles:
role_bundle = {summary, relevant_DR_refs, top_evidence, policies_applied}. That’s what gets stored for that role’s UCXL path.
- The Leader produces condensed bundles:
UI / UX for humans & agents
- Decision Walk UI: timeline slider + graph view + “dead-branch collapse”. Allow toggles: show all ancestors, only experiments, only constraints, only external influences.
- Signal lights: green = no policy violations; amber = conditional; red = violates global policy.
- “Tried before?” quick-check button that runs semantic + provenance test and shows the most relevant prior experiment DR(s).
Fine-tune vs prompt engineering (be practical)
-
Start with robust prompt engineering + toolchain: SLURP + Decision Agent + structured DRs + RAG will already drastically reduce hallucination and keep traceability.
-
Collect good labeled data (DRs + human decisions + outcomes). After you have thousands of DRs and outcomes, fine-tune or train a model specifically to:
- Summarize decisions into DR fields
- Predict likely outcomes given prior DRs
-
Fine-tuning is expensive and useful later; first get the structured pipeline and provenance graph right.
Performance & storage notes (practical)
-
Keep DRs small and reference external evidence via UCXL addresses (don’t inline huge blobs).
-
Index:
- adjacency lists for provenance graph (fast traversal)
- vector index (FAISS / Milvus) for embeddings
- materialized ancestor sets for frequently-read nodes (to instant-query “all ancestors”).
-
Cache role bundles and common walks, invalidate on new DR append.
Attack surface & signing
- DRs should be signed by actor keys; leader validates signatures before incorporating decisions.
- Maintain role-based encryption so private or sensitive DRs are only visible to authorized roles.
Minimal roadmap (first 3 increments)
- DR schema + append-only storage: implement immutable DR format; wire SLURP to create them.
- Provenance graph: start small (Neo4j / DHT adjacency lists) and implement a bounded walk API.
- Decision Agent orchestration: implement the pre-check pipeline (provenance walk + RAG + policy check) and add the “tried before” check into your CI for agents. Add human escalation logging. After those three, build the Decision Walk UI and start collecting data for future fine-tuning.
Final take
You’re right to treat this as something the LLM helps with, not replaces. The LLM’s job should be:
- normalize incoming natural language into structured DR fields,
- summarize and justify decisions in human language (for the record),
- propose candidate ancestors and alternatives (and surface evidence).
But the source of truth must be structured DRs + provenance graph + policy store. That combination lets you (1) prove why something was done, (2) prevent rework by answering “did we try this?”, and (3) safely tailor context to roles.
If you want, I’ll:
- draft a production-ready DR JSON schema (with signed envelope and encryption examples),
- sketch the API endpoints for SLURP + Decision Agent (walk, check, bundle),
- or write the pseudocode for the
walk_back(n, query)scorer and caching strategy.
Which of those do you want next?
Got it — here’s a production-ready Decision Record (DR) schema that fits your CHORUS/BZZZ/UCXL ecosystem, assuming:
- Immutable, append-only storage
- JSON wrapped in an encrypted + signed envelope
- Links cleanly into your UCXL path space
- Supports role-specific exposure
- Explicit provenance edges + policy constraints
1. Encrypted + Signed Envelope
Every DR sits inside an envelope with three top-level sections:
[BUBBLE-SNIPPET-01]
payloadis the raw DR (below), encrypted at rest in your DHT.signaturesverify integrity & identity. Multiple allowed (co-signed decisions).encryption.recipients= keys of roles or individuals allowed to read.
2. Decision Record (DR) Payload
This is the actual schema SLURP will generate and the Decision Agent will consume.
[BUBBLE-SNIPPET-02]
Key Design Choices
idis a content hash (e.g., SHA-256 of canonicalised payload) → immutability guaranteed.ucxl_pathis the anchor for linking into your filesystem/git space.lifecycle_statelets you quickly filter out superseded or rejected DRs without deleting them.alternativesexplicitly track rejected or less-preferred options → critical for “we tried this” lookups.constraintstie directly into your Policy Store.provenanceis a lightweight adjacency list → enables O(1) fetch for neighbours in the graph.role_exposure= filtering hint for SLURP when bundling context for specific roles.embeddings_idlinks to the vector DB for semantic search.
3. Constraints & Policy Store Example
You’d keep these in their own UCXL namespace:
[BUBBLE-SNIPPET-03]
4. Encryption Flow
-
On creation:
- SLURP creates payload (DR)
- Serialises → hashes → signs → encrypts for allowed recipients → stores in DHT
-
On retrieval:
- Decrypt with recipient key → verify signatures → hand DR to agent/human
5. Why This Works
- Auditable: Immutable + signed = no silent changes to history.
- Navigable: Provenance graph is adjacency lists in each DR → fast n-hop walks.
- LLM-Friendly: Structured fields mean you can feed only relevant parts into prompt windows.
- Role-Aware: Exposure flags + encryption let you control information flow at storage time, not just query time.
If you like, I can now extend this with the pseudocode & data layout for the provenance graph so your Decision Agent can do the n-hop ancestry walk efficiently — that’s what will make the “decision space walk-back” instant.
Do you want me to do that next?
Alright — let’s wire this DR schema into a provenance graph you can walk in milliseconds, even at thousands or millions of DRs.
1. Core Idea
- Each Decision Record (DR) in the DHT has provenance edges (
influenced_by,derived_from,supersedes). - We materialize an adjacency index (key-value store) so you don’t need to decrypt and parse every DR just to walk the graph.
- This index is signed & cached locally on each node so graph walks are fast without hitting the network.
2. Data Layout
Two main indexes:
A. Forward Index (Downstream)
[BUBBLE-SNIPPET-04]
B. Reverse Index (Upstream)
[BUBBLE-SNIPPET-05]
- Forward index answers: “What came after this DR?”
- Reverse index answers: “What led to this DR?”
Both can be stored as compact adjacency lists in LevelDB / RocksDB / SQLite.
3. Provenance Walk Algorithm
Here’s pseudocode for bounded n-hop ancestry:
[BUBBLE-SNIPPET-06]
4. Metadata Cache
get_dr_metadata(dr_id) is not a full decrypt — just a lightweight cache entry like:
[BUBBLE-SNIPPET-07]
This cache is generated when SLURP first ingests or updates a DR. That way walks avoid decrypting everything and hitting the full DHT.
5. Fast “We Tried This Already” Check
You can combine provenance walk + semantic similarity:
[BUBBLE-SNIPPET-08]
6. Storage & Sync Strategy
-
Keep adjacency lists locally indexed per BZZZ node.
-
Sync them as small delta updates over p2p (no need to transfer whole DRs).
-
Store
forward_indexandreverse_indexas separate keyspaces in a KV store. -
When a new DR is ingested, SLURP updates:
- forward edges from each provenance target
- reverse edges pointing back to each provenance source
7. Graph Walk + RAG for Decision Agent
The Decision Agent query pipeline becomes:
-
Find entry point
- If starting from concept → vector search → anchor DR(s)
- If starting from DR → directly use ID
-
Upstream Walk
- Reverse index BFS up to
nhops - Role-based filter, state filter
- Reverse index BFS up to
-
Semantic Expansion
- Take ancestor set, vector search for semantically similar DRs (within constraints)
-
Constraint Check
- For each DR, check
constraintsfield vs. Policy Store
- For each DR, check
-
Assemble Provenance Bundle
- For LLM: timeline order + statements + constraints + key evidence refs
8. Why This Will Be Fast
- Graph walk = O(n) in number of edges visited, no DHT round trips
- Metadata cache avoids decryption until absolutely needed
- Role/state filters applied at walk time keep set small
- DHT is only hit at bundle assembly stage, not during raw walk
If you want, I can now mock a Decision Agent’s “bundle” output so you can see exactly what the LLM would get after a walk-back — that’ll make it clear how the agent uses the provenance for “don’t repeat past mistakes” reasoning.
Do you want me to do that next? Lets.. sketch the API endpoints for SLURP + Decision Agent (walk, check, bundle), and write the pseudocode for the walk_back(n, query) scorer and caching strategy.
Got it — let’s wire the API shape first, then drop into pseudocode for walk_back() and its cache layer.
1. API Endpoints Overview
I’m assuming this is internal agent-to-agent API over HTTP or message bus, not user-facing.
SLURP (Ingestion)
| Method | Endpoint | Description |
|---|---|---|
POST |
/slurp/ingest |
Accepts raw DR payload, runs LLM contextualization, updates DHT & provenance indexes. |
GET |
/slurp/metadata/:dr_id |
Returns metadata cache for DR. |
GET |
/slurp/context/:dr_id |
Returns full decrypted DR (if authorized). |
POST |
/slurp/index/update |
Batch index updates (forward + reverse) from peer nodes. |
Decision Agent
| Method | Endpoint | Description |
|---|---|---|
POST |
/decision/walk |
Upstream or downstream walk in decision space (bounded n-hops). |
POST |
/decision/check |
Checks if concept/decision was tried before or blocked (vector search + provenance check). |
POST |
/decision/bundle |
Returns full provenance bundle (timeline + DR details + constraints). |
2. Endpoint Shapes
/decision/walk
[BUBBLE-SNIPPET-09]
/decision/check
[BUBBLE-SNIPPET-10]
/decision/bundle
[BUBBLE-SNIPPET-11]
3. walk_back() Pseudocode with Scoring
[BUBBLE-SNIPPET-12]
4. Caching Strategy
Metadata Cache Layout (local KV store)
Key: dr:sha256:...
Value:
[BUBBLE-SNIPPET-13]
Rules:
- Cache populated at ingestion time (
/slurp/ingest) and on first remote fetch. - TTL: long-lived (e.g., 30 days) unless superseded DR appears.
- Local indexes (
forward_index,reverse_index) reference cache keys. - LRU eviction if memory pressure occurs, but persistent KV keeps full set.
Sync:
- When new DR appears, peer sends:
[BUBBLE-SNIPPET-14]
Peers update both index & cache atomically.
If you want, I can now sketch the minimal persistent store schema for metadata_cache, forward_index, and reverse_index so you can implement this in RocksDB or SQLite without guesswork. That’s the next logical step if you want this ready for code.
If you want, I can sketch the hybrid layout where RocksDB is your live store and SQLite is a periodically-updated “reporting copy” for humans and slower agents. That fits SLURP + decision walk patterns perfectly.
Alright — hybrid it is. We’ll make RocksDB the fast, always-hot operational store for agents, and SQLite the cold-ish, human-friendly analytics layer.
Hybrid Architecture
1. RocksDB: Live KV Store
Purpose:
- Instant lookups during SLURP ingestion & decision walks
- Fast writes when new decisions/edges come in from DHT
Buckets / Column Families (logical separation in RocksDB):
-
meta→ DR metadata by ID- Key:
dr:<hash> - Value: JSON or protobuf blob of metadata (statement, role exposure, state, tags, timestamp).
- Key:
-
fwd→ Forward index (decision → influenced decisions)- Key:
fwd:<hash> - Value: List of DR IDs.
- Key:
-
rev→ Reverse index (decision → ancestor decisions)- Key:
rev:<hash> - Value: List of DR IDs.
- Key:
-
constraints→ Global or scoped constraints for quick rule injection.- Key:
constraint:<role>:<hash> - Value: Constraint description blob.
- Key:
2. SQLite: Reporting & Analytics Store
Purpose:
- Complex joins, filtering, and historical views for humans & slow LLM analytics.
- Ingests batches from RocksDB every few minutes or on demand.
Schema:
[BUBBLE-SNIPPET-15]
3. Data Flow
Ingestion (SLURP):
- LLM processes incoming DR payload.
- Writes metadata, forward index, reverse index to RocksDB.
- Appends to a write-ahead log (also in RocksDB or flat file).
Sync to SQLite:
- Background job reads WAL or change markers.
- Batch INSERT/UPDATE into SQLite tables.
- Allows filtering, joins, analytics queries.
Querying:
- Agent calls RocksDB directly for hot lookups (walk_back, check, bundle).
- Human/LLM analytics uses SQLite for deep queries.
4. Pseudocode for Write + Sync
[BUBBLE-SNIPPET-16]
5. Benefits
✅ RocksDB → fast ingestion + fast agent lookups ✅ SQLite → rich historical queries + human-readable reporting ✅ WAL → Guarantees that no DR is dropped between RocksDB hot store and SQLite cold store ✅ Batch sync → Avoids hammering SQLite with every single update
If you want, I can next integrate the walk_back() scorer with this hybrid store so that:
- It pulls metadata & indexes from RocksDB (fast)
- Falls back to SQLite if key is missing in RocksDB (cold fetch) That gives you both speed and completeness.
Do you want me to do that next?
Alright — here’s how we bolt walk_back() onto the hybrid RocksDB + SQLite design so it can:
- Pull from RocksDB first (fast, low-latency).
- Fallback to SQLite (for older decisions that may be cold or pruned from RocksDB cache).
- Score results based on relevance to the
query(semantic + structural weight). - Cache results so repeated walks don’t burn cycles.
walk_back(n, query) — Hybrid Version
Algorithm
-
Start at a DR ID (passed in or resolved from query).
-
Traverse reverse index in RocksDB → collect ancestors.
-
For each ancestor, fetch metadata from RocksDB.
-
If missing, fetch from SQLite.
-
Score each ancestor based on:
- Graph distance (n-hops)
- Semantic similarity to query (vector store / embedding lookup)
- Constraint impact (penalize or boost depending on role scope / blocking status)
-
Cache results in RocksDB for future queries.
Pseudocode
[BUBBLE-SNIPPET-17]
Caching Strategy
-
Cache Key:
walkcache:{start_id}:{n}:{hash(query)} -
Eviction Policy:
- Keep LRU of last
Xqueries in RocksDB. - Periodic cleanup job to drop caches older than
Ydays.
- Keep LRU of last
-
Cache Scope:
- RocksDB only (agents hit hot cache).
- SQLite is never queried for cache — it’s cold path only.
Benefits of this setup
- Hot path: All RocksDB (fast).
- Cold path: Falls back to SQLite (complete).
- Semantic + structural scoring: Mixes RAG-like similarity with graph depth.
- Constraint-aware: The scoring penalizes or boosts based on role/goals.
If you like, I can extend this with a "bundle" endpoint so that after walk_back(), the agent can request a compact decision dossier with only the most relevant ancestors + constraints, ready for injection into another LLM context window.
That’s basically the “decision ancestry briefing pack” you were hinting at earlier.
Hell yeah — let’s build the Decision Dossier: a compact, role-filtered, provenance-backed bundle agents (or humans) can drop straight into an LLM prompt or a UI. Below I’ll show:
- the
/decision/bundleAPI shape - the bundle JSON schema (what the dossier contains)
- production-ready pseudocode to assemble the bundle (including walk_back reuse, policy checks, redaction, caching)
- operational notes (size limits, caching, security, escalation)
I’ll be blunt: this is where most systems either bloat the LLM context with garbage or leak private details. Keep the bundle tight, signed, and role-aware.
/decision/bundle API
POST /decision/bundle
Request body:
[BUBBLE-SNIPPET-18]
Response: 200
[BUBBLE-SNIPPET-19]
Bundle JSON schema (concise)
timeline element (one item):
[BUBBLE-SNIPPET-20]
Pseudocode — assemble_bundle()
[BUBBLE-SNIPPET-21]
Notes on helper functions:
walk_back(...)is the hybrid rocks/sql walk we already defined (returns scored metas).redact_field(value, role)strips or masks private fields based onrole_exposureand encryption flags. Do not rely solely on redaction for sensitive info — prefer encryption per DR.llm_call()is an LLM microservice; keep prompts minimal and structured. The LLM proposes actions — treat these as suggestions and create a DR for any action taken.is_authorized(role, dr_id)must check role ACLs and decryption rights. Always audit full_payload accesses.
Operational & Safety Details
- Size caps: limit bundle to e.g., 40KB of textual content or 25 DR summaries; for larger needs provide
full_payload_refsonly. - Cache TTL: 12–72 hours depending on project velocity. Invalidate on new DR appended that links into bundle.
- Signing & Audit: every assembled bundle is content-hashed and signed by the assembler (Leader) and stored in the DHT for later audit.
- Rate-limits & quotas: limit how often an agent may request
include_full_dr=trueto prevent exfiltration. - Human escalation: any bundle that triggers policy violations or has low-confidence LLM suggestions should set
escalation.required = trueand create anescalationDR automatically. - Redaction-first policy: prefer not to include secrets or attachments in bundles; reference UCXL paths. Only allow full DRs when strictly authorized.
Example minimal bundle (realistic)
[BUBBLE-SNIPPET-22]
Final notes — practical priorities
- Start small: implement the bundle assembler as a deterministic pipeline that mainly does walk_back + policy checks + evidence aggregation.
- Add the LLM “suggested actions” afterwards; treat LLM as non-authoritative until you have human-labeled outcomes and can fine-tune.
- Audit everything. If you want to fine-tune a model later, use these bundles + outcomes as labeled training data.
Want me to (pick one):
- produce the exact request/response JSON schema as an OpenAPI snippet?
- write the Node/Python server skeleton for
/decision/bundlethat ties into your RocksDB + SQLite stores? - produce a compact LLM prompt template for the
action_suggesterstep that minimizes hallucination?
Which one next? write the skeleton first
Got it. Here’s a clean, modular Python FastAPI skeleton for your /decision/bundle endpoint, wired into the hybrid RocksDB + SQLite stack and using the earlier pseudocode.
This skeleton covers:
- Request validation
- Hybrid metadata fetch with fallback
- Provenance walk reuse
- Bundle assembly stub (no LLM call included yet)
- Cache read/write in RocksDB
- Basic error handling
[BUBBLE-SNIPPET-23]
What to do next
- Replace
embed()andcosine_similarity()with your actual embedding/vector similarity code. - Add authorization and full payload fetch + decryption where marked.
- Plug in your real vector search for query-based anchor resolution.
- Integrate your LLM call for
suggested_actionsand goal alignment scoring. - Harden security (rate limits, audit logs, encryption at rest).
- Add logging & metrics.
If you want, I can help with:
- Vector search integration code sample
- Full payload decryption stub
- LLM prompt templates & invocation wrapper
- Unit tests for the bundle endpoint
What next?
I want you to do all 4 in sequence and then get back to producing a compact LLM prompt template for the action_suggester step that minimizes hallucination.
Got it. I’ll deliver these in sequence, focused, practical, and production-ready:
1. Vector Search Integration Sample
Let’s assume you use a vector DB (like FAISS or Pinecone) or an embedding library (e.g., OpenAI embeddings or SentenceTransformers). This example uses a local FAISS index with embeddings from SentenceTransformers.
[BUBBLE-SNIPPET-24]
2. Full Payload Decryption Stub
Add a placeholder for full DR fetch + decrypt from DHT or encrypted store:
[BUBBLE-SNIPPET-25]
3. LLM Prompt Templates & Invocation Wrapper
Example prompt template for action_suggester that minimizes hallucination:
[BUBBLE-SNIPPET-26]
4. Unit Tests for /decision/bundle
Using pytest and httpx for FastAPI testing:
[BUBBLE-SNIPPET-27]
I’ll follow up with the compact hallucination-minimized LLM prompt template for action_suggester next. Let me know if you want me to iterate on any of these steps or add deeper integration examples.
Here’s a tightly focused, hallucination-resistant LLM prompt template for your action_suggester step. It’s designed to:
- Be explicit about the format and expectations
- Provide just enough context, no fluff
- Force JSON-only output with strict structure
- Use role and constraints to narrow scope
- Include examples for clarity
[BUBBLE-SNIPPET-28]
How to fill in the variables:
-
{role}— the role of the requesting agent/user (e.g., "engineer", "pm", "legal") -
{start_statement}— the main decision or issue statement initiating this bundle -
{timeline}— a numbered, bullet, or newline-separated list of short decision summaries, max 5 items (e.g.:1. Rejected Library Z due to license conflicts 2. Budget capped at $10K for new library experiments 3. Previous tests show incompatibility with cloud infra
)
{constraints}— list of active constraints/policies relevant to the decision
Why this works:
- The strict JSON output and role+constraints focus strongly reduce hallucination
- Explicit instructions on confidence prevent the model from guessing wildly
- The example JSON sets a clear output pattern the LLM can mimic
- Restricting action types ensures semantic consistency and downstream parsing ease
If you want, I can help you build a lightweight parser/validator for the output JSON to catch common format errors and make sure your system safely ingests the recommendations.
Want me to do that next? Absolutely — I’ll give you a pair of clean, reusable Python functions that you can call from anywhere (like a microservice, CLI tool, or n8n custom node) to:
- Send the action suggestion request to your n8n webhook, with exactly the parameters as per the prompt template.
- Receive and validate the JSON response from the webhook, including basic sanity checks to ensure it matches the expected schema.
This will let you easily iterate and tune your prompt in n8n without breaking your app integration.
1. Send to n8n webhook function
[BUBBLE-SNIPPET-32]
2. Example receive handler for n8n webhook (Python Flask style)
If you want a sample minimal HTTP handler that n8n can call internally or externally (or simulate) — here’s how you might do it on your server side:
[BUBBLE-SNIPPET-33]
How to wire this into n8n
- Create an HTTP Request node in your workflow to call your Python microservice’s
/n8n-action-suggesterendpoint or any LLM API endpoint. - The input JSON should be the payload structure defined above.
- The output from that node will be a parsed JSON list of actions, which you can pass downstream for display, notification, or recording.
If you want, I can also draft a ready-to-import n8n workflow JSON snippet for this, including an HTTP Request node plus example trigger.
Want that next? Here’s a minimal n8n workflow JSON snippet you can import directly, illustrating:
- A Webhook Trigger node to start the workflow
- An HTTP Request node calling your external Python microservice (or any LLM endpoint) for action suggestions
- A Set node that outputs the response for easy testing
This snippet assumes your external action suggester service is reachable at http://localhost:5000/n8n-action-suggester. Adjust the URL as needed.
[BUBBLE-SNIPPET-34]
How to use:
- Import this JSON in n8n (top-right menu → Import from file / paste JSON).
- Replace the webhook node's
"webhookId"with your generated webhook ID or leave it to auto-generate. - Update the URL in the Call Action Suggester node to your real Python microservice or LLM API URL.
- Trigger the webhook (e.g.,
POST /webhook/action-suggester) with JSON payload like:
{
"role": "engineer",
"start_statement": "We need to decide on the streaming library.",
"timeline": [
"Rejected Library Z due to license conflicts",
"Budget capped at $10K for new library experiments",
"Previous tests show incompatibility with cloud infra"
],
"constraints": [
"Budget under $10K",
"AWS-only infrastructure"
]
}
- The response will be captured and output as
suggested_actionsin the Set Output node.
If you want, I can help extend this with:
- Validation nodes to verify webhook input
- Retry or error handling for the HTTP Request node
- Formatting outputs for Slack/Email notifications or database storage
Just say the word.
Lets go back to the produce the exact request/response JSON schema as an OpenAPI snippet for the decision bundle
Here’s a concise, practical OpenAPI 3.0 snippet defining the /decision/bundle endpoint’s request and response JSON schemas based on everything we discussed. It’s ready to plug into your OpenAPI spec or Swagger UI.
[BUBBLE-SNIPPET-35]
Notes:
- The
oneOfon request requires eitherstart_idorquery. - Response bundles keep sensitive info redacted by default; full payload references allow lazy fetch if authorized.
timelineis an ordered list of decision records closest to the anchor, with scores.goal_alignmentandsuggested_actionssupport future LLM integration outputs.escalationflags when human attention is required.
If you want, I can help generate a full OpenAPI YAML file around this snippet or create a Postman collection for testing. Just say.