Get in touch
All systems

// teardown

The /ask assistant

Retrieval is an editorial decision.

live·2026
Convex Agent + RAGGemini 3.5 Flashgemini-embedding-001AI SDKNext.js

Indexed ahead of time

Knowledge
Code index
Published writing
↓ searched per question

On every question

Metered gate
Parallel search
Streamed answer

// overview

Ask this site anything about my work at /ask and the answer streams back grounded in three separately indexed sources: hand-written knowledge, source files admitted one at a time, and published writing that re-indexes the moment it goes live. The model call is the easy part. The design work is deciding what a public assistant is allowed to read, and what an anonymous endpoint is allowed to spend.

// how it works

01 · curate

Knowledge is written to be retrieved

The core index is a folder of markdown in the repo: bio, experience, projects, recommendations, working style, and a build digest per codebase. It isn't scraped from the site, because pages are written for layout and these files are written to answer questions. They're versioned in git next to the code, and a sync command embeds each one keyed by its path, so re-running it replaces entries in place instead of piling up duplicates.

02 · admit

Code is admitted, never crawled

The assistant can quote real source, so source gets the strictest door. A manifest names every file individually: no globs, no directories. At index time each named file is read and scanned again, and anything touching a restricted topic, containing a credential-shaped string, or running oversized is rejected. A rejection skips the upload and fails the run, so a file that changed after it was listed can't slip in quietly.

03 · publish

Publishing is the index write

Articles and white papers come from the site's own CMS, and the publish mutation schedules the re-index itself. Editing a live post re-syncs it; unpublishing or deleting one schedules its removal. Entries are keyed by post id, so every sync replaces rather than appends, and each carries its title, type, and /writing link so an answer can cite the page it drew from. No cron job, no manual step.

04 · meter

Metered before it costs anything

Every question clears a gate before a model is touched. Per-session limits cap bursts and daily volume for each anonymous browser session, and because sessions are free to create, a global ceiling applies across all of them on every new thread and every message. Questions have a length cap. The checks run in the mutation that saves the question, so a rejected request never embeds, retrieves, or generates. Token usage from every answer is logged.

05 · retrieve

Three searches, labelled apart

The question runs against all three indexes in parallel, each with its own result limit and similarity floor. Knowledge gets the most room, and code gets a stricter floor, because a loosely related snippet misleads more than a loosely related paragraph. Results enter the prompt as separate, labelled sections, so the model knows whether it is reading a bio fact, a published argument, or a quoted file. An empty code or writing search simply drops its section.

06 · stream

Streamed from a durable thread

Generation runs on Gemini 3.5 Flash as a scheduled server action, not inside the visitor's request. Tokens stream into the conversation thread as they're produced, and the page subscribes to that thread, echoing the question instantly and rendering the reply at a steady reading pace. Because the answer lives server-side, closing the tab doesn't lose it: the browser keeps the thread id, and the conversation is there on return. Try it at /ask.

// decisions

The parts that were a call, not a default — and what the alternative would have cost.

chose

Three indexes, each with its own limit and floor

not

One shared index ranked as a single list

Bio prose, source code, and long-form writing differ in density and in how much they should count. In one ranked list, a long article or a dense file crowds out the short facts that answer most questions. Separate indexes guarantee each source a share, allow a stricter floor for code, and let the prompt say what kind of evidence each excerpt is. The corpus is small and curated, so the discipline lives upstream, in what gets admitted.

chose

Code admitted by name and re-scanned at index time

not

Indexing repos by glob and filtering at answer time

Everything in the index is quotable by a public, anonymous chatbot, so the only filter worth trusting is the one before upload. An allowlist makes every file a deliberate decision, and the scan catches the file that changed after it was approved. A prompt rule can be talked around; a file that was never embedded can't be quoted.

chose

Re-index inside the publish mutation

not

A scheduled re-crawl, or fetching posts at question time

The publish step already knows exactly which post changed and when, so it schedules the sync as part of the same write. There is no polling interval to go stale in, and the question path only ever reads from the index. Unpublish and delete schedule removal the same way, so a pulled post stops being quotable without anyone remembering to clean up.

chose

Gemini Flash for answers, Gemini embeddings for retrieval

not

A frontier model on every question

Grounding does the heavy lifting: the model reads a handful of labelled excerpts and writes a short, linked answer. That is a latency and cost problem more than a reasoning one, and on an anonymous endpoint a fast, inexpensive model is what lets the global ceiling stay generous. One provider covers both embedding and generation behind one key, the same one already serving other jobs on the site.

// guarantees

Nothing unvetted is quotable

Code reaches the index only by name and after a clean scan. Posts are indexed only while published, and pulling one removes it.

Grounded, or it says so

Answers draw on the retrieved excerpts and link to the pages behind them. When nothing covers a question, it says so and points to /contact.

Spend has a ceiling

Per-session limits plus a global cap, checked on every thread and message before any model call. Token usage is logged per answer.

// by the numbers

3
indexes, searched in parallel
9
source files, each allow-listed by name
0
extra steps to index a published post

// let's talk

Let's build something.

Have a product that needs both sharp strategy and someone who can ship it? I'd love to hear about it.

Get in touch

Command Palette

Search for a command to run...