ZhuoQi Devzhuoqidev.com← Back to deskRSSllms.txt
~/posts/managed-agents-outcomes-dreaming.md
Agent Architecture Deep Dives · Part 4Agent EngineeringUpdated ~7,000 words · 31 min read

Agents That Check Their Own Work and Tidy Their Own Memory — Outcomes and Dreaming from the Docs, Plus a Hands-On Test of Claude Code /goal

TL;DR

Agents are good at handing in work that looks finished, and good at letting their memory drift into a mess. Anthropic gave Claude Managed Agents one mechanism for each problem: Outcomes brings in an independent grader to check the work inside a session, and Dreaming reorganizes memory between sessions. Yet in the Claude Code you use every day, neither name appears. This article first takes both mechanisms apart from the docs, then shows what Claude Code actually offers, how it performed in a hands-on test, and how to rebuild both in your own Agent.

Contents
  1. Who this is for
  2. Three plain sentences to remember
  3. Deriving both mechanisms from constraints
  4. The only five things you need to know
  5. The foundation: how a memory store works
  6. Outcomes: a writer-and-grader loop inside one session
  7. Dreaming: reorganizing memory between sessions
  8. What you can see in Claude Code
  9. Hands-on: how much does the /goal evaluator actually do?
  10. Risk: memory is a long-lived injection channel
  11. Rebuilding on your own stack: two contracts
  12. My manual version: “Dreaming” with a Git repository
  13. Seven takeaways
  14. Sources

Version scope: this article follows the official documentation as checked on 2026-10-06, with checks and a hands-on test on Claude Code 2.1.294 added on 2026-10-09. Outcomes and memory stores are in public beta; Dreaming is a research preview that requires requesting access. Both were announced at Code with Claude on 2026-05-06. Fields and limits can change during beta, so check the current docs before integrating.


Who this is for

The default reader is an engineer who knows LLMs, context windows, and basic tool calling, has built or configured an Agent, usually works in Python, TypeScript, Go, or similar, but has not used Claude Managed Agents.

You do not need to know:

  • the Managed Agents API layout or SDK calls;
  • the terms “grader,” “rubric,” or “memory store”; each is explained on first use;
  • any Anthropic internals. Anthropic has not published the internal code for either mechanism, so every “how it is implemented” passage below is marked as documented behavior, my inference, or illustrative pseudocode.

Three plain sentences to remember

  1. Outcomes answers “is this delivery good enough?” You write down what passing looks like. The platform starts a separate grader that cannot see the working Agent’s reasoning and checks the output against your criteria one by one. If anything fails, the itemized feedback goes back for revision, until it passes or the round limit runs out.
  2. Dreaming answers “will the next run go better?” Outside any session, it reads an old memory store plus up to 100 past sessions and produces a new, reorganized store: duplicates merged, stale entries replaced with the latest value, and patterns no single session could see added. The old store is not changed at all.
  3. Both sit on the same foundation: the memory store. It is a set of text files mounted into the Agent’s sandbox. The Agent uses its ordinary file tools on it, and every write leaves an immutable version.
Memory storeDreamingOutcomes
When it runsDuring a session, as the Agent reads and writesBetween sessions, offline and asynchronousWithin one session
Problem solvedSessions forget everything by defaultIncremental writes pile up into a mess“Looks done” is not “done right”
InputThe Agent’s file reads and writesOne existing store + 1 to 100 session transcriptsTask description + rubric + max rounds
OutputMemory files and their version historyA new memory storeOne of five results + the grader’s itemized explanation
Status (2026-10-06)Public betaResearch preview, access requiredPublic beta

Deriving both mechanisms from constraints

Set the API aside and start from constraints no Agent can avoid.

Constraint 1: context is finite and cleared when a session ends. A Managed Agents session starts with a fresh context by default, and whatever it built up is gone when it ends. To keep user preferences, project conventions, and past mistakes, you have to write them outside the context. That is the memory store.

Constraint 2: incremental writes make memory messier over time. Each session only sees what is in front of it, so it appends “run migrations before deploying,” and a later session appends something nearly identical; a setting changes and nobody deletes the old entry. The official docs say a store accumulates duplicates, contradictions, and stale entries over many sessions. Memory does not tidy itself, so something that can see many sessions at once has to reorganize it periodically. That is Dreaming.

Constraint 3: a model grading itself is unreliable. A writer checking its own output inside its own context sees the reasoning it just produced. It declares success when it believes it succeeded, does not reopen a link it already cited, and does not notice that the quote it remembers differs from the page by a few words. Reliable checking requires a checker that cannot see the writer’s reasoning and holds only the criteria and the output. That is the independent grader in Outcomes.

Constraint 4: the loop needs an exit. A grader can always find something to criticize, so revision needs a cap, and a self-contradictory rubric must be able to end in failure instead of spinning.

Chained together, the minimal causal chain is:

Finite context cleared at session end · Write memory outside the context (memory store) · Incremental writes duplicates, contradictions, stale entries · Cross-session reorganization (Dreaming) · Self-grading is biased · Context-isolated grader + checkable criteria · Capped revision loop (Outcomes)
View source (Mermaid)
mermaid6 lines of code
flowchart LR
    A["Finite context<br/>cleared at session end"] --> B["Write memory outside the context<br/>(memory store)"]
    B --> C["Incremental writes<br/>duplicates, contradictions, stale entries"]
    C --> D["Cross-session reorganization<br/>(Dreaming)"]
    E["Self-grading is biased"] --> F["Context-isolated grader<br/>+ checkable criteria"]
    F --> G["Capped revision loop<br/>(Outcomes)"]

Figure: a conceptual sketch of causes, not Anthropic’s internal architecture.

What you must keep and what you can swap:

Must keep (the design)Can swap (the choices)
The grader’s context is isolated from the writerWhich model grades, and whether it matches the writer
Criteria are written as items that can be checked one by oneWhether the rubric is markdown or JSON
Revision is capped, and an inapplicable rubric can end in failureWhether the cap is 3 or 5
Reorganizing memory never edits the original, and results are reviewed before useWhether the original lives at Anthropic, in Git, or in a database
Every memory write leaves a versionWhether versions come from a managed service or from Git

The only five things you need to know

Plain nameOfficial nameWhat it isLayer
SessionsessionOne complete run of an Agent, with its own sandbox and event streamRuntime
Memory storememory storeA set of text files mounted as a sandbox directory, kept across sessionsManaged storage
RubricrubricA markdown document stating, item by item, what passing looks like and how to verify itYour input
GradergraderAnother Agent the platform starts automatically, with its own context, checking only against the rubricRuntime
DreamdreamAn asynchronous job that reads old memory and past sessions and writes a new storeManaged job

The prose below uses the plain names on the left; official names appear only in request examples and sources.


The foundation: how a memory store works

Where it sits: when you create a session you attach the store as a resource. The platform mounts it as a directory under /mnt/memory/ in the sandbox and automatically adds a note to the system prompt saying what the directory is called, what it holds, and whether it is writable. The Agent reads and writes it with the same tools it uses for every other file; there is no special “memory tool.”

Documented rules:

  • Every change creates an immutable version, so you can audit, roll back to a point in time, or redact sensitive content from history.
  • Limits: 100 kB per memory (about 25k tokens), 10,000 memories per store, 8 stores per session. The docs recommend many small files over a few large ones.
  • Stores can only be attached at session creation, not added or removed while a session runs.
  • Access is read-write or read-only, defaulting to read-write and enforced at the filesystem level.
  • Memory store endpoints use the beta header agent-memory-2026-07-22, which must not be combined with the session header managed-agents-2026-04-01; sending both returns 400. Attaching a store to a session still uses the session header.

The complete minimal request to create a session with a store attached (from the official docs):

bash19 lines of code
curl -s https://api.anthropic.com/v1/sessions \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: managed-agents-2026-04-01" \
  -H "content-type: application/json" \
  --data @- <<EOF
{
  "agent": "$agent_id",
  "environment_id": "$environment_id",
  "resources": [
    {
      "type": "memory_store",
      "memory_store_id": "$store_id",
      "access": "read_write",
      "instructions": "User preferences and project context. Check before starting any task."
    }
  ]
}
EOF

Outcomes: a writer-and-grader loop inside one session

What happens in one loop

In product terms:

  1. Instead of a normal user message, you send a “define outcome” event with three things: a task description for the writer, a rubric for the grader, and a maximum number of rounds. The Agent starts on receipt; no other message is needed.
  2. After the writer finishes a round, the platform starts a brand-new grader. It uses the same model and the same tools as the writer, but its context is separate: it cannot see the writer’s reasoning or know what shortcuts were taken, and holds only the rubric and the output.
  3. The grader returns a verdict per criterion. If everything passes, the session ends. If anything fails, the grader’s explanation goes back to the writer verbatim; after the revision, the platform starts another new grader that checks everything again from scratch.
  4. If the cap is reached without passing, the writer takes one final wrap-up turn with no further grading, and the session ends.
