Skip to content

OpenCode scheduled tasks: migrate the Muse→OpenCode cron wrappers into OpenChamber scheduled tasks (waves, monitoring gates) #2174

Open

Description

@dezren39

What this is

Drew, 2026-10-29: find where OpenCode-native scheduled tasks can replace the Muse cron jobs that currently launch OpenCode — to save Muse tokens or raise throughput — with proper management and monitoring of those tasks and their outcomes. Study: ~/workspace/research_notes/opencode-scheduled-tasks-20261029/findings.md.

Verdict: PARTIAL — the trigger exists, in OpenChamber, not OpenCode

  • OpenCode core has no scheduler. Zero cron/timer code in packages/core|server|cli; sessions are purely event-driven.
  • OpenChamber has a real scheduled-tasks subsystem (packages/web/server/lib/scheduled-tasks/): cron/daily/weekly/once with timezones, per-project + chats scopes, new-session-per-run (model/agent pinning, permission auto-accept, goal mode with token budget, sessions stamped metadata.kind='scheduled') or prompt-into-existing-session, cross-instance occurrence claiming, loop files (.agents/loops/*.md), REST + UI + CLI + an agent tool (openchamber_schedule).
  • But it is a prompt scheduler, not a job system: state holds only the last run, no token capture, no alerting, in-code defaults of 4 global / 2 per-project concurrent runs, and a 30-minute run cap (vs ~90-minute shift windows).

Migration waves (Claude input tokens, trailing-24h, runtime-DB attribution)

Wave 1 (~3.4B/day of the fleet's ~5.1B/day):

  1. Repo-queue worker shifts — ~2.26B/day, save ~2.1–2.2B. The Muse layer is claim/checkpoint/terminal-verbs via repo-queue.py plus implementation over laptop-ssh — the identical runbook an OpenCode session already executes when dispatcher-launched. Ports near-verbatim as per-queue scheduled tasks. Blockers: repo-queue.py must run laptop-side; sandbox lane-lock files replaced by the atomic DB claim; run cap raised.
  2. Issue creators — ~0.96B/day, save ~0.9B. The runbook already is a self-contained prompt. One API-created scheduled task per repo-lane. Note: this reprices the 3.93M-Claude-tokens/filed-issue figure behind Drew's held creator-throttle decision into pool tokens.
  3. OpenChamber dispatcher + babysitter — ~390M/day, save ~350–370M. The most rule-bound jobs in the fleet, currently spending Claude tokens operating the API of the system that would run the loop natively. Port as existing-session scheduled pokes into persistent sessions (cross-round state lives in session context instead of being re-read at ~1.4M/run).

Wave 2 (~0.25B): fleet-monitor, pr-diff pair, issue-sweep, pool-quota-steward (partial fits).

Keep on Muse: fleet-watchdog (it is the monitor — it must instead gain coverage of the OpenChamber tasks) and orchestration-pass (the consumer/escalation collector).

The honest trade

The throughput ceiling moves from Claude-subscription/cron concurrency to pool quotas + the laptop's 12-session cap. At a known-100% pool the native fleet stalls where Muse lanes currently sail past. This makes the migration dependent on the full-quota utilization program (#2173) keeping pools fed and rotated.

Gating monitoring gaps (fix before migrating anything)

  1. Per-run history — scheduled-task state is last-run-only (laptop JSON). Consume the already-emitted openchamber:scheduled-task-ran SSE events into Supabase.
  2. Token accounting — every OpenCode assistant message already carries cost + tokens (schema/session-message.ts), but nothing exports it; the mirror covers only the Muse runtime. Build an OpenCode-side exporter twin of runtime-db-export (this is External-system observability: unified PM + queue + quota attribution (webhooks, hot list, session IDs, projections) #2072 work).
  3. Failure alerting / dead-man's switch — fleet-watchdog polls GET /api/openchamber/scheduled-tasks/status + queue-claim staleness.
  4. Server residency — the scheduler dies with the OpenChamber server process; confirm the laptop deployment is always-on (not socket-activated or idle-stopped).
  5. Shared-state locality — fleet bookkeeping (backlogs, sweep.log, lane locks) is sandbox-resident; OpenCode sessions are laptop-resident. Move per-job state to Supabase, following the queue pattern.
  6. Identity binding (task id / session id / queue task id in one place), the loop-file approval gate (silently pauses migrated jobs), escalation path for urgent classes.

Acceptance

  • Gaps 1–4 closed and demonstrated on one pilot: ONE queue lane running as an OpenChamber scheduled task for 48h with per-run history, token accounting, and watchdog coverage — reviewed against its Muse-cron baseline (outcome, tokens, time), then a go/no-go for the rest of wave 1.

Acceptance criteria

  • Each Muse cron wrapper that launches OpenCode is inventoried (in the linked study or a comment here) with a disposition: migrated to an OpenChamber scheduled task, kept on Muse cron with a named reason, or retired.
  • Migrated tasks run in waves with monitoring gates between waves: a wave's runs are observed completing (run records + outcome checks) before the next wave migrates.
  • For every migrated task, the Muse-token cost change is measured or explicitly estimated, and any throughput change is noted — the migration's stated purpose is token savings or throughput, so the result must say which it got.

Activity

  1. developing-today commented on Oct 29, 2026

    @developing-today
    Contributor
  2. dezren39 commented on Oct 29, 2026

    @dezren39
    MemberAuthor

    DREW DIRECTION (2026-10-29, main chat) — corrections to this issue's framing:

    1. The 30-minute run cap is ours to turn off. OpenChamber is our code; treat the cap as a config/code change, not a constraint.
    2. Tokens: the data exists. OpenCode stores every session; any run can be looked up. The work is an EXPORTER that pulls from OpenCode's own history database (Drew's preferred approach — pull it out of the giant OpenCode DB), not new capture. This is the External-system observability: unified PM + queue + quota attribution (webhooks, hot list, session IDs, projections) #2072 exporter leg.
    3. Alerting: monitoring these jobs is Muse's job (the watchdog). Jobs can also self-signal — e.g. file a GitHub issue when they hit a problem — and alerting can be built into OpenCode/OpenChamber where it belongs.
    4. ROLLOUT IS ADDITIVE. Add OpenChamber scheduled tasks alongside the existing Muse crons; do NOT migrate or remove the Muse side first. Adjust over time as comparison evidence accumulates. OpenClaw's scheduler (OpenClaw scheduled tasks: use the native automations scheduler for watchdog-class and laptop-local work #2177) is the complementary piece, not a competitor.
    5. Pace: faster than a 48-hour single-lane pilot — stand up MORE THAN ONE queue lane as scheduled tasks from the start.
    6. Review metrics are RELATIVE, never absolute token counts across systems: cost-relative tokens, time taken, and outcome quality. (Same frame applies to the OpenClaw: finish setup, begin passing jobs, and review results against OpenCode #2171 review.)

    The 'gates' in the issue body are hereby build tasks, not gates: per-run history export (from the OpenCode DB), watchdog monitoring of the scheduled tasks, residency confirmation. First build dispatched 2026-10-29: two queue lanes as OpenChamber scheduled tasks, additive, DB-claim only, Muse lanes untouched.

  3. dezren39 commented on Oct 29, 2026

    @dezren39
    MemberAuthor

    OpenChamber scheduled lanes — LIVE (additive, per Drew's direction 2026-10-29)

    Two OpenChamber scheduled tasks now run the repo-health queues ADDITIVELY alongside the existing Muse cron lanes. No Muse cron job was touched; no OpenChamber/OpenCode config file was edited outside the scheduled-tasks CLI.

    Lane OpenChamber task id Project Schedule Model / agent Session per run
    code queue (repo-health-code-pilot) 8ac61616-3815-4e7b-b33d-840a3829154a /home/user/git/code cron */40 * * * *, America/Chicago opencode-route/workhorse (route pool, no raw model pin) / build new session
    opencode queue (repo-health-opencode-pilot) 3fe37e8a-c61b-419c-9e97-769ecd264d5d /home/user (no OpenChamber project exists for ~/git/opencode; the prompt anchors the session at ~/git/opencode and uses absolute worktree paths) cron */40 * * * *, America/Chicago opencode-route/workhorse / build new session

    Each task's prompt = a lane header + the SAME per-repo worker runbook the Muse lanes execute, verbatim. Header adaptations only: no sandbox lane-lock files (the atomic DB claim is the only claim), health gate first, worktrees under ~/git/fleet, ~30-min run discipline with terminal park/complete verbs, first checkpoint carries "origin": "openchamber-scheduled-lane" for audit, sandbox backlog/sweep-log writes skipped (bookkeeping noted in the complete() result instead). Worker ids unchanged (repo-worker-code / repo-worker-opencode).

    Verification (2026-10-29, fleet clock UTC)

    • Pre-checks passed: repo-queue.py health runs on the laptop against Supabase; openchamber schedule CLI surface works; quota re-read before triggering — no workhorse pool walled.
    • Manual trigger: code session ses_e5e795f9576ffe8tuA71C91x8c, opencode session ses_e5e795f8099ffeWHVRz8NTmgh8.
    • First unattended cron occurrence fired on time (08:00:00Z / 08:00:01Z): code session ses_e5e7953b638ffenCsU6ORljLIC, opencode session ses_e5e7953b16fffevBgwWxHh8ur1; next occurrence 08:40Z. Both tasks remain ENABLED.
    • Claims audit (via the origin marker in agent.tasks.progress): OpenChamber lanes claimed tasks 2827, 2828, 3227, 2833 (code) and 2712, 2705, 2900 (opencode) — all distinct ids; Muse lanes claimed different tasks in the same window (2661, 2681, plus the pre-existing 2821/2825/2990/2826/3223). No task was double-claimed. The atomic claim is doing its job.
    • Work product already real: task 2827 completed a full triage of mcpx: administration by intent — finish the operation surface, then route a sentence to a declared operation #1931 (verdict + comment + follow-up task 3226 enqueued); 2828 also completed; 3227 and 2705 parked properly with resume briefs; 2712/2833/2900 in flight.

    Notes / watch items:

  4. dezren39 commented on Oct 29, 2026

    @dezren39
    MemberAuthor

    Second OpenChamber scheduled lane for the code queue is live (Drew's direction, 2026-10-29) — mirroring the first code lane exactly: task 2717091f-6f5a-4a46-beae-5e7db58d376b, project /home/user/git/code, cron */40 * * * * America/Chicago, model opencode-route/workhorse, agent build, new session per run, same lane prompt (header + code worker runbook verbatim), atomic DB claim only, origin marker openchamber-scheduled-lane. The CLI accepted the identical task name with no refusal.

    Verification: manual trigger → session ses_e5e793aea23ffeFylbU3awqnEu (lastStatus success). Its first claim landed 51s after dispatch: task 2869 (triage of code#1968), claimed 2026-10-29T08:27:56Z with the origin marker in its first checkpoint, attempts=1. No first-lane cron occurrence existed in that window, and the concurrent Muse-lane claims (2864, 2866, 2871) carry no origin marker — zero overlap, the atomic claim held across two OpenChamber code lanes + the Muse lanes. Left ENABLED on the regular */40 cadence. Full record: rollout addendum in ~/workspace/research_notes/opencode-scheduled-tasks-20261029/rollout.md. No existing lane or Muse cron was touched.

  5. added
    kind/infrastructureInfrastructure / host-portability work
    source/humanFiled directly by a human
    priority/highImportant and time-sensitive
    effort/lLarge: multi-day or open-ended; expect staged check-ins
    meta/enrichedIntake review done: acceptance criteria, links, and normalized labels added
    on Oct 29, 2026
  6. dezren39 commented on Oct 29, 2026

    @dezren39
    MemberAuthor

    Intake review

  7. dezren39 commented on Oct 31, 2026

    @dezren39
    MemberAuthor

    Lane expansion — Drew, 2026-10-31 ~07:56 CDT: +4 code, +1 openchamber, +1 nix OpenChamber scheduled lanes.

    Created via the laptop openchamber schedule CLI, all ENABLED, all cron */40 * * * * America/Chicago, model opencode-route/workhorse, agent build, new session per run, origin marker openchamber-scheduled-lane:

    • code: c29174f1-48e0-4b13-b4ff-9b73ab67e9ab, 262eb403-3347-4a7c-ba6b-e7bc263ca853, 335a5d1f-bfc1-4831-99ea-850c8939a727, 7b47536b-c162-444c-be8a-3a9538cc4cb5 (project ~/git/code; staged lane prompt reused verbatim)
    • openchamber: 46984602-51a3-438c-b3a5-f2857811adb1 (project /home/user — no registered project for this repo; prompt anchors at the primary clone ~/git/openchamber-fork)
    • nix: 8d133595-c60c-4dcb-8e82-59b7d0ac9b5a (project /home/user; prompt anchors at ~/code/dezren39-nix)

    Openchamber/nix prompts = the 2026-10-29 rollout header adaptations + each queue's own worker runbook verbatim; staged in hatch-personal workspace/research_notes/opencode-scheduled-tasks-20261029/prompts/ (commit d95bfaf7b) and copied to the laptop with sha256 verification.

    Verification: manual triggers of one new code lane (session ses_e5e6df34549ffeX6juJcU0fKTk), the openchamber lane (ses_e5e6df34345ffePROYiiixfhjY), and the nix lane (ses_e5e6df34175ffewvlbwb0qw4Lm) all ended lastStatus=success. The nix queue currently holds 0 queued tasks, so that lane idles until work lands. Pre-flight quota read (aggregator, 12:45Z): workhorse route doors have weekly headroom (opencode-go.personal 39%, opencode-go.lab 75%, command-code 64%); the Meta reserve door reads 100% weekly.

    Store inventory now: 9 enabled queue lanes (code ×6, opencode ×1, openchamber ×1, nix ×1) plus the #3026 claimant task (disabled).

    Two discrepancies vs the 2026-10-29 record — flagged, not silently fixed:

    1. The recorded second code lane 2717091f is no longer in the store. The second code lane present is 9aca43b4-5439-4939-9f71-323f5d32521b: prompt byte-identical to the lane prompt, but cron is 20,40 * * * * and the model is unset (falls back to the project default instead of the pinned workhorse route).
    2. The laptop's hatch-personal clone has diverged from origin (fast-forward pull refused), so the prompt files reached the laptop by checksum-verified transfer rather than git pull.
  8. 8 remaining items

  9. added
    queue/queuedHas a queued task in the Supabase agent queue
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/claimedQueue worker lifecycle state
    work/assignedQueue worker lifecycle state
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 3, 2026
  10. dezren39 commented on Nov 4, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

  11. dezren39 commented on Nov 4, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up

  12. dezren39 commented on Nov 4, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · triaged / recommendation recorded

    Fresh check — gates and pilot

    • Gap 1 — per-run history: PARTIAL, not centrally closed. OpenChamber emits openchamber:scheduled-task-ran; the live scheduled-task records retain only latest-run fields. The OpenCode session/message APIs preserve individual run evidence, but this pass found no consumer persisting scheduled-run events into Supabase. At 2026-11-04 07:19Z the code project had 12 enabled queue lanes (11 on */40, one on 20,40); 7 latest sessions were still active, so scheduler lastStatus=success alone is not a terminal outcome record.
    • Gap 2 — token accounting: PARTIAL / exporter now live. Issue External-system observability: unified PM + queue + quota attribution (webhooks, hot list, session IDs, projections) #2072 records PR feat(pm-stack): OpenCode usage exporter — pull from live history DB (#2072 slice 1) #4680 merged (exporter reads live OpenCode session_v2 / session_message history). On this laptop opencode-usage-export.timer is active and state.json reports last tick 2026-11-04T07:15:13Z; latest.json and history.jsonl are present. This verifies per-session export, but not a normalized OpenChamber-vs-Muse comparison for this pilot.
    • Gap 3 — watchdog coverage: NOT VERIFIED. The prior OpenCode scheduled tasks: migrate the Muse→OpenCode cron wrappers into OpenChamber scheduled tasks (waves, monitoring gates) #2174 triage (task 7306) also found no evidence that watchdog checks scheduled-task status plus stale queue claims. This pass did not find a fresh live proof of that coverage; do not treat this gate as closed.
    • Gap 4 — residency: PARTIAL. Live OpenChamber/OpenCode user services and socket are active; the server process started 2026-11-03T20:56:27Z, and user lingering is enabled. The scheduler itself is process-local, however, so laptop sleep, shutdown, or power loss still interrupts it.
    • 48h comparison: no valid one-lane cohort or relative outcome/token/time comparison was established: 12 code lanes are enabled and the latest sample still had active sessions. Drew’s 2026-10-29 direction on this issue supersedes a single-lane cutover gate: rollout is additive and starts with multiple lanes; Muse jobs remain untouched.

    Recommendation to Drew: NO-GO for any Muse cutover/retirement; continue only the additive scheduled lanes while closing the missing run-history/watchdog evidence and collecting relative outcome, token, and elapsed-time data. This is a recommendation, not a cutover decision; no schedule was changed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

effort/lLarge: multi-day or open-ended; expect staged check-inskind/infrastructureInfrastructure / host-portability workmeta/enrichedIntake review done: acceptance criteria, links, and normalized labels addedpriority/highImportant and time-sensitivesource/humanFiled directly by a humantask/triageQueue packet kind: verify, reproduce, and route this item

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions