--- id: WARDEN-WP-0021 type: workplan title: "Enable the scheduled worker tick — conservative inbox triage, unattended" domain: infotech repo: ops-warden status: active owner: claude topic_slug: custodian planning_priority: high planning_order: 21 created: "2026-06-30" updated: "2026-06-30" state_hub_workstream_id: "8c487014-b630-4016-a4f0-31b971a473d2" --- # WARDEN-WP-0021 — Enable the scheduled worker tick **Goal:** turn the WP-0020 conservative worker from *built-but-disabled* into a reliable, unattended schedule — so ops-warden's State Hub inbox is auto-triaged into a digest of **drafted replies** the operator reviews and approves, without anyone starting a session. This is the payoff of WP-0020: it ends the cross-session relay toil. **Posture (unchanged):** schedule the **conservative** tier only — triage + draft, never auto-send (Option A / build-stage decision `813899f9`). The four guardrails hold. Easy kill switch is a requirement, not an afterthought (recoverability). **What "enabled" means here:** (1) the tick runs on a schedule and survives the failure modes (hub/llm-connect down → graceful degrade), (2) the operator actually *sees* new drafts, (3) the operator can *act* on a draft with one command, (4) it's trivial to stop. **Out of scope:** the full-auto (auto-send) path; flipping `policy.enabled`; moving the worker off the workstation. **Depends on / relates to:** WP-0020 (the worker + `scripts/worker-tick.sh`); the State Hub migration to railiance01 (`cust-wp-0011`/`0038`) may change `WARDEN_HUB_URL` later — the tick already honors that env var. --- ## Decisions to settle (first) - **Scheduler:** `systemd --user` timer (recommended — clean logs via `journalctl`, `systemctl --user status`, built-in scheduling) vs. plain cron (simplest) vs. activity-core (ecosystem-native durable trigger; heavier for build stage). Recommend the systemd user timer; cron documented as the one-liner fallback. - **Cadence:** every 15 min (default) — adjustable. - **llm-connect reachability:** per-tick short-lived port-forward (current behaviour) with rule-brain fallback, vs. a persistent forward. Recommend keeping the per-tick forward + fallback for build stage (no standing process). --- ## Tasks ### T1 — Scheduler install + enablement + kill switch ```task id: WARDEN-WP-0021-T01 status: done priority: high state_hub_task_id: "10451fe6-7fab-4ae0-8494-e6cfdfbcf8cf" ``` - [ ] `systemd --user` timer + service units (`ops-warden-worker.{service,timer}`) that run `scripts/worker-tick.sh` on the chosen cadence, with `WARDEN_HUB_URL` / `WORKER_BRAIN` from an env file. Install script + documented cron fallback one-liner. - [ ] Concurrency is already guarded by the tick's `flock`; verify under the timer. - [ ] **Kill switch:** `systemctl --user disable --now ops-warden-worker.timer` (and the env-file `WORKER_ENABLED=0` short-circuit) — one command to stop, documented. ### T2 — Scheduled-run robustness (graceful degradation) ```task id: WARDEN-WP-0021-T02 status: done priority: high state_hub_task_id: "1f35f816-1af5-46ff-b48c-1715f3ae5784" ``` - [ ] Harden `worker-tick.sh` for unattended runs: bounded timeouts, hub-unreachable → clean skip + log (no crash loop), llm-connect-unreachable → rule-brain fallback (already present; verify), non-zero exit only on real faults. - [ ] End-to-end verify a real timer-fired tick: new message → digest + progress note; no new message → no-op; hub down → graceful skip. ### T3 — Operator visibility (see new drafts) ```task id: WARDEN-WP-0021-T03 status: todo priority: medium state_hub_task_id: "3c7f6423-8db0-4bc6-b67d-078d9d929c6d" ``` - [ ] Surface new drafts beyond the file: desktop `notify-send` on new digest (when a display is present) and/or keep the hub progress note as the durable signal. - [ ] `warden worker status` — last run time, pending-draft count, digest path, timer state. ### T4 — Review→send loop (`warden worker approve`) ```task id: WARDEN-WP-0021-T04 status: todo priority: high state_hub_task_id: "dabc9fc0-abb1-4e9d-b87e-5f0c5950693c" ``` - [ ] Persist structured drafts during the tick (`state_dir/worker-drafts.json`: message_id → to_agent, subject, drafted body, thread_id — no secret material). - [ ] `warden worker approve [--edit]` — send the reviewed draft as the caller's reply + mark read; `warden worker drafts` to list pending. This is what makes the scheduled digest *actionable* in one command instead of hand-composing. ### T5 — Runbook + SCOPE ```task id: WARDEN-WP-0021-T05 status: todo priority: medium state_hub_task_id: "9915da96-1b33-4d0f-b752-408ea8d43333" ``` - [ ] `wiki/playbooks/scheduled-worker.md` — enable/disable, cadence, the approve workflow, failure modes, and the build-stage posture (conservative only). SCOPE note. --- ## Acceptance - A `systemd --user` timer (or cron) runs the conservative tick unattended; one command disables it. - A timer-fired tick triages new messages into a digest + progress note and degrades gracefully when the hub or llm-connect is down. - The operator is notified of new drafts and can send a reviewed draft with `warden worker approve `. - Still conservative: nothing is auto-sent; no secret value is read, sent, or logged. ## See also - `WARDEN-WP-0020` (the worker + `scripts/worker-tick.sh`), build-stage decision `813899f9` - `cust-wp-0011`/`cust-wp-0038` (State Hub → railiance01; future `WARDEN_HUB_URL`)