activity-core/docs/recurring-automations-playbook.md
tegwick 747d63a993
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Document recurring automations playbook for activity-core as organizer.
Codify when/what/where vs rein-aharness execution vs domain instance state,
and the checklist to register cron definitions without workstation cron
bypasses.
2026-08-03 17:53:06 +02:00

157 lines
7.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 “Ill 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-<definition-uuid> 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/<id>/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