diff --git a/docs/recurring-automations-playbook.md b/docs/recurring-automations-playbook.md new file mode 100644 index 0000000..bc90e28 --- /dev/null +++ b/docs/recurring-automations-playbook.md @@ -0,0 +1,157 @@ +# Recurring automations playbook — activity-core as the organizer + +**Audience:** operators and coding agents adding or fixing recurring work +**Principle:** activity-core answers **when / what / where** only. It does **not** execute. + +This is the org standard. Prefer this path over workstation cron, ad-hoc shell +timers, or “I’ll remember to run it.” + +--- + +## The three layers (do not collapse them) + +```text +┌─────────────────────────────────────────────────────────────┐ +│ 1. activity-core — WHEN / WHAT / WHERE │ +│ Temporal schedule · context resolvers · rules │ +│ → emits activity_task_spawn (state-hub sink by default) │ +└────────────────────────────┬────────────────────────────────┘ + │ +┌────────────────────────────▼────────────────────────────────┐ +│ 2. rein-aharness (or domain executor) — DOES THE WORK │ +│ llm-connect · repo checkout · commit · completion event │ +│ Host timers on railiance are *interim* until poll/claim │ +│ of spawns is fully wired (state-hub sink is not claimable)│ +└────────────────────────────┬────────────────────────────────┘ + │ +┌────────────────────────────▼────────────────────────────────┐ +│ 3. domain repo — INSTANCE STATE │ +│ activity-definitions/ · playbooks · briefs/ · catalogs │ +│ completion event types (fi_daily_brief, binky_daily_brief)│ +└─────────────────────────────────────────────────────────────┘ +``` + +| Layer | Owns | Must not own | +| ----- | ---- | ------------ | +| **activity-core** | Triggers, due checks, task specs, audit of spawns | Brief writing, git commits, LLM prompts for domain content | +| **rein-aharness** | Execution session, credentials at runtime, hub completion | Inventing schedules, domain policy | +| **domain repo** | Definition markdown, playbook, artifacts | Scattered crons that bypass activity-core | + +**Anti-pattern:** workstation `crontab` that “does the daily brief” while +activity-core also schedules it — dual cadence and forgotten design. + +**Correct interim (Binky / FI):** activity-core schedule is authority for *when +due*; railiance user-systemd timer invokes rein-aharness a few minutes later as +executor. Document both; never pretend the host timer *is* the scheduler. + +--- + +## How to add a recurring automation (checklist) + +### A. Domain definition (source of truth) + +1. Create `{domain-repo}/activity-definitions/{slug}.md` + - `trigger.type: cron` + timezone + `misfire_policy` + - `context_sources` with a **due resolver** when work is once-per-day + - `rule` with `target_repo`, labels, description pointing at the playbook + - Start with `enabled: false` until dry-run +2. Implement completion event shape in domain playbook + (e.g. `fi_daily_brief` with `detail.repo` + `detail.date`) +3. If needed, add resolver in activity-core (`context_resolvers/`) + tests + +### B. Register with activity-core + +| Environment | How | +| ----------- | --- | +| **Dev** | `ACTIVITY_DEFINITION_DIRS=/path/to/domain-repo` + `make sync-activity-definitions` | +| **Railiance** | Project definition into ConfigMap `actcore-external-activity-definitions` (and keep `k8s/railiance/20-runtime.yaml` in git) | + +Then: + +```bash +# after ConfigMap / files update +kubectl -n activity-core exec deploy/actcore-api -- python3 -c \ + 'import urllib.request as u; print(u.urlopen(u.Request( + "http://localhost:8010/admin/sync?definitions=true&schedules=true", + method="POST")).read().decode())' +``` + +Verify: + +```bash +# definition enabled + cron +# Temporal schedule: activity-schedule- not paused +kubectl -n activity-core exec deploy/actcore-worker -- python3 -c '...' # describe schedule +``` + +Ops UI: `activity.coulomb.social` → automations status / trigger (SSO). + +### C. Executor (rein-aharness) + +1. Prefer a **named rein-aharness command** for the domain work + (e.g. `brief-daily`, `fi-research-brief`) using **llm-connect**, not Claude CLI on railiance01 +2. Install **railiance user-systemd timer** in the domain repo + (`scripts/railiance-rhythm/`) that calls rein-aharness +3. Timer should sit **just after** the activity-core cron (due check already + enforced by completion events on re-run) +4. On success, post the **domain completion event** so the resolver sets `due=false` + +### D. Prove the loop + +1. `POST /activity-definitions//trigger` (ops UI or API) +2. Confirm `activity_task_spawn` progress event (when sink=state-hub) +3. Run executor once (timer or manual rein-aharness) +4. Confirm domain completion event → resolver `due=false` for that day +5. Set `enabled: true` if still false; commit domain + activity-core CM projection + +--- + +## Where definitions live + +| Kind | Location | +| ---- | -------- | +| Org-wide (SBOM, consistency, inventory) | `activity-core/activity-definitions/` and/or ConfigMap | +| Domain-specific (Binky, FI, …) | `{domain}/activity-definitions/` + ConfigMap projection | + +ADR-002: domain repos own domain automations; activity-core discovers them via +`ACTIVITY_DEFINITION_DIRS` or the external ConfigMap mount. + +--- + +## Sinks (ACTIVITY-WP-0022) + +| `ISSUE_SINK_TYPE` | Use | +| ---------------- | --- | +| **`state-hub`** (default) | Internal fleet work → `activity_task_spawn` progress | +| **`null`** | Dry-run | +| **`rest`** | Explicit external issue-core / Forgejo only | + +Do not flip production to `rest` globally. + +--- + +## Reference implementations + +| Domain | Definition | Executor | +| ------ | ---------- | -------- | +| **Binky** | `binky-daily-rhythm` in activity-core / binky | `rein-aharness brief-daily` + `binky-control/scripts/railiance-rhythm/` | +| **Freedom Intelligence** | `fi-daily-research-brief` | `rein-aharness fi-research-brief` + `freedom-intelligence/scripts/railiance-rhythm/` | + +--- + +## Operator daily habit + +1. Ops console: did automations run? (`/ops/automations/status`) +2. State Hub: `activity_task_spawn` + domain completion events +3. Domain repo: brief/artifact files + git history +4. If something is missing, **fix activity-core first** (schedule paused? + definition disabled? resolver due stuck? sink errors?) — do not invent a new cron + +--- + +## Related + +- `INTENT.md` — when/what/where boundary +- `docs/task-emission-consumer-contract.md` — spawn payload + consumer duties +- `docs/runbook.md` — sync, trigger, ops UI +- `docs/adr/adr-002-definition-format.md` — definition files