Your program · Platform · Writer (the session's Agent) · Grader (new each round) · Cap reached without passing: max_iterations_reached, writer takes one wrap-up turn, no further grading · Define outcome: task + rubric + max rounds · Task description, start work · Work; write output to /mnt/session/outputs / · Start a new grader with only the rubric and the output · Check each criterion (may use all tools to verify) · satisfied · Session goes idle · needs_revision + itemized explanation · Explanation passed back verbatim · [Everything passes] · [Each round] · alt · loop
View source (Mermaid)
mermaid20 lines of code
sequenceDiagram
    participant U as Your program
    participant P as Platform
    participant W as Writer (the session's Agent)
    participant G as Grader (new each round)
    U->>P: Define outcome: task + rubric + max rounds
    P->>W: Task description, start work
    loop Each round
        W->>W: Work#59; write output to /mnt/session/outputs/
        P->>G: Start a new grader with only the rubric and the output
        G->>G: Check each criterion (may use all tools to verify)
        alt Everything passes
            G-->>P: satisfied
            P-->>U: Session goes idle
        else Something fails
            G-->>P: needs_revision + itemized explanation
            P->>W: Explanation passed back verbatim
        end
    end
    Note over P,G: Cap reached without passing: max_iterations_reached,<br/>writer takes one wrap-up turn, no further grading

Figure: a simplified sequence drawn from the documented behavior; the grader’s internal reasoning is not exposed.

The complete request and events

The define-outcome event (from the official docs; max_iterations is optional, default 3, maximum 20):

json10 lines of code
{
  "events": [
    {
      "type": "user.define_outcome",
      "description": "Build a DCF model for Costco in .xlsx",
      "rubric": { "type": "text", "content": "# DCF Model Rubric\n..." },
      "max_iterations": 5
    }
  ]
}

This goes to POST /v1/sessions/{session_id}/events. You can also upload the rubric through the Files API first and pass {"type": "file", "file_id": "..."}, which makes it reusable across sessions and reviewable like code. Or put this event in initial_events when creating the session to create it and start work in one call.

The event stream then shows three kinds of grading events:

json10 lines of code
{ "type": "span.outcome_evaluation_start",   "outcome_id": "outc_01a...", "iteration": 0 }
{ "type": "span.outcome_evaluation_ongoing", "outcome_id": "outc_01a...", "iteration": 0 }
{
  "type": "span.outcome_evaluation_end",
  "outcome_id": "outc_01a...",
  "result": "satisfied",
  "explanation": "All 12 criteria met: revenue projections use 5 years of historical data, ...",
  "iteration": 0,
  "usage": { "input_tokens": 2400, "output_tokens": 350, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 1800 }
}

For readability, id, processed_at, and similar fields are removed from the three events above; the values are the examples from the official docs.

iteration counts from 0: 0 is the first grading, 1 is the grading after the first revision. The ongoing event is only a heartbeat. You cannot see what the grader is thinking, only its final explanation.

The result on the end event decides what happens next:

ResultNext
satisfiedThe session goes idle
needs_revisionThe writer starts a new round
max_iterations_reachedThe writer takes one wrap-up turn with no further grading, then goes idle
failedThe session goes idle. Returned when the rubric does not apply to the deliverables, for example when the description and rubric contradict each other
interruptedYou interrupted the session (with user.interrupt) while the outcome was active

Only one outcome runs at a time, but you can chain them by sending another define-outcome event after the previous one ends. The session stays conversational afterward and keeps its history. Deliverables are written to /mnt/session/outputs/ in the sandbox; list and download them through the Files API by session ID.

The three-round revision from Anthropic’s cookbook

The following run was performed and published by Anthropic in its cookbook; I did not run it. The writer drafts a one-page brief on the unit economics of US public DC fast charging, citing at most 6 sources, each with a verbatim quote. The rubric has two parts:

  • A 7-item coverage checklist, each item more specific than the task description. For example, the “named operator” item requires the GAAP net income or net loss from a public charging operator’s most recent 10-K or 10-Q, and the citation must be the SEC filing itself on sec.gov, not a press release, earnings-call recap, or news article.
  • A 3-step citation check: every URL must open directly with the web-fetch tool (no mirrors, reposts, or search snippets as substitutes); the quoted string must be found on the page; and the quote must actually support the claim it is attached to.

What the grader actually reported over three rounds:

RoundGrader verdictProblem caughtWhat the writer changed
Round 0Coverage 5/7Demand charges described only qualitatively, with no $/kW figure; EVgo’s net loss cited to a third-party news siteAdded $20/kW; went to sec.gov for EVgo’s FY2024 figure
Round 1Coverage 6/7, citations 6/6The new citation is 8-K Exhibit 99.1, an earnings press release filed with the SEC, not a 10-KFound the actual 10-K on EDGAR
Round 2satisfied, 7/7None3 rounds in total, 12 min 56 s

Round 1 is the one to study. Exhibit 99.1 also lives on sec.gov, so a grader that only checked “is the link on sec.gov” would have passed it. It was caught because the rubric said “must be a 10-K or 10-Q, not a press release,” which forced the grader to open the document and confirm what it actually was.

Writing a rubric that works

The cookbook lists several principles; here they are regrouped in my own words into four:

PrincipleBadGood
Make the grader produce evidence“Check that the brief covers demand charges”: the grader skims, sees a paragraph, ticks the box, and never opens a source“Find the demand-charges section and confirm it gives a $/kW figure or a % of operating cost”
Be more specific than the taskThe task says “operator economics,” and so does the rubricPin down the metric (GAAP net loss), the document type (10-K / 10-Q), and the source domain
Describe the goal, not the stepsRequire a specific command; when it is unavailable, the check silently never runsDefine what counts as proof; the grader has the writer’s full toolset and will find a way
Block the writer’s shortcuts and fix the feedback formatWithout “no mirrors,” a dead link gets swapped for a scraped copy and passesState the forbidden substitutes; require a one-line score first, then one bullet per failure saying what is wrong and what to do, plus a list of things not to flag (style nits, out-of-scope issues)

If you have no rubric yet, the docs suggest giving Claude a known-good example of the deliverable, asking it to analyze what makes it good, and turning that analysis into criteria. This works better than starting from a blank page.

When to use it, and when not to

Good fit:

  • Tasks where “good” can be written as checkable items: complete coverage, accurate detail, verifiable citations;
  • Subjective quality also works, such as matching a brand voice, as long as the rubric pins the voice down to something checkable;
  • Deliverables are files the grader can open and verify with tools.

Avoid, or think twice:

  • You cannot write checkable criteria. With a vague rubric the grader approves everything; you pay for grader tokens and check nothing.
  • Every run hits the cap, and the grader keeps flagging the same kind of issue. The writer cannot act on the feedback, for example because it lacks a tool, a permission, or the data. More rounds only pay for a loop that does not converge.
  • Latency matters. Every round re-grades the whole artifact; the example took 13 minutes for 3 rounds.
  • The grader and writer may share blind spots. This is my inference, not something the docs say: by default the grader uses the same model and tools as the writer, so what the writer cannot judge, the grader may not judge either. Context isolation fixes “being led by the writer’s reasoning,” not “neither side knows.” High-stakes conclusions still need a human or deterministic checks such as tests and validation scripts.

The official numbers: up to 10 points higher task success than a standard prompting loop, with the largest gains on the hardest problems; +8.4% task success on docx generation and +10.1% on pptx. These are internal vendor benchmarks and deserve skepticism; for your own task, run a comparison on your own samples.


Dreaming: reorganizing memory between sessions

What happens in one dream

A dream is an asynchronous job with two inputs:

  • one existing memory store: the thing to verify, deduplicate, and reorganize;
  • 1 to 100 sessions: complete past transcripts to mine for patterns to fold into the new memory.

It produces another memory store, separate from the input. Once the job is running, the platform first clones the input store, and the new store’s ID then appears in the job’s outputs[]. The docs are explicit: a dream never deletes or modifies its inputs.

If you have only transcripts and no existing memory, create an empty store first and pass it as the input.

The complete request and response

Dream endpoints need two beta headers; the session header alone is not enough. The complete minimal request (from the official docs):

bash15 lines of code
curl -s https://api.anthropic.com/v1/dreams \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: managed-agents-2026-04-01,dreaming-2026-04-21" \
  -H "content-type: application/json" \
  --data @- <<EOF
{
  "inputs": [
    { "type": "memory_store", "memory_store_id": "$store_id" },
    { "type": "sessions", "session_ids": ["$session_a", "$session_b"] }
  ],
  "model": "claude-opus-4-8",
  "instructions": "Focus on coding-style preferences; ignore one-off debugging notes."
}
EOF

The immediate response is a queued job object (also from the docs):

json18 lines of code
{
  "type": "dream",
  "id": "drm_01AbCDefGhIjKlMnOpQrStUv",
  "status": "pending",
  "inputs": [
    { "type": "memory_store", "memory_store_id": "memstore_01Hx..." },
    { "type": "sessions", "session_ids": ["sesn_01...", "sesn_02..."] }
  ],
  "outputs": [],
  "model": { "id": "claude-opus-4-8" },
  "instructions": "Focus on coding-style preferences; ignore one-off debugging notes.",
  "session_id": null,
  "created_at": "2026-04-29T17:04:10Z",
  "ended_at": null,
  "archived_at": null,
  "usage": { "input_tokens": 0, "output_tokens": 0, "cache_read_input_tokens": 0, "cache_creation_input_tokens": 0 },
  "error": null
}

Models supported during the research preview: claude-opus-5, claude-fable-5, claude-opus-4-8, claude-opus-4-7, claude-sonnet-5, claude-sonnet-4-6.

Status, observation, and wrap-up

StatusMeaning
pendingCreated and queued
runningProcessing; usage updates as work progresses
completedSucceeded; outputs[] holds the new store
failedErrored. The new store keeps whatever was written before the failure
canceledCanceled. The new store is likewise kept

A dream typically takes minutes to a few hours, driven mostly by the number of input sessions. While it runs, its session_id points at the underlying session doing the work, and you can stream that session’s events to watch what it reads and writes in real time; when the dream ends, that session is archived rather than deleted, so the record remains.

With the result in hand you have two paths:

  • Adopt: attach the new store to future sessions, replacing the old one or alongside it;
  • Discard: delete or archive the new store.

If you delete or archive an input store, or delete an input session, while the dream is running, the dream fails (input_memory_store_unavailable / input_session_unavailable).

Steering with instructions: what it can and cannot do

The optional instructions field (up to 4,096 characters) is applied throughout the job: what to read closely, what to merge or drop, and how to structure the output. Use it for high-level direction such as “focus on coding-style preferences,” “keep this section unchanged,” or “use this file structure throughout.”

It is a synthesis pass, not a line editor. Directives such as “change sentence X to Y” or “fix the count in section Z” generally produce no change. For targeted edits, use the memory store API on the new store directly.

Cost and scheduling

Dreams are billed at the standard token rates of the chosen model, and the usage field reports exact totals. The docs say cost scales roughly linearly with the number and length of input sessions and recommend starting with a small batch before scaling up.

The official blog describes Dreaming as a scheduled process that can update memory automatically or let you review changes first. As of 2026-10-06, I found only the manual create endpoint in the docs, with no scheduling setting; if you integrate it yourself, call the endpoint from a scheduled job and put the review step before adopting the new store.

On customer results, the official blog says Harvey saw task completion rise about 6x in its tests with Dreaming; with memory stores alone (no Dreaming), Rakuten saw 97% fewer first-pass errors, 27% lower cost, and 34% lower latency. These are customer figures reported by the vendor, with no public methodology, and deserve skepticism.


What you can see in Claude Code

First, where these features live: Outcomes and Dreaming are Managed Agents features on the Claude Developer Platform. You call them from your own code and pay per token. Claude Code, the tool you use in a terminal or the desktop app, has neither name. It has a close relative of each, similar in name and purpose but different in the design choice that matters. The findings in this section come from checks on Claude Code 2.1.294 on 2026-10-09: the official docs, the program and configuration on my machine, and the runs in the next section.

The relative of Outcomes: /goal

/goal <condition> sets a completion condition for the current session. After every turn, Claude Code sends the condition and the whole conversation so far to a small model (Haiku by default), which answers “not yet met”, “met”, or “impossible” with a one-line reason; if the condition is not met, the reason goes back to Claude and it keeps working. The official docs describe it as a thin wrapper around “a session-scoped, prompt-based Stop hook.”

Outcomes (Managed Agents)/goal (Claude Code)
Who gradesA fresh grader started by the platform each round, the same model as the writer by defaultThe configured small model, Haiku by default
What the grader seesOnly the rubric and the deliverable, not the writer’s reasoningThe condition plus the whole conversation: what the writer said and what it ran
Can the grader check for itselfYes, it has all of the writer’s tools and can open files and run codeNo, it calls no tools and can only judge what already appears in the conversation
Writer and criteriaThe task description goes to the writer and the rubric to the grader, written separatelyThe condition itself is the writer’s instruction; the writer sees all of it
Round limitmax_iterations, default 3, maximum 20, enforced by the platformNo hard limit; “at most N turns” can only be written into the condition for the evaluator to judge. The hard stops are /goal clear or --max-budget-usd on claude -p
Possible resultsSatisfied / needs revision / limit reached / rubric does not apply / interruptedMet / not yet met / impossible, plus automatic clearing on errors and a stop after several turns without tool use
Grader costBilled at the grading model’s standard price, plus session runtimeRuns on the small model; the docs call it usually negligible

In one sentence: the Outcomes grader is a second person who can go and check; the /goal evaluator is someone who only reads the meeting notes. Earlier I argued that the key to Outcomes is isolating the grader’s context from the writer’s. /goal has exactly no such isolation: whatever the writer ran and said in the conversation is what the evaluator judges. So with /goal, write the condition as something the conversation can prove, such as “npm test exits 0 and its summary line shows 0 failed,” and require the writer to actually run that evidence.

The relative of Dreaming: auto-dream

Claude Code’s auto memory (Claude writing notes for itself in a memory directory) also has a background consolidation feature, called auto-dream in the program: once enough hours have passed since the last pass and enough new sessions have piled up, it sends a sub-agent in the background to read recent session transcripts and merge or correct the files in the memory directory.

Dreaming (Managed Agents)auto-dream (Claude Code)
TriggerYou create a job through the APIAutomatic. The program’s default threshold is at least 24 hours and at least 5 new sessions since the last pass; the remote configuration my account receives says 24 hours and 3 sessions
InputOne memory store plus 1 to 100 sessionsThis project’s memory directory plus session transcripts since the last pass
OutputA separate new memory store; the original is untouched and reviewed before useEdits the files in the memory directory directly
Official docsYes, as a research preview that requires accessNone. The autoDreamEnabled setting is not in the official settings reference; it appears only in the setting descriptions built into the program
Who can use itAccounts granted accessDecided by a server-side rollout flag

The auto-dream thresholds and behavior come from my reading of the 2.1.294 program and configuration. They are not an official commitment and can change at any time.

On my machine, neither path works:

  • API: calling GET /v1/dreams with my personal key returns 404, the same as a path that does not exist at all, while the same key gets 200 from the session and memory store list endpoints. Managed Agents works; the Dreaming research preview is not enabled on this account.
  • Claude Code: the rollout flag cached in ~/.claude.json, tengu_onyx_plover, is {"enabled": false, "minHours": 24, "minSessions": 3, "remoteEnabled": false}, and nowhere in ~/.claude or the memory directory is there a .consolidate-lock file or any consolidation state that auto-dream leaves behind when it runs, so it has never run. Setting "autoDreamEnabled": true does not help: the program checks the rollout flag first and, while the flag is off, never reads that setting. Issue #86209 in the official repository reports exactly this behavior; it was later closed automatically for inactivity and not fixed.

Three things named Dreaming

Search for “Claude Dreaming” and you will find three things with the same name and different origins and uses:

Claude Code auto-dreamManaged Agents DreamingOpenClaw Dreaming
Built byAnthropicAnthropicThe open-source OpenClaw project, unrelated to Anthropic
For whomClaude Code users, released gradually behind a rollout flagTeams building their own Agents on the Claude Developer PlatformPeople who run OpenClaw themselves
TriggerAutomatic, in the background once enough time and sessions have passedCreate a job through /v1/dreamsA scheduled job; the current docs say it is on by default
Edits the original?Edits the files in the memory directory directlyWrites a separate new store; the original is untouchedRewrites MEMORY.md, saving the previous version in plugin state first and writing a summary of additions, merges, and supersessions to DREAMS.md for people to read
Injection defenseNo public description foundThe docs do not discuss the dream job itself (see “Risk” below)Removes candidates labeled untrusted or system-generated before building the consolidation prompt
Official docsNoneYesYes

Timeline:

  • 2026-03-24: users already saw “Auto-dream: on · last ran 1d ago” in /memory on Claude Code v2.1.81 (issue #38461), so it was already out to a small group.
  • 2026-03-31: Claude Code v2.1.88 on npm shipped with a source map, exposing the full source. As retold by analyses such as Layer5’s and Soma’s, its autoDream is the first column above: a sub-agent sent off in the background that runs only after 24 hours, 5 sessions, and acquiring a lock, works in four steps (orient, gather, consolidate, prune), and gets a read-only shell.
  • 2026-04-05: the first commit of the Dreaming doc in the OpenClaw repository. The timing is close, but I have no evidence the two are related.
  • 2026-05-06: Anthropic announces the Managed Agents Dreaming research preview.
  • 2026-10-09: the official Claude Code changelog does not mention dream anywhere.

So not seeing Dreaming in Claude Code is not a missed option; the account simply is not in the rollout. The only review loop you can see is /goal, and it differs from Outcomes in exactly the most important respect.


Hands-on: how much does the /goal evaluator actually do?

I did not run Outcomes this time (see the top of the article for why), so this section tests its relative in Claude Code, /goal. The question: does adding an evaluator after every turn make the output better, and if so, is it the evaluator or simply writing the criteria down?

Setup

  • Task: write a Python function parse_duration that converts durations such as “1h30m” and ”45s” into seconds, with pytest tests. The writer’s instructions are one sentence:

    text1 lines of code
    Write a Python module `duration.py` that exposes `parse_duration(text: str) -> int`, converting compact duration strings such as "1h30m" or "45s" into a whole number of seconds. Invalid input must raise an exception. Also write a pytest file `test_duration.py` for it. Put both files in the current directory.
  • Strict criteria: 23 examples in four groups, plus “the tests cover every rule and pass.” 7 values (including “one space between components is allowed” and “leading and trailing whitespace is ignored”); 13 strings that must raise ValueError (empty, number only, unit only, unknown unit, uppercase “1H”, decimals, signs, out-of-order units “30m1h”, repeated units “1h1h”, a space between number and unit, two spaces); 3 types that must raise TypeError (None, 90, b"1h").

  • Loose criteria: only “common formats convert, invalid input raises an exception, the tests pass.”

  • Hidden test: a scoring script built from the 23 strict examples, seen by neither the writer nor the evaluator, applied to every run’s output after all runs finished. I checked the script first: a reference implementation scores 23/23 and a deliberately sloppy one 13/23.

  • Groups: four groups, three runs each, all with Claude Sonnet 5.5 as the writer. Each run used claude -p in an empty directory without loading my user-level settings or memory.

GroupWhat the writer getsEvaluator
A one sentenceThe sentence aboveNone
B rules in the promptThe sentence plus the full strict criteriaNone
C /goal + strict conditionExactly the same text as B, prefixed with /goalHaiku, reading the conversation after every turn
D /goal + loose conditionThe sentence plus the loose criteria, prefixed with /goalSame

The exact conditions for C and D:

text3 lines of code
C: /goal Write a Python module `duration.py` that exposes `parse_duration(text: str) -> int`, converting compact duration strings such as "1h30m" or "45s" into a whole number of seconds, plus a pytest file `test_duration.py`, both in the current directory. The goal is met only when all of the following hold, each shown in this conversation by actually running the examples and pytest: (1) "1h30m" -> 5400, "45s" -> 45, "2d" -> 172800, "0s" -> 0, "1d2h3m4s" -> 93784, units d=86400 h=3600 m=60 s=1, return type int; (2) one space between components is allowed ("1h 30m" -> 5400) and leading/trailing whitespace is ignored (" 45s " -> 45); (3) each of these raises ValueError: "", "   ", "90", "h", "5w", "1H", "1.5h", "-5m", "+5m", "30m1h", "1h1h", "1 h", "1h  30m"; (4) None, 90 and b"1h" raise TypeError; (5) test_duration.py covers every rule above and `python -m pytest` passes.

D: /goal Write a Python module `duration.py` that exposes `parse_duration(text: str) -> int`, converting compact duration strings such as "1h30m" or "45s" into a whole number of seconds, plus a pytest file `test_duration.py`, both in the current directory. The goal is met when parse_duration converts common duration strings such as "1h30m" and "45s" to seconds, invalid input raises an exception, and the tests in test_duration.py pass.

Results

The 12 runs on 2026-10-09:

GroupHidden test score (3 runs, out of 23)Evaluator verdictTime per runCost per run (subscription usage, priced)
A one sentence22, 20, 19No evaluator14–15 sabout $0.05
B rules in the prompt23, 23, 23No evaluator15–26 s$0.05–0.08
C /goal + strict condition23, 23, 23“Met” after the first turn, all 3 times24–27 sabout $0.07
D /goal + loose condition18, 18, 18“Met” after the first turn, all 3 times13–20 s$0.04–0.07

Each evaluator check took 1.6 to 8.2 seconds. Its verdict is not in the claude -p output stream; it is a goal_status record in the session transcript, ~/.claude/projects/<directory>/<session ID>.jsonl. Here is run 2 of group D verbatim:

json2 lines of code
{"type": "goal_status", "met": true, "iterations": 1,
 "reason": "The transcript shows duration.py and test_duration.py were written with the Write tool to the current directory. The test file asserts ('45s', 45) and ('1h30m', 5400), and test_invalid covers inputs such as '' , 'abc' and '30m1h' with pytest.raises(ValueError). The Bash run returned '19 passed in 0.01s', so the tests pass."}

The durationMs and tokens fields are omitted for readability.

Three observations:

  1. What worked here was writing the criteria down, not the evaluator. Group B had no evaluator at all and scored full marks three times; group C’s evaluator said “met” after the first turn every time and never sent Claude back for another round. Every point group A lost was a rule the sentence did not state: none of its three runs guessed that “one space between components is allowed”; twice it added a “weeks” unit of its own and accepted “5w” as five weeks; once each it accepted uppercase “1H”, accepted the out-of-order “30m1h”, and rejected ” 45s ” with surrounding whitespace. The writer was not incapable; it did not know what you wanted.
  2. With a loose condition, the evaluator approves anyway. Group D got “met” all three times, yet scored only 18/23 on the hidden test, failing identically every time: uppercase “1H” accepted as one hour, None, 90, and b"1h" raising ValueError instead of TypeError, and a space between components rejected. The evaluator’s reason was always that pytest passed (for example “19 passed”), and those tests were written by the writer to its own understanding. This is the caveat from the top of the article, as it shows up in /goal: a vague standard gets approved. Group D even scored below group A, which had no review at all; my guess is that “invalid input raises an exception” nudged the writer toward raising ValueError for everything, but three samples are not enough to conclude anything.
  3. The evaluator only reads the conversation. All six reasons cite things that appeared in the conversation: “a Bash run printed…”, “the Write tool reported File created successfully”, “pytest showed 22 passed.” It did not, and cannot, open duration.py and run it itself.

Limits: the task is small and Sonnet 5.5 finishes it in one turn, so the evaluator never got to do what /goal is really for, which is pushing a Claude that wants to stop early back to work on long jobs such as migrations or clearing a backlog. Each group ran only three times. So this data supports one claim only: on a task finished in one turn, the /goal evaluator will not add requirements you left out of the condition. The Outcomes grader can check with tools, but whether it would catch group D’s mistakes still depends on whether the rubric names them; I did not test that, and it is an inference from the design.


Risk: memory is a long-lived injection channel

As quoted above, the docs warn that one injection into a read-write store can plant malicious content that every later session trusts. Dreaming adds another layer to that risk.

What follows is my inference; the docs do not discuss it: a dream reads complete transcripts from up to 100 sessions. If one of them read an injected web page or tool output, the dream may treat the injected content as “a pattern that recurs across sessions” and write it into the new store, phrased even more convincingly than the original.

Defenses:

  • Split stores by trust level. Give Agents that handle untrusted input only read-only reference stores; attach writable stores only to Agents that never touch external input. This follows the same idea as Meta’s Rule of Two: of “processing untrusted input,” “accessing sensitive data or systems,” and “changing state or communicating externally,” a session should hold at most two; when it needs all three, it should not run autonomously and needs at least human approval or another reliable means of validation.
  • Review dream output before adopting it. Dreaming does not overwrite the original by default, and the review step is your line of defense. Where untrusted input is involved, do not configure automatic adoption.
  • Choose which sessions feed a dream. Sessions that read a lot of external content should either be left out or be paired with instructions that limit extraction to a specific kind of information.
  • Filter candidates by provenance, not just by prompt. OpenClaw’s Dreaming makes this a structural rule: every candidate memory carries a provenance label, and candidates labeled untrusted or system-generated are removed before the consolidation prompt is built, so the model never sees them. When you build your own, recording which session and what kind of input each memory came from stops injection earlier than review after the fact.

Rebuilding on your own stack: two contracts

Outcomes and Dreaming are managed features, but the designs have nothing to do with Anthropic and work with any model and any language. Here they are as inputs, outputs, state, and invariants.

Contract 1: a capped, independent review loop

Content
InputTask description (for the writer), rubric (for the grader), output location, max rounds
StateCurrent round, each round’s verdict
OutputPassed / needs revision / cap reached / rubric does not apply / interrupted, plus an itemized explanation
InvariantsEvery grading round uses a fresh context; the grader gets only the rubric, the output, and tools, never the writer’s reasoning; the loop always terminates

Illustrative pseudocode (Python style, not any SDK’s real API):

python11 lines of code
def run_outcome(task, rubric, max_iterations=3):
    workspace = new_workspace()            # outputs live here; writer and grader can both read them
    feedback = None
    for iteration in range(max_iterations):
        writer_turn(workspace, task, feedback)       # writer: same session, continues with last round's feedback
        verdict = grade_in_fresh_context(rubric, workspace)  # grader: new context, only rubric and output
        if verdict.result in ("satisfied", "failed"):
            return verdict
        feedback = verdict.explanation               # itemized feedback goes back to the writer verbatim
    writer_turn(workspace, task, feedback, final=True)   # one wrap-up turn, no more grading
    return Verdict("max_iterations_reached", feedback)

The key is grade_in_fresh_context: it must not reuse the writer’s conversation history. It is a brand-new request whose system prompt holds only the rubric, and it reads the output through tools. The same holds in Go, TypeScript, or with another provider’s model. If you use a workflow engine like Temporal (which I covered in why we moved from Celery to Temporal), it becomes a capped loop workflow with the writer and grader as separate activities, and rounds and verdicts land in workflow history for free.

Contract 2: memory consolidation that never edits the original

Content
InputA snapshot of current memory, some session transcripts, a focus direction
OutputA new memory
InvariantsNever modify the input; the new memory can be diffed item by item against the old; there is a review gate before adoption; every memory can be traced back to the session it came from

Illustrative pseudocode (Anthropic has not published the pipeline’s internals; this only expresses the contract):

python5 lines of code
def consolidate(store, transcripts, focus=None):
    draft = clone(store)                     # clone first; the docs say the official job also clones the input store
    for t in transcripts:
        draft = synthesize(draft, t, focus)  # merge duplicates, replace stale entries, add cross-session patterns
    return draft                             # a human reviews diff(store, draft) before swapping it in

In my LLM memory research I broke Agent memory into a loop: experience, writing, storage, retrieval, context assembly, action feedback, and finally consolidation, revision, and forgetting. Dreaming pulls the “consolidation and revision” step out on its own and turns it into an offline, reviewable batch job.


My manual version: “Dreaming” with a Git repository

My own Agent work memory is a private Git repository, and in hindsight it is almost a manual Dreaming:

Managed AgentsMy work memory repository
Memory store: many small filesA memory/ directory: one fact per file with frontmatter, plus an index file loaded automatically every session and capped at 200 lines
An immutable version per writeGit commit history
Dream input: past sessionsDaily journals: raw material recording “what happened”
Dream: merge duplicates, replace stale entries, add patternsPeriodic cleanup: merge duplicate entries, delete ones proven wrong, promote key lessons buried in journals into standalone memories
Review the new store before useRead git diff before committing

The differences are just as clear:

  • I edit in place; it writes a separate copy. I rely on Git to roll back; it never touches the original by design, which is safer. The idea worth borrowing is to reorganize on a branch and merge only after reviewing the diff, which amounts to “leave the original untouched.”
  • It reads raw session transcripts; I read only journals. Journals are already filtered by me, so patterns I missed within a single session are invisible to my cleanup.
  • My review relies on a rule. I gave my Agent a rule: never write “merged” or “deployed” before doing it, and treat machine evidence such as PR state, CI, and deploy logs as the source of truth. That is the same idea as an Outcomes rubric: make the checker produce evidence instead of trusting the worker’s claim that it is done.

Seven takeaways

  1. Outcomes fixes self-grading bias. The point is not “one more Agent”; it is a grader whose context is isolated from the writer and who holds only the criteria and the output.
  2. The rubric decides everything. Make it more specific than the task, and make every item force the grader to produce evidence, or it will approve anything.
  3. The loop has to converge. If every run hits the cap with the same kind of issue, the writer cannot fix it, and more rounds will not help.
  4. Dreaming fixes memory entropy. It reads many sessions offline and writes a separate, reorganized store, leaving the original untouched for review before use.
  5. Memory is a long-lived injection channel. Default read-write stores, plus a job that reads raw transcripts, both call for splitting by trust level and keeping a review gate.
  6. Both can be rebuilt without Anthropic. One is a capped, independent review loop; the other is consolidation that never edits the original. Both hold for any model and any language.
  7. Claude Code only has /goal, and its evaluator only reads the conversation. In the test, what worked was writing the criteria down; with a loose condition it still said “met.” auto-dream is still in a rollout, and accounts outside it cannot turn it on with a setting.

The rule: never let the worker grade its own work, and never let memory only grow.


Sources

Checked on 2026-10-06; checks on Claude Code 2.1.294 and the /goal test added on 2026-10-09. Outcomes and memory stores are in public beta and Dreaming is a research preview, so fields, limits, and supported models may change. All Outcomes, Dreaming, and memory store request examples come from the official documentation above, and I did not run them; the pseudocode expresses design contracts only and does not represent Anthropic’s internal implementation.

Say hi on WeChat

Liu ZhuoQi's WeChat QR code

Scan it in WeChat, or long-press it on your phone

Mention why you're reaching out, or I might miss it.

Scroll or pinch to zoom · drag to pan · double-click to toggle