Autonomous Dreaming

Epic #2169. Child issues: #2170#2179, reload fix #2181.

Product intent

Autonomous machine review is the design center. Human proposal/approve/deny UX is an escape hatch — not the happy path for continual learning (#2177).

The candidate store is for autonomous safety, not a human review inbox. The machine gates, promotes, observes outcomes, and rolls back.

Compose stages under shadow/candidate mode run with dryRun: true and must emit structured proposed ops (add / merge / delete with OCC preHash where applicable). A synthetic curriculum summary is metrics-only when stages emit zero proposals — it is never a promoteable candidate.

Task path vs Dream path

┌─────────────────────────────┐     ┌──────────────────────────────────┐
│ In-band (task / session)    │     │ Out-of-band Dream (#2171)         │
│                             │     │                                  │
│ short-loop capture          │     │ snapshot store_revision          │
│ memory_store / auto-capture │     │ attach permission-scoped sessions│
│ immediate FactsDB write     │     │ compose distill→…→consolidate    │
│                             │     │ emit candidates (#2170)          │
│                             │     │ machine gates → promote/rollback │
└─────────────────────────────┘     └──────────────────────────────────┘

Task path learns for this session. Dream path spends dedicated maintenance budget (CostFeature.dream) to improve curriculum for future sessions.

Control plane

sessions + store snapshot
        ↓
   Dream run (#2171) + steering (#2176)
        ↓
 candidate entries (#2170)
        ↓
 machine gates (#2172 prevalence / evidence / permission #2174)
        ↓
 auto-promote | quarantine  (+ OCC #2175)
        ↓
 outcome window (#2173) → auto-rollback if regression  ← self-heal = “deny”
        ↓
 ROI report (#2179)

Existing Dream Cycle, distill, reflection, consolidate, Loom, and Event Bus producers are composed under Dream, not deleted. When dreaming.enabled and skipNightlyOverlap are true, overlapping nightly steps are skipped only if the nightly dream-run maintenance step is scheduled (fail closed: otherwise the owned steps still run so curation is not dropped).

There is no separate Rumination Engine process (#2178): Event Bus status lifecycle is advanced by distributed consumers (maintenance + Dream).

Blast-radius / prevalence (#2172)

Machine gates require evidence before promote. Defaults:

Scope minSessions minAgents
session 1 1
agent 2 1
user 2 1
global 3 2

Optional minConfidence per tier. personalSingleTenant lowers the global agent bar to 1. Missing provenance or live contradiction-worsening (heuristic / verified peer / unresolved delete) blocks promote.

Permission-scoped attachment (#2174)

permissionBoundary.targetScope (default session) controls which transcripts may feed a dream:

  • Fail closed: unresolved ACL → exclude
  • More-private content cannot feed a more-public dream (SCOPE_RANK[source] >= SCOPE_RANK[target])
  • Writes cannot exceed the dream target scope
  • dream run persists attachment.excluded[] (sessionId + reason) on the run metrics; included ids live in session_ids_json
  • Nightly dream-run discovers recent sessions from FactsDB (provenance_session / session scope_target), then applies the same selectDreamSessions filter — it does not scoop an empty or unrestricted time window
  • Dream distill / self-correction compose pass the attached session allowlist (empty → process zero transcripts)
  • Dream reflect mines only attached-session facts (provenance_session / session scope_target); nightly reflect remains globalOnly
  • ACL resolution never invents global from scope=global fact writes alone (those only prove the session existed). Transcript found under agents/<id>/sessions/ floors to agent. Explicit --session-scopes id:global (or personalMode) is required for global attachment.

Example: dream targeting global with sessions {s1:session, s2:agent} attaches neither unless scopes are explicitly upgraded or personalMode: true.

Optimistic concurrency (#2175)

  • Dream run snapshots input_store_revision at start
  • Promote refuses if live store revision drifted (unless --force)
  • Per-fact supersede/delete uses candidate preHash (propose-time OCC token), not a live token at apply time

Self-heal (outcome feedback) — autonomy substitute for deny

After promote (#2173):

  1. Capture a pre-promote baseline (session-scoped feedback signals).
  2. Tag applied facts dream-run:<id> for attribution. While autoRollback is enabled, recall mildly prefers those tagged facts (canary boost) so post-promote curriculum is exercised.
  3. Observe for autoRollback.observeWindowHours.
  4. Nightly dream-outcome-probe (when autoRollback enabled) collects after-window metrics scoped to the dream’s sessions, compares effect score, and auto-rollbacks via reverse plan on regression.
  5. Decisions (keep / rollback / insufficient_data) are journaled on dream_runs.metrics_summary_json. Insufficient data never false-rollbacks.

Outcome signal: Outcome metrics now read terminal success/failure workflow traces from the dedicated workflow-traces database, strictly allowlisted to the dream’s attached sessions (signalSource: task_success). When explicit feedback is also present, the report is a task-success-weighted blended signal. Unknown traces never count as success; feedback remains the safe fallback when no terminal task outcome exists.

How to read Dream ROI

hybrid-mem dream report --since-days 30 --json (#2179):

Field Meaning
totals.cost.tokens / usdProxy LLM spend attributed to feature=dream during compose
totals.promoted / rolledBack / quarantined Lifecycle outcomes
totals.candidatePromoteRatio Candidates that cleared gates vs proposed
perRun[].metricsSummary Per-run cost/hygiene + optional outcome decision
howToRead Inline guidance string

Shadow mode first: compare ROI and rollback rate before turning autoPromote on.

# Accumulate shadow runs (autoPromote stays false; candidateStore.shadow true)
openclaw hybrid-mem dream run --dry-shadow --json
openclaw hybrid-mem dream validate-shadow --json   # exit 1 until criteria pass

# Only after validate-shadow exits 0:
#   dreaming.candidateStore.shadow: false
#   dreaming.autoPromote.enabled: true

autoPromote.shadowValidation (enabled by default) refuses live apply until the lookback window has enough shadow runs with acceptable quarantine/failure/zero-candidate rates. --force promote bypasses the gate (escape hatch / canary only).

Steering (set-and-forget)

dreaming.steering (#2176) — default personal profile for single-operator autonomy:

  • promote: preference, routine, insight
  • ignore: one_off_debug, transient_path, secret_like
  • notes: freeform LLM guidance injected into distill/reflect prompts

No nightly UI confirmation. Profile id is stored on each dream_run.

Config (defaults conservative)

{
  dreaming: {
    enabled: false,
    mode: "autonomous",          // #2177 — machine gates are happy path
    compose: ["distill", "contradictions", "reflect", "consolidate"],
    maxSessions: 20,
    maxRuntimeMinutes: 30,
    skipNightlyOverlap: false,   // opt-in: do not drop live nightly until Dream owns promote
    promoteAfterRun: false,
    candidateStore: {
      enabled: false,
      shadow: true
    },
    autoPromote: { enabled: false, requireProvenance: true, blockOnContradictionWorsening: true,
      shadowValidation: { enabled: true, minShadowRuns: 7, minWouldPromoteRuns: 3 } },
    autoRollback: { enabled: false, observeWindowHours: 24, regressionThreshold: 0.15 },
    prevalence: { /* session/agent/user/global bars — #2172 */ },
    permissionBoundary: {
      targetScope: "session",    // raise for global curriculum (#2174)
      enforce: true,
      personalMode: false
    },
    steering: {
      profile: "personal",
      promote: ["preference", "routine", "insight"],
      ignore: ["one_off_debug", "transient_path", "secret_like"]
    }
  }
}

When mode: "autonomous" and Dream is enabled, nightly pending-digest is skipped so human triage is not implied for correctness. Set mode: "supervised" to restore digest-first workflows.

Compose under candidate/shadow mode runs maintenance stages with dryRun: true so the live store is unchanged until machine promote.

Shadow matrix

candidateStore shadow autoPromote Behavior
off Today’s live mutate paths unchanged
on true off Candidates + gates + would-promote; no live apply
on false on Real promote when gates pass
+ autoRollback on Observe → reverse plan on regression (#2173)

CLI

openclaw hybrid-mem dream run --json --dry-shadow
openclaw hybrid-mem dream run --sessions s1,s2 --compose distill,reflect --json
openclaw hybrid-mem dream status
openclaw hybrid-mem dream status <id>
openclaw hybrid-mem dream promote <id>        # machine path; --force under shadow
openclaw hybrid-mem dream rollback <id>       # escape hatch
openclaw hybrid-mem dream observe <id>        # auto-collect metrics; --apply to rollback
openclaw hybrid-mem dream observe --all       # probe elapsed windows
openclaw hybrid-mem dream report --since-days 30 --json
openclaw hybrid-mem dream validate-shadow --json

Host rollout

  1. Deploy a build that includes Autonomous Dreaming (dream_runs / memory_candidate_entries schema).
  2. Migrate config from legacy dream-cycle (frequency / phases) to:
{
  dreaming: {
    enabled: true,
    mode: "autonomous",
    candidateStore: { enabled: true, shadow: true },
    autoPromote: { enabled: false },   // keep off until validate-shadow passes
    autoRollback: { enabled: false },
  }
}
  1. Accumulate nightly dream-run (or CLI dream run --dry-shadow) for ≥ shadowValidation.minShadowRuns days. Nightly dream-run always gates after compose (shadow when autoPromote is off) so runs leave running and validate-shadow can score wouldPromote. With skipNightlyOverlap: true, owned live steps are skipped only after that gated dream-run succeeds — do not leave promoteAfterRun/candidateStore misconfigured expecting live distill to keep running.
  2. openclaw hybrid-mem dream validate-shadow --json — exit 0 required.
  3. Only then: candidateStore.shadow: false and autoPromote.enabled: true.

After promote is live:

  • Inspect / promote / rollback / observe / report via CLI above
  • dreaming.mode: "supervised" for operators who want digest-first workflows
  • Manual --force promote under shadow for debugging

Legacy config compatibility

Releases after the Autonomous Dream migration continue to accept the legacy dreaming.frequency, model, execution, phases, and verboseLogging keys so an upgrade cannot prevent the gateway from starting. The plugin emits an actionable startup warning whenever it sees them; compatibility acceptance is intentionally not a silent config discard.

Legacy key Compatibility behavior
frequency A five- or six-field cron expression is used as nightlyCycle.schedule if that setting is absent. Other frequency formats are retained for boot compatibility but require operator migration.
model Used as nightlyCycle.model if that setting is absent.
phases Maps to dreaming.compose only when every phase is a supported modern compose stage and compose is absent. Unsupported values remain non-operative and are named in the warning.
execution No lossless equivalent; retained for boot compatibility and called out in the warning.
verboseLogging No lossless equivalent; retained for boot compatibility and called out in the warning.

Explicit modern settings always win. Replace legacy keys with nightlyCycle.schedule, nightlyCycle.model, and dreaming.compose during a normal maintenance window, then remove the legacy keys after confirming the startup warning is gone.


Back to top

OpenClaw Hybrid Memory — durable agent memory

This site uses Just the Docs, a documentation theme for Jekyll.