BlogHow an agent session uses Stele
How it works

How an agent session uses Stele

You talk to your agent exactly the way you already do. What changes is what happens in the eight seconds before it answers, and what it leaves behind afterwards.

Stele7 min readUpdated
Banner: the context an agent receives before answering, ending in a risk callout reached through a caused-by edge.

The turn, end to end

Here is the shape of a single prompt, before we take any of it apart. You type something. Before the model sees it, a hook runs, searches the project record, walks outward from what it found, and prepends whatever it thinks bears on the question. The model answers with that in hand. If real work starts, a task gets created and claimed. As the session learns something durable, it gets written back.

Read in a list like that it sounds like an ordinary retrieval pipeline. The parts that turned out to be interesting are the ones where the obvious implementation was wrong.

Before the answer: seeds, then one hop

The hook has a hard budget. Six seconds for the graph walk, eight for the whole hook, and if it runs out it returns whatever it has rather than making you wait. Nothing about your prompt is blocked on this succeeding, which is deliberate: a memory layer that can stall your agent is worse than no memory layer.

It also does not run on everything. Prompts under 25 characters skip the walk entirely, because ok and keep going and now the tests do not have enough signal to retrieve against, and injecting a guess into a short turn is how you get an agent that suddenly changes the subject. The exception is that naming an identifier directly bypasses the length gate: type TASK-418 and that node gets resolved and pulled in whole, however short the sentence around it.

Otherwise it is two stages.

Seeds. Full-text and semantic search against the record produce up to ten starting points. This is the part every retrieval system has, and on its own it is not very interesting.

The walk. From those seeds it follows edges outward. One hop by default. This is the part that earns its keep, because it reaches facts that share no words with your question.

The whole argument for a graph rather than a pile of embeddings is that the most important fact is often the one you would never have thought to search for.

You ask about the checkout flow. Search finds the checkout decision. The walk follows an edge from that decision to a risk about the payment retry budget, which does not mention checkout anywhere in its text. You would not have retrieved it. You needed it.

Ranking, and the weights that took several tries

Everything the walk reaches gets scored, and the scoring is unglamorous on purpose: small integers, added up, minus a penalty for distance. The absolute number means nothing. It is an ordering key.

the weight tables
by category            by relation followed        distance
  decision      3        fulfills        3           −0.5 per hop
  architecture  3        blocks          3
  goal          2        supersedes      3          container tasks
  risk          2        caused-by       2           epic/objective
  lesson        2        contradicts     2           /milestone: −3
  priority      2        relates-to      1
  gap           1        mentions        1
  fyi           1        embeds          1
  opportunity   1        expires-on      1

Two of these entries are the scar tissue, and they are the ones worth explaining.

Container tasks score negative three. Epics, objectives, and milestones are roll-up nodes. They aggregate progress from their children and carry almost no signal about the work in front of you. But because a child task blocks its parent, they are reached through the strongest relation tier, which meant they arrived with a high score and flooded the window with entries like Q3 Platform Work. A strong negative base cancels even that edge, so they sink to the bottom and surface only when nothing more useful is nearby.

Components score zero. A component is a subsystem folder. Asking about MCP would match the component literally named Plugin MCP, which then tied with real knowledge and took a slot for the privilege of telling you that a subsystem exists.

The pattern in both fixes is the same, and it is the single most repeated decision in this system: downrank, do not exclude. An excluded node cannot come back when it genuinely is the answer. A downranked one can, and it costs a slot only when nothing beats it.

The risk callout

There is one category the walk treats specially, and it is the feature that most changes what an agent does.

If the walk reaches a risk one or two hops out, through an edge that means something causal rather than merely topical, that risk is lifted out of the ranked list and rendered as its own warning block. Only four relations qualify. A risk reached through relates-to or mentions is a risk in the neighbourhood; a risk reached through caused-by, blocks, contradicts, or a supersession edge is a risk on the path you are about to walk down.

What lands in front of the model looks roughly like this:

