Skip to content
Ahmed Hamza

Project Retrospectives

Why semantic code search is not enough for coding agents

Notes on building Satori around deterministic MCP workflows, exact reads, stale-index warnings, and bounded retrieval contracts.


Date

Read

3 min

Satori started from a practical frustration: coding agents are fast at editing, but they are often weak at finding the right context before they edit.

The first version of the idea was simple enough. Index a repository, search it semantically, and return useful file spans. The harder part was making that useful for agents that need repeatable workflows, not just a good-looking search demo.

The failure mode is subtle: a semantic result can be relevant and still be unsafe to edit from. A snippet might be stale, truncated, generated, test-only, or missing the caller/callee context that explains why the code exists.

The boundary I wanted

I did not want Satori to become a general agent framework or a large toolbox. The useful boundary is narrower:

That led to the fixed six-tool MCP surface: lifecycle, search, outline, graph, read, and list. The small surface matters because agents need predictable contracts more than they need another set of clever options.

The contract shape

The important part is that retrieval returns navigation evidence, not permission to guess:

const fallback = {
  message: NAVIGATION_FALLBACK_MESSAGE,
  context: { codebaseRoot, relativeFile, absolutePath },
  readSpan: {
    tool: "read_file",
    args: {
      path: absolutePath,
      start_line: safeStartLine,
      end_line: safeEndLine,
    },
  },
};

When symbol data is available, the response can also carry the next bounded operation instead of leaving the agent to invent one:

const nextActions = {
  openSymbol: {
    tool: "read_file",
    args: { path: absolutePath, open_symbol: { symbolId, symbolLabel } },
  },
  traceCallers: {
    tool: "call_graph",
    args: { path: codebaseRoot, symbolRef, direction: "callers" },
  },
  traceCallees: {
    tool: "call_graph",
    args: { path: codebaseRoot, symbolRef, direction: "callees" },
  },
};

That kind of response tells the agent what it found, how trustworthy the result is, and what verification step should happen before editing. The goal is not to make the model smarter through vibes. The goal is to make the tool contract harder to misuse.

What changed after the first useful version

The project became more serious when I started hardening the failure states.

Search needed runtime-first defaults because docs, tests, fixtures, and generated output can dominate results. Sync needed to understand ignore-rule changes without forcing a full reindex. The CLI needed strict stdout safety because MCP over stdio breaks if normal logs leak into the protocol stream.

The index lifecycle was the biggest lesson. A local snapshot can claim a repo is ready, but that does not always mean the remote vector collection is complete and compatible. Satori now treats completion proof, fingerprints, sidecar readiness, and reindex requirements as part of the product behavior.

The Zilliz timeout lesson

One hardening pass came from slow or indeterminate Milvus/Zilliz delete operations. The tempting behavior is to clear local state after asking the backend to drop a collection. That is unsafe. If the remote delete times out, the system does not know whether the collection is gone.

The fix was semantic, not cosmetic: do not report clear success unless remote absence is verified. If the delete/probe state is indeterminate, preserve local state and make the retry path explicit.

That changed how I think about agent tools. When infrastructure is uncertain, the tool should not pretend certainty for the sake of a neat response. It should keep state honest and give the next safe action.

The tradeoff

This makes Satori more conservative than a simple vector-search wrapper. It has to track freshness, sidecar readiness, lifecycle state, and exact file verification. That adds work before the edit happens.

But that is the point. Coding agents do not just need “nearby text.” They need enough verified structure to avoid writing a confident patch against the wrong file state.

What this taught me

The visible feature is semantic search, but the real engineering value is in the contracts around it:

Satori is still an active project, but it has become my best proof of systems thinking: not because it is large, but because each hardening pass made the boundary clearer.