TOTAL RECALL.
Give your coding agent a memory of your repo that survives the session ending.
This one is about your CODE, not your chat history. If you were at the older night with the same name, that was conversational memory. Today we index a codebase.
$10 early · $20 door · Frontier Tower members free, reach out to the team for a ticket
Your ticket includes z.ai and Claude Code for the session, and Nebius Token Factory credits to run the embeddings.
Every session, it meets your repo for the first time.
You open a fresh session. The agent has never seen your codebase. It re-derives the module map it worked out yesterday, opens the wrong files, and asks you where things live.
An agent that forgets your repo is an agent you have to supervise.
And the cost is not only the twenty minutes. It is that you stop trusting it with anything structural, because you know it is guessing about a codebase it cannot see. So you use it for small local edits and do the real work yourself.
Whatever agent you use is fine for this. It needs to be able to read files and run a command you give it. That is the whole prerequisite, and step zero sets the rest up from scratch.
"Use a bigger context window" is not the answer.
The obvious fix is to paste more in. Windows are enormous now, so why not load the whole repo every turn and stop worrying about it?
So the fix is not more context. It is memory, which stores things once, and retrieval, which fetches only the slice this question needs. Everything after this slide is how those two are built.
"Memory" is four different things.
They are not competing options. A working agent has several at once, and the useful way to tell them apart is not what they store, it is how the agent gets them back.
Almost every "our agent has memory" product is one or two of these four with a good name on it. When you evaluate one, the only question that matters is which rows it covers and which rows you are still on the hook for. Today you build rows two and three, and you get row one for free.
Retrieval, end to end.
Two separate pipelines that people constantly mistake for one. The top row runs when your code changes. The bottom row runs on every question, and it has to be fast.
Bad chunks cannot be rescued later.
The most common way this whole thing fails is not the model and not the database. It is that the repo was cut into pieces that do not mean anything on their own.
payload, algorithms=["HS256"]) except jwt.ExpiredSignature: return None return claims
// auth/tokens.py:41 verify_token def verify_token(raw: str) -> Claims | None: """Validate a bearer token.""" ...
The rule: retrieval quality is set at chunk time. If your results are junk, look at your chunks before you reach for a better embedding model or a reranker. Neither can put back a signature you cut off.
Three questions a vector index gets wrong.
Similarity search finds text that resembles your question. That is the right tool most of the time, and it is the wrong tool in three specific cases you will hit this week.
Do not start here. A graph costs an extraction step that can be wrong, and a schema you have to maintain. Build the vector index first, use it for a week, and write down the questions it answered badly. If they all look like the three above, add edges. If they do not, you just saved yourself a system.
What is actually out there.
Sorted by which of the four rows they cover. Not a ranking, and not an endorsement: the only useful question is whether a tool stores the thing your failing query needs.
| Tool | Row it covers | What it stores | Reach for it when |
|---|---|---|---|
| Files | Always on | Plain text your agent reads at startup. A project instruction file, a notes file. | Always. It is free, it is versioned with your code, and it is the highest-value memory per line you will ever write. |
| Chroma | Retrieval | Embeddings plus metadata, on your disk. Open source. | You want repo retrieval today with no service to run. This is what the lab uses. |
| pgvector | Retrieval | The same vectors, in a Postgres you already operate. | You have Postgres and would rather not add a second datastore. |
| Pinecone | Retrieval | A hosted vector index, managed and scaled for you. | The index has outgrown one machine, or you do not want to run it. |
| Mem0 | Working + always on | Facts extracted from conversations, recalled per user and session. | The thing you need to remember is about a person or a session, not about a codebase. |
| Zep / Graphiti | Graph | A knowledge graph where every edge carries time, so facts can supersede each other. | "What was true when" is a real question in your domain. This is the temporal case from the last slide. |
| Cognee | Graph + retrieval | A pipeline that ingests documents and builds the graph and the vectors together. | You want the extraction step handled rather than writing it yourself. Open source. |
| Letta | Always on, self-editing | Memory blocks the agent rewrites itself as it learns, inside a stateful agent runtime. | You want the agent to curate its own always-on tier instead of you doing it by hand. |
The honest default: row one plus Chroma covers most of what most teams need from a coding agent. Everything below that line exists because someone hit a query the first two rows could not answer. Find your failing query first, then come back to this table and pick the row that answers it.
Product names are used descriptively to identify the tools. No affiliation or endorsement implied.
One agent, six memories.
This is the personal automation repo running on the laptop in front of you. It is not a demo, it has been in daily use for months, and it grew every one of the four rows without anybody planning to. Every number here was measured this morning.
| What | Row | Size, measured | How it got there |
|---|---|---|---|
| CLAUDE.md | Always on | 75,767 bytes | Hand-written project instructions. Read in full at the start of every single session. |
| memory/*.md | Always on + graph | 313 files 535 links |
One fact per file, cross-linked to each other. 245 unique link targets, so it is a graph that nobody sat down to design. |
| MEMORY.md | Always on | 141 lines | The index over those 313 files. One line each, so the agent knows what exists without loading it. |
| graph.yaml | Graph | 4,493 people 264 companies |
Derived, never hand-edited. Regenerated from message history and event data by a command. |
| kernel_index.json | Retrieval | 478 verbs 77 modules |
Built by parsing the source, not embedding it. Exact lookup of every capability the repo has. No vectors involved. |
| pgvector farm | Retrieval | 768-dim HNSW |
Postgres on a cluster node, embedding research papers and video transcripts for semantic search. |
Read the third column as a warning, not a scoreboard. Nobody chose this mix. Each row was added the day a specific question could not be answered without it, which is exactly the order slide 7 recommends, arrived at by accident rather than on purpose.
The cheapest tier to build is the most expensive to run.
Each row has a completely different cost shape, and picking the wrong one for a given fact is how memory systems quietly get expensive.
Put a fact in the always-on tier only if the agent needs it in nearly every session.
Everything else belongs in an index it can go and get. The instinct is always to add one more line to the instruction file, because it works immediately. It works immediately and it charges you forever.
Four things that went wrong, so they do not go wrong for you.
Every one of these was expensive to learn and cheap to avoid if somebody tells you first.
425 capabilities. The generated index, built from the source this morning, has 478. The file was right when it was written and the code kept moving.
Found while building this deck, in the repo on this slide. Nothing warned anybody, because a confident wrong number reads exactly like a right one.The pattern under all four: memory fails quietly. A slow agent is obvious and a wrong memory is not, so the only defence is to make staleness visible on purpose. Step 4 of the lab does exactly that.
Pick a repo and get the dependencies in.
Use a codebase you actually work in. The bigger and messier it is, the better the demo lands, because you already know which questions it is hard to answer.
# a scratch folder inside your repo. everything today lives here. mkdir -p .recall && cd .recall uv init -q && uv add -q openai chromadb # the embedding endpoint. the key is on your ticket. export NEBIUS_API_KEY=paste-yours export EMBED_BASE_URL=https://api.studio.nebius.com/v1
# a scratch folder inside your repo. everything today lives here. mkdir -p .recall && cd .recall uv init -q && uv add -q openai chromadb # the embedding endpoint. the key is on your ticket. export NEBIUS_API_KEY=paste-yours export EMBED_BASE_URL=https://api.studio.nebius.com/v1
# PowerShell. note $env: instead of export. mkdir .recall; cd .recall uv init -q; uv add -q openai chromadb # the embedding endpoint. the key is on your ticket. $env:NEBIUS_API_KEY = "paste-yours" $env:EMBED_BASE_URL = "https://api.studio.nebius.com/v1"
# on a cloud box, same thing, and add the repo you want to index git clone <your-repo> work && cd work mkdir -p .recall && cd .recall uv init -q && uv add -q openai chromadb export NEBIUS_API_KEY=paste-yours export EMBED_BASE_URL=https://api.studio.nebius.com/v1
uv run python -c "import chromadb, openai; print('ok')" prints ok. If it does not, flag a facilitator now rather than at 11:30. Nothing else today works until this line does.Cut the repo where the language says a unit ends.
One chunk per function or class, carrying its file, its name and its start line. This is the step that decides whether the rest works.
# Python's own parser. No install, exact boundaries, real line numbers. import ast, pathlib, json SKIP = {".git", ".venv", "node_modules", ".recall", "dist"} KINDS = (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef) def chunks_for(path): src = path.read_text(encoding="utf-8", errors="ignore") try: tree = ast.parse(src) except SyntaxError: return lines = src.splitlines() for n in tree.body: # top level only, on purpose if isinstance(n, KINDS): yield {"path": str(path), "symbol": n.name, "line": n.lineno, "code": "\n".join(lines[n.lineno - 1 : n.end_lineno])[:6000]} def walk(root=".."): for p in pathlib.Path(root).rglob("*.py"): if not SKIP & set(p.parts): yield from chunks_for(p) out = list(walk()) pathlib.Path("chunks.json").write_text(json.dumps(out)) print(f"{len(out)} chunks from {len({c['path'] for c in out})} files")
uv run python chunk.py prints a count. Open chunks.json and read one at random: it should start with a def or class and be something you could hand a colleague. If it starts mid-expression, your boundaries are wrong and nothing downstream can fix it.Not a Python repo? Same shape, different parser. tree-sitter gives the identical node types for JavaScript, TypeScript, Go, Rust and forty more: swap the parser, keep every other line. Python's ast is used here only because it needs no toolchain.
Turn every chunk into a vector, and keep it.
The boring, solved middle of the pipeline. Twenty lines, and afterwards your repo is searchable by meaning instead of by filename.
import os, json, chromadb from openai import OpenAI client = OpenAI(base_url=os.environ["EMBED_BASE_URL"], api_key=os.environ["NEBIUS_API_KEY"]) db = chromadb.PersistentClient(path="store") col = db.get_or_create_collection("repo") def embed(texts): r = client.embeddings.create(model="BAAI/bge-en-icl", input=texts) return [d.embedding for d in r.data] chunks = json.load(open("chunks.json")) for i in range(0, len(chunks), 64): # batch, or you will wait b = chunks[i : i + 64] col.upsert( # id = path:line makes re-indexing idempotent. run it again on a # changed repo and only the chunks that moved get rewritten. ids=[f"{c['path']}:{c['line']}" for c in b], # embed the LOCATION with the code. "verify_token in auth/tokens.py" # is signal, and it is why a search for a concept finds the file. embeddings=embed([f"{c['symbol']} in {c['path']}\n{c['code']}" for c in b]), metadatas=[{"path": c["path"], "symbol": c["symbol"], "line": c["line"]} for c in b], documents=[c["code"] for c in b]) print(f"indexed {min(i+64, len(chunks))}/{len(chunks)}")
store/ folder exists on disk. That folder is the memory. It survives this session, your laptop closing, and the walk home.Hand the agent the right files, with line numbers.
A command your agent can run whenever it needs to know where something lives. It returns real paths and real lines, so the answer can be checked instead of trusted.
import sys from index import col, embed # reuse the collection you just built def retrieve(query, k=6): hits = col.query(query_embeddings=embed([query]), n_results=k) return "\n\n".join( f"// {m['path']}:{m['line']} ({m['symbol']})\n{doc}" for doc, m in zip(hits["documents"][0], hits["metadatas"][0])) print(retrieve(" ".join(sys.argv[1:])))
## Finding code in this repo Before answering where something lives, run: uv run python .recall/ask.py "<the question>" Cite the path and line it returns. Do not guess at file locations.
Next step up: expose retrieve as an MCP tool so the agent calls it as a real function instead of shelling out. Same code behind it. A transport change, not a rethink, and not worth debugging mid-sprint.
Persist what it learns, then add the edge vectors cannot give you.
The index already survives on disk. What does not survive is everything the agent worked out along the way, and the one relationship it will keep asking for.
# Repo notes ## Map - token validation: auth/tokens.py:41, NOT the middleware - the event state machine is 11 stages, events/__init__.py ## Conventions - never hand-edit data/graph.yaml, regenerate it ## Stale check - index built: 2026-08-08. Re-run index.py after a big merge.
import ast, json, collections from chunk import walk, SKIP calls = collections.defaultdict(set) for c in walk(): for n in ast.walk(ast.parse(c["code"])): if isinstance(n, ast.Call) and isinstance(n.func, ast.Name): calls[n.func.id].add(f"{c['path']}:{c['line']}") # now "what breaks if I change verify_token" is a lookup, not a guess json.dump({k: sorted(v) for k, v in calls.items()}, open("edges.json", "w"))
edges.json and see every caller listed. That second file is a context graph with one edge type in it, built in nine lines. Everything on slide 8 is that, with more edge types and a real query language.Where this bites you next week.
All four of these have already happened to somebody in this room's future. They are cheap to prevent and annoying to diagnose.
Notice that four of the five are about knowing when the memory is wrong, not about making it better. That ratio is correct, and it is the part most retrieval write-ups skip.
What is on your laptop that was not there at 10am.
Four files and a folder, on a repo you actually work in.
chunk.py splits your codebase on real function boundaries, carrying path, symbol and line.index.py embeds those chunks into a store that persists on disk and re-indexes idempotently.ask.py returns the right files, by meaning, formatted for the agent to cite.edges.json answers "who calls this", which no vector index can. A context graph with one edge type.MEMORY.md carries what the agent learned into every future session, for free.The proof is the new session. Close everything, open a fresh one, and ask the question your agent has never once got right. If it answers with a path and a line, you did not just learn retrieval this morning, you shipped it. Show your team on Monday.
Everything you used this morning.
Two of these came with your ticket. The rest are open source and yours to keep running for nothing.
pgvector if you already run Postgres, or a hosted index when it outgrows one machine.ast for today. tree-sitter for everything else, with the same node types across forty-odd languages.BAAI/bge-en-icl. Try Qwen/Qwen3-Embedding-8B on the same endpoint and re-run step 2 to compare what comes back. Same code, one string changed.ask.py over MCP so the agent calls it as a real tool. Then revisit slide 8 with the query your index got wrong this week.Product names are used descriptively to identify the tools used in the lab. No affiliation or endorsement is implied by their appearance here.
Your agent knows your codebase by name now.
Keep it. Re-index it. Bring the question it still gets wrong to the next one.
t.me/+EBFzKXmJAVk5ZGU0Site
vibecodingnights.comShow somebody on Monday. That is the whole point.
Hosted by Vibe Coding Nights: Rayyan Zahid (Immersive Commons), Michalis Vasileiadis (Hacker Bob), Eric Mockler (AI Geneticist), Devinder Sodhi (Learning Layer Labs). Facilitator: Rayyan Zahid.