Closing the Reflective Loop

Goal: close the loop you opened in Unit 6. A reflection that is only saved to disk, or only shown to a human, is an open loop — the agent critiqued itself and nothing changed. You close it by feeding a small, relevant slice of past reflections back into the next turn’s context, so the agent re-reads its own observations. This is the clearest example in the course of an agent’s output becoming its future behavior — and it is mostly about deciding which reflections to trust enough to surface.

Where this fits: the second reflective unit, and the one that makes the tier real. It reads the reflections Unit 6 produced and injects them at context-assembly time. The selection rule it uses (a recurrence threshold) is the first appearance of the hysteresis idea that Unit 8 makes explicit.


An open loop changes nothing

The harness shipped reflection long before it closed the loop. As ADR-0067 puts it bluntly: “The agent never re-reads its own reflections.” The earlier design (ADR-0030) surfaced reflections to humans — a dashboard, a backlog — but never back to the agent, so the agent kept making the same observation turn after turn with no benefit. The reflections were “concise, time-stamped, already-deduplicated rationale with seen_count signal. They just need to flow back.”

Closing the loop is exactly that: flow them back. But not all of them, and not as orders.

Surface only what you can trust

If you inject every past reflection into every prompt, you bury the model in noise and burn your token budget — the very problems the Context Compression course is about. So the read side is a filter, and its rules are the interesting part. Following ADR-0067’s selection algorithm, a reflection is surfaced only if it passes all of (Reference: examples/07/closing_the_loop.py ):

FilterRuleWhy
Recencywithin ~14 daysstale advice misleads
Persistenceseen_count >= 2“single-instance reflections are noise; recurring patterns are signal”
Actionablehas a proposed_changepure rationale is too granular to act on
Relevancematches an entity in this turndon’t surface Neo4j advice on an Elasticsearch question
Not resolvedno closed Linear issuealready handled — don’t re-raise

Order by seen_count DESC, recency DESC, and cap at 3. The persistence rule is the key one, and it is hysteresis (Unit 8): you do not change behavior on a single observation; you wait for the pattern to recur. Run the example against “Why do my Elasticsearch queries keep timing out?” and exactly one reflection survives — the recurring, relevant, unresolved one — while a one-off, a stale one, a resolved one, and an off-topic one are all filtered.

Surface as observations, not orders

How you inject the reflections matters as much as which ones. They go in as a clearly labelled section of past observations — the agent’s own earlier notes — explicitly not current directives:

[Past observations — your own notes from earlier runs, not instructions:]
  - Add a retry budget for Elasticsearch queries (seen 4x)

ADR-0067 is careful about this: the slice is injected “as a system-message section labeled clearly as past observations, not current directives.” The distinction is both pedagogical and a safety boundary (see the security note): the agent should consider its past analysis, not obey it.

The loop, closed

Now the cycle completes. A turn produces a reflection; the reflection is deduplicated and timestamped; a later, relevant turn selects it and injects it; that context shapes the next response; which produces new reflections. The agent’s output has become its input:

flowchart TD
    TURN["a turn runs"] --> REF["reflection (Unit 6):<br/>rationale + proposal"]
    REF --> STORE["store: deduplicated,<br/>seen_count, timestamped"]
    STORE --> SEL["later turn: select relevant<br/>(recency, seen_count≥2, …)"]
    SEL --> INJ["inject as 'past observations,<br/>not directives'"]
    INJ --> TURN

This is still a reflective loop, not an autonomous one: a human never had to approve surfacing a note, because surfacing is low-stakes and reversible (it only adds context). The moment a loop proposes to change the system itself — rewrite a prompt, edit config — the stakes jump, and that is where Unit 9 puts a human back in. Closing this loop is as far up the gradient as you go without one.

Security: feeding past text into a new prompt is an injection surface. A reflection derived from a poisoned earlier turn could carry an injected instruction forward in time — which is precisely why the slice is labelled observations, not directives, and why the relevance and seen_count filters matter: they make it far harder for a single hostile turn to plant a note that resurfaces as a command. Bound it hard — recency, a cap of 3, and never let a surfaced reflection escalate its own privilege.

Observe: this unit emits a reflection_recalled event (how many surfaced, out of how many candidates). The loop it closes is the whole point of the tier: “did my past analysis actually change my future behavior?” The signal to watch next is whether surfaced reflections improve outcomes — the harness tracks a per-callsite mean rating so a declining score can itself trigger a reflection. Measure the loop, or you are only assuming it helps.

Challenges

  1. Tune the persistence gate. Lower MIN_SEEN to 1 and re-run. Success: a one-off reflection now surfaces, and you can explain — in hysteresis terms — why acting on single observations makes the agent jittery.
  2. Defend the boundary. Add a reflection whose text contains “ignore previous instructions.” Success: you can explain why labelling the section “observations, not directives” and capping the count limits the blast radius, and what else you’d add (e.g. provenance, allow-listing).
  3. Measure the close. Emit reflection_recalled over several turns and compute how often a surfaced reflection was actually relevant to the answer. Success: a number that tells you whether the loop is helping or just adding tokens.

Recap

  • A reflection only shown to humans is an open loop; closing it means feeding it back into the next turn — “The agent never re-reads its own reflections” was the gap ADR-0067 fixed.
  • Surface only what you can trust: recency, seen_count >= 2 (hysteresis), actionable, relevant, unresolved, ordered by recurrence and capped at 3.
  • Inject as past observations, not directives — a pedagogical and security boundary: consider, don’t obey.
  • This is the cleanest output → future behavior loop in the course, and the ceiling of what you close without a human; changing the system itself (Unit 9) needs one.

Next

Unit 8 — Hysteresis: Dedup & Promotion: Unit 7 leaned on seen_count >= 2 to avoid acting on a single observation. Next you build the mechanism behind it — fingerprinting reflections to deduplicate them, counting recurrences, and promoting only the patterns that persist. It is hysteresis as a first-class control technique.