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 runpersistsattachment.excluded[](sessionId + reason) on the run metrics; included ids live insession_ids_json- Nightly
dream-rundiscovers recent sessions from FactsDB (provenance_session/ sessionscope_target), then applies the sameselectDreamSessionsfilter — 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/ sessionscope_target); nightlyreflectremainsglobalOnly - ACL resolution never invents
globalfromscope=globalfact writes alone (those only prove the session existed). Transcript found underagents/<id>/sessions/floors to agent. Explicit--session-scopes id:global(orpersonalMode) 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_revisionat 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):
- Capture a pre-promote baseline (session-scoped feedback signals).
- Tag applied facts
dream-run:<id>for attribution. WhileautoRollbackis enabled, recall mildly prefers those tagged facts (canary boost) so post-promote curriculum is exercised. - Observe for
autoRollback.observeWindowHours. - 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. - Decisions (
keep/rollback/insufficient_data) are journaled ondream_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
- Deploy a build that includes Autonomous Dreaming (
dream_runs/memory_candidate_entriesschema). - 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 },
}
}
- Accumulate nightly
dream-run(or CLIdream run --dry-shadow) for ≥shadowValidation.minShadowRunsdays. Nightlydream-runalways gates after compose (shadow whenautoPromoteis off) so runs leaverunningandvalidate-shadowcan scorewouldPromote. WithskipNightlyOverlap: true, owned live steps are skipped only after that gated dream-run succeeds — do not leavepromoteAfterRun/candidateStoremisconfigured expecting live distill to keep running. openclaw hybrid-mem dream validate-shadow --json— exit 0 required.- Only then:
candidateStore.shadow: falseandautoPromote.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
--forcepromote 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.