injected before the prompt
── from the project record ───────────────────────────────
DECISION  Rate limiting sits in front of auth, not behind
          Nov 2025: moving it behind auth let unauthenticated
          traffic reach the token-metered path. 4h outage.

TASK-418  in progress · claimed by codex 40m ago
          Rate limiter rewrite

⚠ RISK on your path
  "Auth middleware ordering is load-bearing" (via caused-by)
  Changing the order here has broken production once.
  Confirm the limiter still rejects before anything metered runs.
──────────────────────────────────────────────────────────

The cap on returned entries has to know about this. A risk callout is by definition a walked node rather than a seed, so a cap that filled up on seeds first would drop the risk exactly when the record was rich enough for it to matter, which is to say always.

Twenty entries, and no way to ask for more

The window is capped at twenty nodes. People ask why it is not configurable upward, and the answer is that we do not think a bigger number is a better product.

Injected context competes with your actual prompt for the model's attention. Forty entries is not twice as helpful as twenty. It is twenty helpful entries plus twenty distractions, and the distractions dilute the ones that mattered. If the right fact is not in the top twenty, the fix is better ranking, not a longer list.

False sufficiency is the real risk here

The failure we watch for is not the agent missing context. It is the agent treating what arrived as the complete picture and skipping the search it would otherwise have run. Injected context now says out loud that it is a starting set rather than everything, which helps. How much it helps is one of the things we are still measuring.

Going deeper on purpose

Auto-injection is a starting set. When the agent needs the full picture, it dispatches a read-only search helper that interrogates the record in its own context and returns a short, cited briefing.

The context isolation is the whole point. Investigating a question properly means dozens of searches and a lot of raw hits, and doing that in the main thread fills your window with search noise before any work starts. The helper absorbs that and hands back a paragraph.

It cannot write. In harnesses that can restrict tool access, that is enforced by the harness. In the others it is instruction-only, which is worth knowing rather than glossing.

Claiming the work

When something real starts, the agent creates a task and claims it, and the claim is atomic. Two agents cannot hold the same task, and the second one to try gets told who has it and since when rather than just being refused.

A task is also the unit of continuity. It is what makes what was I doing answerable tomorrow, by you or by a different agent, with the decisions and risks that were linked to it still attached.

Writing back, which is the part that is genuinely hard

Everything above is retrieval, and retrieval is the easy half. The hard half is that a record is only worth reading if something is putting good things into it, and the moment when an agent knows something worth recording is the moment it is busy doing something else.

Three things help, and none of them is a rule that blocks you.

Nudges at the right moment. Before the first code change of a session, a one-time prompt to check the record first, so the agent does not re-decide something already settled. Around completion, a prompt to write down what was learned. The agent can proceed regardless. The point is to make the honest update the path of least resistance rather than to enforce it.

Concrete triggers in the text. A lesson written abstractly is invisible exactly when it would help. A lesson containing the literal error string surfaces the moment somebody hits that error again. So the guidance asks for the real thing:

two versions of the same lesson
✗ "Be careful with type generation, it can break the build."

✓ "Regenerating DB types breaks the SDK build: columns filled by a
   BEFORE INSERT trigger come out required, and tsc fails with
   TS2769: No overload matches this call … Property 'short_id' is
   missing. Port new columns by hand instead."

The second one is findable by the person having the problem, because it contains the words they are about to paste into a search.

Anchors. A fact can name the files and symbols it is about, which makes editing that file the trigger rather than mentioning it. Fuzzy matching catches the fact you described in other words. Anchors catch the fact you were not describing at all.

What the loop actually gives you

Every part of this runs by default, on every prompt, in whichever agent you happen to be using. Relevant decisions arrive before the model answers. A risk on the path you are about to take is lifted out of the ranking and put in front of you as a warning. Work is claimed atomically, so a second agent is told who holds it rather than colliding with them. What the session learns goes back into the record with a retirement condition attached, so it can age out on its own.

It is retrieval on a budget rather than a guarantee that every relevant fact reaches the model on every turn, and it gets better as the record fills. How much all of it changes what an agent actually does is a measurable question, and we are building the apparatus to answer it properly rather than asserting a number.