« Phase 01 · Warmup · Track Overview

Lab 01 — The Agent Kernel

The problem

Thirty teams want to run agents on your platform. Each of them will, if you let them, write their own loop — and each of those loops will forget the step budget, keep state in process memory, grow the context until the run dies, and log nothing an auditor can use.

Your job is to make that unnecessary. You build the kernel: the runtime that owns the lifecycle, enforces the budgets, externalizes the state, manages the memory tiers, and emits the execution chain. Agent authors bring a goal, a tool set, and a policy. Everything else is yours.

The OS analogy is exact and worth holding onto: a process does not choose its own memory limit, is not trusted to yield the CPU voluntarily, and cannot decide where its page tables live. Neither should an agent.

What you build

#ComponentWhat it does
1RunState, Event, TRANSITIONS, transitionan explicit lifecycle state machine; anything not in the table is an IllegalTransition, and terminal states accept nothing
2Step, Scratchpadworking memory with token accounting and compaction that never eats the recent window
3Fact, SemanticMemorylong-term facts partitioned by (scope, owner); visibility is filtered before ranking, never after
4Episode, EpisodicMemorymemory of what happened, recalled by tag overlap then recency
5SessionSnapshot, SessionStoreexternalized state with optimistic concurrency — a stale write is rejected, not merged
6AffinityRouterconsistent-hash session routing with virtual nodes, drain(), and a stable hash so restarts do not reshuffle
7Budgets, BudgetBreachkernel-enforced steps, tokens, cost, and deadline
8Decision, Policythe injected model, as a pure function of the rendered scratchpad
9AgentKernelthe loop: budget-check → decide → dispatch → observe → compact → checkpoint, with HITL pause/resume and an execution_chain read from the store

Key concepts

ConceptWhereWhy it matters
Explicit transition tableTRANSITIONSan undeclared transition is a bug you can prove does not exist
Recoverable vs fatal errorrun() unknown-tool brancha hallucinated tool name feeds back as an observation; a budget breach ends the run
Budget-before-callrun() loop headchecking after the model call means paying for the step you are about to reject
CompactionScratchpad.compact_if_neededattacks the quadratic scratchpad term; lossy for the model, never for the audit record
Scope-partitioned memorySemanticMemory.searchthe same defect class as an un-namespaced vector index
Optimistic concurrencySessionStore.savetwo workers, one session, exactly one winner
Externalized stateSessionSnapshotmakes affinity an optimization instead of a requirement
Consistent hashingAffinityRouteradding a replica moves ~1/n of sessions; modulo moves ~(n−1)/n
Stable hash_hash_to_inthash() is salted per process — a ring built on it reshuffles every restart
DrainingAffinityRouter.drainrolling deploys need "no new work here", not "gone"
Execution chainexecution_chain()the debugging artifact and the audit artifact are the same object

Files

FileRole
lab.pyyour implementation
solution.pyreference; python solution.py prints eight worked sections
test_lab.py60 tests
requirements.txtpytest

Run

pip install -r requirements.txt
pytest test_lab.py -v
LAB_MODULE=solution pytest test_lab.py -v
python solution.py

Success criteria

  • All 60 tests green against your lab.py.
  • Every terminal state rejects every event — the test iterates all of them.
  • No state is a trap: every non-terminal state has an edge to a terminal state.
  • compact_if_needed returns False when under budget and when the only steps left are the recent window.
  • SemanticMemory.search returns [] for a caller holding no scope, whatever the tags.
  • A stale save raises ConcurrentModification — it does not silently overwrite.
  • Two AffinityRouters built with different insertion order route identically.
  • Adding a replica to a 3-node ring moves between 10% and 45% of sessions, and every moved session goes to the new replica (no churn between existing ones).
  • The policy is called exactly max_steps times when an agent loops — not max_steps + 1.
  • execution_chain still has every step after a compaction.

How this maps to the real stack

This labThe real thingWhat we simplified
TRANSITIONSLangGraph's compiled graph and its interrupt states; Bedrock AgentCore session states; ADK's event-driven runnerreal runtimes fold the state machine into a graph executor, so the states are implicit in the graph topology — which is exactly why an explicit table is worth building once
Scratchpad + compactionLangGraph message trimming / summarization nodes, Claude Code's context compaction, LlamaIndex chat memory buffersreal compaction calls a model and is tuned against a quality eval; ours is deterministic so the test can assert it
SemanticMemory scopesADK's user:/app:/temp: state prefixes; AgentCore Memory's short/long-term strategies; mem0-style storesreal stores add embedding search and TTLs; the scope partition is the part that must not change
EpisodicMemoryagent "experience" stores and trajectory replays used for few-shot selectionreal recall uses embeddings, not tag overlap
SessionStore + CASa Postgres/DynamoDB session row with a version column, or a LangGraph checkpointer (MemorySaver, PostgresSaver)real checkpointers also store the graph's pending tasks so resumption is exact mid-node
AffinityRouterEnvoy/Istio consistent-hash load balancing (ring_hash, maglev) on a session header; Kubernetes sessionAffinityreal rings run thousands of virtual nodes and are weighted by replica capacity
BudgetsLangGraph recursion_limit, provider max-tokens, gateway cost ceilings, request deadlinesreal budgets are enforced in several places at once (kernel, gateway, mesh timeout)
execution_chainOpenTelemetry spans with GenAI semantic conventions, exported to Tempo/Jaeger/Datadogreal chains are spans in a distributed trace, not a list in a row

Honest limits. The kernel is single-process and synchronous. It does not do parallel tool calls, streaming, multi-agent fan-out, or true durable execution across a crash mid-tool-call (the checkpoint happens after the observation, so a crash during dispatch replays the call — which is precisely why the action gateway's idempotency keys in Phase 10 are not optional).

Extensions

  1. Crash-safe dispatch. Checkpoint before the tool call with the call recorded as in_flight, and on resume either re-dispatch with the same idempotency key or reconcile. That is the difference between "checkpointed" and "durable."
  2. Parallel tool calls. Let a Decision carry several tool calls, dispatch them concurrently, and merge observations deterministically (sort by tool name) so the trace stays diffable.
  3. Streaming. Yield partial steps from run() as a generator and assert the stream is a prefix-consistent view of the final result.
  4. Capacity-weighted ring. Give each replica a weight and allocate virtual nodes proportionally; verify the distribution matches the weights.
  5. Memory promotion. Add a rule that promotes a fact observed in three episodes into SemanticMemory, with provenance — then think hard about what that means for the audit trail.
  6. A second planner. Implement ReWOO (plan the whole chain up front, execute, then solve) as an alternative run() mode, and compare step counts and token totals on the same policy.

Interview / resume bullets

  • "Built the platform's agent kernel: an explicit lifecycle state machine with a provable transition table, kernel-enforced step/token/cost/deadline budgets, externalized session state with optimistic concurrency, and an execution chain that doubles as the audit artifact."
  • "Made session affinity an optimization rather than a requirement by externalizing state, then implemented consistent-hash routing with draining so a rolling deploy moves ~1/n of sessions instead of all of them."
  • "Cut long-run token cost by adding scratchpad compaction that folds old steps into a summary while preserving the recent window — attacking the quadratic growth term without losing the full record, which stays in the checkpointed chain."
  • "Classified agent failures into recoverable (a hallucinated tool name feeds back as an observation) and fatal (a budget breach ends the run), which turned a class of agent bugs into self-correction instead of incidents."