activity-core/docs/adr/adr-005-ops-runs-vs-dev-work-records.md
tegwick f0a897e088
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Container Image / build-and-push (push) Successful in 33s
Harden ops run identity and leases
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a028de-e2c8-7732-8521-46a7fc5db82f
2026-08-23 13:01:46 +02:00

8.5 KiB
Raw Blame History

id type title status owner revision last_reviewed review_interval decided_by date scope affects tags supersedes_notes
ACT-ADR-005 architecture-decision-record Ops runs vs development work records — claim queue and plane split accepted activity-core accepted-1 2026-08-03 6m Bernd Worsch 2026-08-03 cross-repo
activity-core
rein-aharness
state-hub
issue-core
domain repos (Binky, freedom-intelligence, …)
architecture
activity-core
ops-run
work-records
rein-aharness
issue-core
forgejo
Clarifies ACTIVITY-WP-0022 sink practice and G2 executor gap from ACTIVITY-WP-0023. Does not reverse WP-0022 (no default external tracker issues).

ACT-ADR-005: Ops runs vs development work records

Status

Accepted (2026-08-03).

Context

The fleet has two healthy patterns that were forced into one vague “task” idea:

  1. 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 and work-record-types_v0.1.md and is how humans and coding agents ship product.

  2. Operations / scheduled automation — activity-core Temporal schedules (when / what / where), rein-aharness execution (how), completion events (idempotence). Instances of “todays 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-cores 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.

This is also the precise meaning of INTENT's no-task-lifecycle boundary: ops_run claim/lease/outcome is durable runtime delivery state, not work-item state. It may not grow assignment, commitments, dependencies, project phases, or manually managed task status.

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:

  1. Write ops_run row (durable, claimable) with idempotency key
    (e.g. fi-daily:2026-08-04 for once-per-day briefs).
  2. Optionally dual-write activity_task_spawn progress for existing observers.
  3. Do not open a Forgejo issue.
  4. Do not create a workplan task file for that days fire.

Claim API (implemented in ACTIVITY-WP-0026 and hardened in ACTIVITY-WP-0036):

  • POST /ops-runs/claim — lease next open run matching labels / worker id
  • POST /ops-runs/{id}/complete — succeeded + completion metadata
  • POST /ops-runs/{id}/fail — failed + retry policy
  • GET /ops-runs?state=open — operator visibility

Worker mutation credentials are bound to one configured queue worker_id; the request body cannot assert a different claim owner. Heartbeat, completion, and failure lock the row and require its lease deadline to remain strictly in the future. Operator/SSO credentials provide visibility and explicit administration, but do not act as a normal worker identity.

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 set due=false, and closes the ops_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-consistency only.

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=rest to 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.md
  • docs/task-emission-consumer-contract.md
  • docs/issue-core-emission-boundary.md
  • ACTIVITY-WP-0022, ACTIVITY-WP-0023 (G2)
  • the-custodian/canon/standards/work-record-types_v0.1.md
  • issue-core/INTENT.md (work-record boundary)
  • state-hub/docs/cluster-operating-model.md (coulombcore primary)