Nightly Caretaker + file-driven scheduler#212
Draft
pseay-imbue wants to merge 28 commits into
Draft
Conversation
Phase 3 of the Caretaker/scheduler feature: - manage-scheduled-tasks skill: query/edit runtime/scheduled_tasks.toml via the scheduler CLI, with the [[task]] schema, cron syntax, and catch-up semantics. - check-app-errors skill: survey supervisorctl status and scan /var/log/supervisor logs for errors; reusable by chat agents and the Caretaker's nightly scan. - CLAUDE.md: remind agents to check /var/log/supervisor after building or editing a service, pointing at check-app-errors. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add the file-driven scheduler as a supervised service and seed its
default schedule on first boot.
- supervisord.conf: add [program:scheduler] (uv run scheduler run),
mirroring [program:app-watcher]'s knobs, logging to
/var/log/supervisor/scheduler-{stdout,stderr}.log.
- pyproject.toml: register the scheduler lib (libs/scheduler/) as a
workspace member, dependency, and uv source.
- bootstrap/manager.py: on first boot, seed runtime/scheduled_tasks.toml
(iff absent) with the single nightly Caretaker task per the frozen
contract, and create runtime/scheduler/ + runtime/caretaker/.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Introduce libs/scheduler: a thin, supervised task scheduler that runs recurring shell commands on a cron schedule with offline catch-up (a task missed during downtime runs once on the next boot; coalesced). - data_types: ScheduledTask + TaskRunState (frozen models) - engine: pure due/overdue/coalesce logic, timezone-aware, unit-testable - schedule_file/state: tomlkit round-trip for runtime/scheduled_tasks.toml and atomic runtime/scheduler/state.toml - runner: non-blocking daemon loop (launch/reap, running guard, boot tick) - cli: `scheduler run|list|add|remove|show` console script 27 unit tests pass; pyright + ruff clean. Workspace registration of the lib lives in phase 2 (root pyproject.toml) to keep file ownership disjoint. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ly-agent-caretaker
…ly-agent-caretaker
Regenerated after merging phase 1 (libs/scheduler) and phase 2 (workspace registration). Adds the scheduler workspace package and its croniter dep. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Caretaker is a singleton, once-a-night agent that scans the workspace's service logs for problems, reviews its previous run, and proposes (or, with the user's recorded permission, applies) fixes -- always explained in plain, non-technical, user-experience terms. - .mngr/settings.toml: [create_templates.caretaker] (claude agent oriented by an appended system prompt to run the caretaker skill) - .agents/skills/caretaker/SKILL.md: the nightly routine (clear, incremental logging, consent-gated scan/fix, first-run welcome, prune to 30 logs) - scripts/run_caretaker.sh: idempotent wake -- create if absent, message if idle, wrap-up message if mid-run (singleton, found by the caretaker=true label; created with auto_created=true so minds shows it as a new tab) - scripts/preferences.py: get/set/show for runtime/caretaker/preferences.toml (auto_scan, auto_fix, fix_scope, introduced) - references/welcome-message.md: the canonical first-run welcome text Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Satisfies test_meta_ratchets' requirement that every project ship a test_*_ratchets.py. Counts: one while-true and one time.sleep (the daemon tick loop); zero builtin-exception raises, broad excepts, or relative imports. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The nightly Caretaker (any agent labelled auto_created/caretaker) now opens as its own tab in the workspace chat UI and blinks in the accent color until the user opens it -- the real "new tab" surface (minds sidebar tabs are per-workspace is_primary agents, so a same-host sub-agent like the Caretaker can never be a sidebar tab; it belongs here, inside the workspace). DockviewWorkspace.ts: surfaceAutoCreatedAgents() auto-opens an inactive tab for each newly-seen auto_created agent (once ever, tracked in localStorage so a closed tab isn't reopened); createCustomTab() adds a .dv-tab-new blink class until the tab's first activation, then marks it seen (localStorage). style.css: the tab-new-pulse accent animation. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Caretaker was narrating its internal routine into the chat (status lines, "first run", etc.), which read as unclear and oddly formatted. Tighten the skill so the chat is strictly user-facing: - Add a "How you talk to the user" section: chat = warm, plain, non-technical; do work silently via tools; keep reasoning in thinking and working notes in the run-log file, never the chat; end with a single clean message. - First run delivers a pre-prepared, verbatim "Hi, I'm your Caretaker..." welcome (references/welcome-message.md), mirroring the main chat's /welcome. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Caretaker self-invoking "/clear" never cleared anything (it only emits text). Make mngr the one that clears: on re-wake, send "/clear" to the agent's stdin, settle briefly, then send the caretaking run trigger. First creation skips the clear (nothing to clear) so the welcome is the first thing the user sees. Busy Caretakers still get a graceful wrap-up message, then the same mngr-driven clear + run sequence on restart. Remove the agent-self-clear step from SKILL.md; the routine now starts at opening the run log. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The unopened auto-created (Caretaker) tab now flashes in this workspace's own accent color instead of the fixed green, and uses a sharp flash-then-fade animation so it is much harder to miss. The server injects the primary agent's hex `color` label as a `system-interface-accent-color` meta tag (mirroring the existing agent-id tag), the frontend reads it at bootstrap and publishes it as a `--workspace-accent` CSS variable, and the `.dv-tab-new` animation snaps bright in that accent then eases out to the normal idle tab look each cycle. Falls back to `--color-accent` when no per-workspace accent is configured. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- system_interface: replace "surface once ever" with re-surfacing on each fresh run. A new run is the rising edge of the Caretaker's activity; if the user does not already have the tab open, re-open it (inactive) and reset the flash so it draws attention again. An already-open tab is left untouched. - run_caretaker.sh: the first-creation trigger is now a welcome-only nudge, so day 1 shows just the welcome; the recurring "It's time for your caretaking run" message only appears from day 2 onward (after mngr's /clear). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The tab keyed surfacing on activity_state (THINKING/TOOL_RUNNING), but the Caretaker's lifecycle is RUNNING while activity is often idle/null, so the tab never popped up. Switch to a reliable run-key: the caretaker_run label that mngr bumps on every wake. The UI surfaces (and re-flashes) the tab when the key changes -- including first sight -- so it pops up reliably and re-appears each run, while leaving an already-open tab alone. run_caretaker.sh sets/bumps caretaker_run on create and re-wake. Welcome heading is now "Hi, I'm a Caretaker for your Mind." Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…sage Mirror the initial chat's creation (mngr create ... --message /welcome): the Caretaker is created with --message /caretaker-welcome, a new verbatim skill that emits a fixed welcome and runs no routine. So the first thing the user sees is the canned welcome, delivered the same way as the main chat's -- not an agent improvising while running its routine. The caretaker skill is now routine-only (scan/fix on later nights) and records consent when the user answers; the old first-run/introduced/welcome path and welcome-message.md are removed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Flash the whole .dv-tab clickable region (via :has) instead of just the inner label, so the new-tab indicator covers the entire tab. - Format the /caretaker-welcome message with bold headings for the greeting and the two setup questions. - Hide the /caretaker-welcome trigger user-message in the chat, the same way the main chat hides its /welcome trigger (isHiddenUserMessage). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
mngr observe --discovery-only events carry no labels (the discovery certified_data omits them), so the observe-snapshot handlers were overwriting the label-bearing entries from the initial list_agents discovery with label-less ones. The web UI keys tab auto-surfacing on the caretaker/auto_created labels and hides the is_primary services agent by label, so a label-less snapshot meant the Caretaker tab never opened and is_primary filtering only worked by luck. Read each agent's labels from its local data.json (which mngr writes on create) when the discovery event lacks them. Falls back to empty labels for agents with no local state dir (e.g. tracked on a remote host). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Welcome shows only on the very first run, gated on a persistent 'introduced' preference, so it never reappears on later nights. - Each later run retires the old Caretaker and creates a fresh agent (clean chat). An in-session /clear starts a new Claude session id the system interface isn't watching, so it never actually cleared the UI; a brand-new agent is the only reliable clear. - The in-workspace UI now prunes the retired Caretaker's dead tab when the agent disappears, scoped to auto-created agents so a user's own chat tab is never at risk. - The welcome now asks whether to take a first look right now; if the user says yes, the Caretaker runs its routine on the first day instead of waiting for the nightly schedule. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Stop overriding the tab label's text color during the flash. The faded keyframe forced color: var(--color-text-secondary), which did not match the tab's actual resting color in every state. Now only the wrapper background animates (accent -> transparent) behind the label, so between flashes the label is exactly the gray dockview would give it normally. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Caretaker skill now makes clear it is chatting directly in its own tab: address the user as 'you', never prefix/label messages (no '@user:'), never narrate channel choice or bookkeeping, always plain language. Its closing summary is written straight as its response rather than routed through send-user-message (whose inline fallback added the '@user:' prefix and a 'no telegram, so...' narration). - The run now starts with a short hello before any work, worded from the user's saved preferences, so the user always sees the Caretaker greet them and say what it is about to do. - Later runs are triggered by a hidden '/caretaker' slash command (invokes the routine) instead of a visible nudge; hidden in the chat UI like '/caretaker-welcome'. - caretaker-welcome skill: output only the message, with no 'I was asked to output the following' preamble. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Caretaker is now a single persistent agent invoked via /caretaker on every run. The skill detects the first-ever run (introduced=false AND no prior runtime/caretaker/*.md logs); on first run it sends the welcome verbatim and sets introduced=true, otherwise it runs the nightly routine. Removes the separate caretaker-welcome skill. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…eduler Researched whether a well-established library (APScheduler, schedule, croniter, Celery beat, systemd timers, anacron) could replace the custom libs/scheduler. Conclusion: keep ours -- no library cleanly covers the combination of cron schedules, file-driven TOML config, a supervisord-hosted Python service, and offline catch-up of missed runs from on-disk state. APScheduler 3.x is closest but needs non-default config and adds a TOML<->jobstore reconciliation layer (net complexity increase). Documented the analysis and a contingency migration plan in libs/scheduler/SCHEDULER_LIBRARY_EVALUATION.md. No behavior change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ally clears The nightly Caretaker is now a single persistent agent that mngr clears and re-triggers each run, instead of being destroyed and recreated. run_caretaker.sh creates the Caretaker once (first message /caretaker) and on later runs bumps its run key, sends /clear, then sends /caretaker again -- the idempotent skill self-detects first vs later run. Removed the destroy/recreate path, the introduced gating, and the welcome-vs-run branching. To make /clear actually clear the rendered chat, the system interface now renders only the sessions at or after the most recent /clear boundary in an agent's session history (previously it merged every session, so a cleared chat still showed the old transcript with the new one appended). compact/resume/startup sources remain continuations and never reset the view. Dropped the retired /caretaker-welcome trigger from the hidden-message list in the frontend (only /welcome and /caretaker remain hidden). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
After mngr sends /clear to re-trigger the Caretaker, the cleared session opens with the raw '/clear' command and Claude Code's '<local-command-caveat>' message. Neither is a real user turn, so hide both in isHiddenUserMessage -- the cleared chat now starts at the Caretaker's own first message. Extends the message-classification test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a self-managing scheduled-task system and a nightly Caretaker agent that quietly keeps a workspace healthy. Pairs with the minds-side PR (imbue-ai/mngr
preston/nightly-agent: blinking tab + timezone capture).What's in here
libs/scheduler— a thin, supervised file-driven scheduler with offline catch-up (a task missed while the machine was off runs once on next boot; multiple misses coalesce). One readable, agent-editableruntime/scheduled_tasks.toml; per-task state atruntime/scheduler/state.toml.scheduler run|list|add|remove|show.[program:scheduler]under supervisord; bootstrap seeds the default schedule + dirs on first boot (the nightly Caretaker task at 3 AM).[create_templates.caretaker]+ thecaretakerskill. A singleton, once-a-night agent created lazily on first run. Consent-gated (runtime/caretaker/preferences.toml); first run is a cheap survey + a pre-prepared, user-friendly welcome (clean, user-facing-only messaging); later runs scan logs (viacheck-app-errors), read the prior log, and propose/apply fixes.run_caretaker.shis the idempotent wake (create / message-idle / wrap-up-busy).manage-scheduled-tasks,check-app-errors; CLAUDE.md nudge to check/var/log/supervisor/after editing a service.Testing
Draft — open for review alongside the paired minds PR.
🤖 Generated with Claude Code