Status
Accepted (2026-08-03).
Context
The fleet has two healthy patterns that were forced into one vague “task” idea:
- Development / coordination work — structured workplans and tasks as repo files, indexed by State Hub on coulombcore (
fix-consistency, UUIDv7 write-back). This matches ADR-001 andwork-record-types_v0.1.mdand is how humans and coding agents ship product.
- Operations / scheduled automation — activity-core Temporal schedules (when / what / where), rein-aharness execution (how), completion events (idempotence). Instances of “today’s FI brief” or “this prune fire” are ephemeral runs, not multi-day workplan bodies.
Practice collapsed (2) into broken paths:
| Path | Failure |
|---|---|
Emit activity_task_spawn progress only | Append-only; not claimable; schedule can “succeed” while nothing runs |
ISSUE_SINK_TYPE=rest → issue-core → Forgejo | Spam + 503s; issue-core INTENT forbids origin of fleet work |
| Host systemd timers dual-clocked with Temporal | Shadow scheduler; weak self-healing |
issue-core’s correct role is a connector facade over external trackers (Forgejo, GitHub, Jira, …) so agents need not know each backend. It is not the origin of work records and not the default internal ops queue. Gitea is out of scope for this fleet; the self-hosted forge is Forgejo.
Canon already allows a DB-only exception for “runtime operations data (logs, metrics, run histories, token events)” (work-record-types_v0.1.md). This ADR names that exception for ops runs.
Decision
1. Two planes for “work,” one vocabulary for people
| Plane | Artefact | Home | Lifecycle | ||
|---|---|---|---|---|---|
| A — Development / coordination | Work records (workplan, task, intake, …) | Repo files + State Hub (coulombcore) | File-backed, reviewable, multi-day | ||
| B — Recurrence control | Activity definitions + Temporal schedules | activity-core (railiance) | Git definitions; DB schedule state | ||
| C — Ops execution | ops_run (runtime claim object) | activity-core DB + claim API | open → claimed → succeeded \ | failed \ | expired |
| D — External collab | Tracker issues | issue-core optional projection to Forgejo (and others) | Never default for internal automation |
Coding agents orient on Plane A via State Hub. Automation orients on Planes B+C. Humans outside the fleet may see Plane D only when deliberately projected.
2. activity-core owns the ops_run claim queue
When a definition emits a TaskSpec for internal fleet work:
- Write
ops_runrow (durable, claimable) with idempotency key (e.g.fi-daily:2026-08-04for once-per-day briefs). - Optionally dual-write
activity_task_spawnprogress for existing observers. - Do not open a Forgejo issue.
- Do not create a workplan task file for that day’s fire.
Claim API (sketch; implement in ACTIVITY-WP-0026):
POST /ops-runs/claim— lease next open run matching labels / worker idPOST /ops-runs/{id}/complete— succeeded + completion metadataPOST /ops-runs/{id}/fail— failed + retry policyGET /ops-runs?state=open— operator visibility
activity-core remains when / what / where only: it does not run domain LLM sessions or hold tenant git credentials.
3. rein-aharness owns how (approach selection + execute)
- Continuous claim loop (service / Deployment), not dual wall-clock timers as source of truth.
- Host systemd timers become break-glass after cutover.
- Approach selection at claim time (minimal table v1):
| Labels / definition family | Approach |
|---|---|
| mail / mail-intake | deterministic adapter (+ optional triage) |
| research-brief / fi-daily | structured llm-connect (fi-research-brief) |
| binky rhythm | structured llm-connect (brief-daily) |
| agent-session | persona + tool profile session |
| unknown | refuse; do not invent |
- Success posts domain completion event (e.g.
fi_daily_brief) so resolvers setdue=false, and closes theops_run.
4. State Hub remains the work-record read model
- Stays on coulombcore; railiance peers via edge relay / ops-bridge.
- May project open/failed ops runs for fleet ops UI (read-only), but is not the claim authority.
- Development workplans/tasks continue file +
fix-consistencyonly.
5. issue-core is Forgejo (and multi-tracker) facade only
- Forgejo is the self-hosted forge; do not plan for Gitea as a product.
- Optional project / link of an existing work-record UUID to an external issue when collaboration needs it.
- Authenticated
POST /issues/means “create/link external tracker work,” never “spawn fleet automation.” - Internal automation must not use issue-core as the ops claim queue unless a future design adds an internal-only backend with projection hard-off for automation labels — out of scope unless a later ADR says otherwise.
6. Promotion path ops → dev
When an ops run discovers multi-day product work (e.g. “collect Kimi K3 under new quota”), the executor or human promotes an intake / workplan in the domain repo (Plane A). Ops runs never become permanent fake workplans.
Consequences
Positive
- Self-healing recurrence: schedule → claimable row → claim → complete/fail.
- Aligns issue-core INTENT with practice; stops Forgejo spam path.
- Keeps activity-core thin; keeps rein-aharness as sole session/credential shell.
- Preserves the good dev loop (files + State Hub).
Negative / cost
- New schema + API + migrator in activity-core.
- rein-aharness must leave issue-core-only poll as primary for ops.
- Temporary dual-write and dual timers until cutover proven.
Non-goals
- Replacing workplans for development.
- Running domain briefs inside activity-core workers.
- Global
ISSUE_SINK_TYPE=restto Forgejo. - Gitea support or migration paths.
Topology
coulombcore: State Hub + work-record registry
railiance: activity-core (Temporal + ops_run queue)
rein-aharness (claim loop + approach + llm-connect)
domain checkouts
forgejo: optional external collab via issue-core projection only
Implementation stack
| Workplan | Owner | Role |
|---|---|---|
| ACTIVITY-WP-0026 | activity-core | ops_run schema, claim API, emit path, dual-write |
| REIN-A-0002 | rein-aharness | claim loop, approach table, FI/Binky cutover |
| ISSUE-WP-0006 | issue-core | Forgejo-only language; projection boundary; no ops queue |
| STATE-WP-0078 | state-hub | Read projection of ops_run for ops UI (optional consume) |
References
docs/recurring-automations-playbook.mddocs/task-emission-consumer-contract.mddocs/issue-core-emission-boundary.md- ACTIVITY-WP-0022, ACTIVITY-WP-0023 (G2)
the-custodian/canon/standards/work-record-types_v0.1.mdissue-core/INTENT.md(work-record boundary)state-hub/docs/cluster-operating-model.md(coulombcore primary)