Skip to content

TUI panel 'dreamer last run' shows stale time — reads abandoned V1 field instead of task_schedule_state #194

Description

@wjiuxing

Bug

The magic-context TUI sidebar panel displays a stale "dreamer last run" time that never updates after migrating to Dreamer V2 (v0.27.0+). In practice, it shows the time of the last V1-era monolithic dream cycle, which may be days or weeks old, even though V2 per-task scheduling is executing successfully every night.

Environment

  • @cortexkit/opencode-magic-context v0.29.0
  • OpenCode 1.17.11

Reproduction

  1. Upgrade from a pre-V2 version (e.g. v0.26.x) to v0.27.0+
  2. Let Dreamer V2 tasks run overnight (task_schedule_state and dream_runs populate correctly)
  3. Open the magic-context TUI sidebar panel
  4. Observe "dreamer last run" shows a time matching the last V1 dream cycle, not the most recent V2 task execution

Observed behavior

In my setup, the panel shows "2d ago" (Jun 25 03:28), while:

-- task_schedule_state: most recent V2 task actually ran Jun 27 07:02
review-user-memories  last_run=2026-06-27 07:02:00  status=completed

-- dream_runs: 8 successful runs on Jun 27 (02:01–07:02), 0 failures

But the TUI reads the stale value:

-- dream_state: frozen at V1 migration seeding time
last_dream_at:dir:007ef97e12ec = 1782329286233  (= 2026-06-25 03:28:06)

Root cause

dream_state.last_dream_at is never written in V2

A full-text search of the v0.29.0 dist confirms zero write paths for last_dream_at:

$ grep -n "last_dream" dist/index.js
194421:  const legacy = getDreamState(db, `last_dream_at:${projectIdentity}`);
204702:  const dreamRow = db.prepare("SELECT value FROM dream_state WHERE key = ?").get(`last_dream_at:${projectIdentity}`);
  • Line 194421 (ensureSeeded in task-scheduler.ts): One-time migration read. When a V2 task is first seeded into task_schedule_state, it reads the legacy last_dream_at as the initial lastRunAt. This is correct and runs once.
  • Line 204702 (TUI panel data source): Reads dream_state.last_dream_at to populate lastDreamerRunAt for the sidebar display. This is the bug.

setDreamState() is called in exactly 4 places — all for lease management (holder, heartbeat, expiry), never for last_dream_at:

// lines 189637–189651: lease acquire/renew only
setDreamState(db, keys.holder, holderId);
setDreamState(db, keys.heartbeat, String(now));
setDreamState(db, keys.expiry, String(now + LEASE_DURATION_MS2));

The V2 task scheduler (task_scheduler.ts) updates task_schedule_state.last_run_at and inserts rows into dream_runs on every execution — these are correct. But the old dream_state.last_dream_at is a dead field that was only written by the removed V1 dream cycle.

The TUI reads the wrong source

// Line 204702 — current (stale) read
const dreamRow = db.prepare("SELECT value FROM dream_state WHERE key = ?")
  .get(`last_dream_at:${projectIdentity}`);
if (dreamRow?.value) {
  lastDreamerRunAt = Number(dreamRow.value) || null;
}

Suggested fix

Read from task_schedule_state instead:

// Fixed: read the most recent task execution
const row = db.prepare(
  "SELECT MAX(last_run_at) as max_at FROM task_schedule_state WHERE project_path = ?"
).get(projectIdentity);
if (row?.max_at) {
  lastDreamerRunAt = Number(row.max_at) || null;
}

Alternatively, add setDreamState(db, \last_dream_at:${projectIdentity}`, String(Date.now()))at the end of each V2 task execution — but queryingtask_schedule_state` directly is cleaner since the data already exists there.

Impact

  • Severity: Low (cosmetic — task execution is unaffected)
  • Confusion: High — users may believe dreamer tasks are not running when they actually are, leading to unnecessary debugging
  • The misleading display can mask real task execution failures, since users learn to distrust the "last run" indicator

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions