# Scheduling & Resilience — Tax Snapshot Harness

**Harness:** `/workspace/vibe-dashboard/run_tax_snapshot.sh`
**Scheduler code:** `/workspace/vibe-dashboard/schedule/`
**Snapshot output:** `/workspace/vibe-dashboard/data/snapshots/` (per-lot read-only tax table + `INDEX.md`)
**Scheduler log:** `/workspace/vibe-dashboard/data/schedule.log`

The snapshot harness is read-only and local-only. It never places orders and
never sends data off-box. Scheduling simply invokes the same script on a timer.

---

## Three ways to run this on boot / on a schedule

### (a) Detached daemon (recommended, simplest) — `start_schedule.sh`

A self-contained, crash-tolerant Python loop:

- Runs the harness **immediately on start**, then every **N seconds**
  (default `86400` = once daily, matching the snapshot cadence).
- Wraps every run in `try/except` — a failed harness is logged and the loop
  **continues**; the daemon never dies on harness errors.
- Logs each tick to `/workspace/vibe-dashboard/data/schedule.log`.
- Fully detached via `setsid` + `nohup` so it survives the launching shell/ssh.
- Writes its pid to `/workspace/vibe-dashboard/schedule/scheduler.pid`.

**Start it (default daily):**
```bash
/workspace/vibe-dashboard/schedule/start_schedule.sh
```

**Start with a custom interval in seconds** (e.g. every 6 hours = 21600s):
```bash
/workspace/vibe-dashboard/schedule/start_schedule.sh 21600
```

**Check status / last log lines:**
```bash
/workspace/vibe-dashboard/schedule/status_schedule.sh
```

**Stop it:**
```bash
/workspace/vibe-dashboard/schedule/stop_schedule.sh
```

To make it run again after a reboot, add the `start_schedule.sh` line to a
shell init/autostart (this host has no systemd/cron — see (c)).

---

### (b) Hermes cron bond (exact params)

This workspace hosts the Hermes agent, which has its own built-in cron
scheduler. Create a cron bond/job with **exactly** these parameters:

- **schedule:** `0 3 * * *`   (daily at 3:00 AM)
- **command:** `/workspace/vibe-dashboard/run_tax_snapshot.sh`

Example (copy-paste into the Hermes cron tool, e.g. `cronjob create`):

```text
schedule: '0 3 * * *'
command: /workspace/vibe-dashboard/run_tax_snapshot.sh
```

The harness is idempotent and safe to run on any schedule — each run writes a
fresh timestamped snapshot, rebuilds `INDEX.md`, and prunes snapshots older
than 90 days.

---

### (c) No systemd / cron on this host — a daemon or the Hermes scheduler is required

This host is **NixOS workspace** and has **no systemd and no cron daemon**.
There is no `crontab`, no `/etc/systemd/system` unit directory, and no
`systemctl` — so traditional `cron` entries and `systemd` timers **will not
work here**. The host also auto-terminates idle processes, so a plain
foreground `nohup ... &` from an interactive shell is not durable across
sessions.

Therefore one of these is required to keep snapshots running unattended:

1. **The detached daemon — `start_schedule.sh`** (option a). It survives the
   SSH/shell session via `setsid`, and the Hermes workspace keeps it alive. This
   is the most robust local option.
2. **The Hermes cron scheduler** (option b). Hermes has its own scheduler that
   persists across restarts and is the intended mechanism on this host.
3. As a lightweight fallback, a manual loop (not recommended for durability):
   ```bash
   nohup sh -c 'while true; do /workspace/vibe-dashboard/run_tax_snapshot.sh; sleep 86400; done' \
     >>/workspace/vibe-dashboard/data/schedule.log 2>&1 &
   ```
   but prefer (a) or (b) since the daemon adds crash tolerance and pid/status
   management.

---

## Layout

| File | Purpose |
|------|---------|
| `schedule/daemon.py`          | Resilient loop: run harness on start + every N s, log, never die |
| `schedule/start_schedule.sh`  | Detached launcher (setsid+nohup), writes `scheduler.pid` |
| `schedule/stop_schedule.sh`   | Stops the daemon, clears the pidfile |
| `schedule/status_schedule.sh` | Reports running/stopped + last log lines |
| `data/schedule.log`           | Append-only tick log |
| `data/snapshots/`             | Timestamped snapshots + `INDEX.md` |
