Accept ACT-ADR-005 and open ACTIVITY-WP-0026 for ops_run claim queue.
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s

Split ops runs (claimable DB) from development work records (repo + State Hub),
ban default Forgejo queueing for automation, and stack WP-0026 with rein-aharness
and issue-core follow-ups. Forgejo-only forge language; no Gitea product path.
This commit is contained in:
tegwick 2026-08-03 18:51:37 +02:00
parent 747d63a993
commit d1e24348c3
4 changed files with 422 additions and 10 deletions

View file

@ -0,0 +1,211 @@
---
id: ACTIVITY-WP-0026
type: workplan
title: "Ops run claim queue — durable claimable work for scheduled automation"
domain: infotech
repo: activity-core
status: ready
owner: grok
topic_slug: activity-core
priority: high
created: "2026-08-03"
updated: "2026-08-03"
depends_on: []
related:
- ACT-ADR-005
- ACTIVITY-WP-0022
- ACTIVITY-WP-0023
- REIN-A-0002
- ISSUE-WP-0006
- STATE-WP-0078
---
# ACTIVITY-WP-0026 — Ops run claim queue
## Origin
ACT-ADR-005 (ops runs vs development work records). Closes **G2 executor gap**
without reopening default **Forgejo** issue spam (ACTIVITY-WP-0022).
## Goal
Make internal scheduled automation emit a **claimable `ops_run`** in activity-core
so rein-aharness (and peers) can claim, execute, complete, or fail with lease
semantics — while activity-core stays when/what/where only.
**Non-goals:** execute domain work in Temporal activities; use issue-core or
Forgejo as the claim queue; invent Gitea support.
## Architecture (target)
```text
Temporal schedule / trigger
→ resolve context (e.g. fi_brief_status)
→ rules produce TaskSpec(s)
→ INSERT ops_run (open) + optional activity_task_spawn progress
→ rein-aharness claim loop
→ complete/fail + domain completion event (fi_daily_brief, …)
```
## Tasks
### T01 — Spec ops_run model and API
```task
id: ACTIVITY-WP-0026-T01
status: todo
priority: high
```
Document in `docs/ops-run-queue.md`:
| Field | Notes |
| ----- | ----- |
| `id` | UUIDv7 |
| `activity_definition_id` | FK |
| `idempotency_key` | unique; e.g. `{def}:{date}` or hash of spawn key |
| `target_repo` | string |
| `title`, `description` | from TaskSpec |
| `labels` | JSON array |
| `priority` | |
| `state` | `open \| claimed \| succeeded \| failed \| expired` |
| `claim_owner` | worker identity |
| `lease_until` | timestamptz |
| `attempt` | int |
| `source_type`, `source_id`, `triggering_event_id` | audit |
| `approach_hint` | optional from definition/rule |
| `result` | JSON completion metadata |
| `created_at`, `updated_at` | |
API (REST on actcore-api):
- `POST /ops-runs/claim` body: `{ worker_id, labels?, limit? }` → runs
- `POST /ops-runs/{id}/heartbeat` extend lease
- `POST /ops-runs/{id}/complete` / `fail`
- `GET /ops-runs` filter by state, definition, since
Auth: same operator/worker posture as existing admin APIs (document; prefer
service credential for harness, SSO for humans later).
**Done when:** spec reviewed against ACT-ADR-005; OpenAPI-ready field list.
---
### T02 — Schema migration + ORM
```task
id: ACTIVITY-WP-0026-T02
status: todo
priority: high
```
Alembic migration for `ops_runs` (+ indexes on `state`, `lease_until`,
`idempotency_key` unique). SQLAlchemy model. Unit tests for constraints.
**Done when:** migrate up/down clean on dev; model tests green.
---
### T03 — Emit path: create ops_run on TaskSpec emit
```task
id: ACTIVITY-WP-0026-T03
status: todo
priority: high
```
On successful rule emit (internal fleet path):
1. Upsert/insert `ops_run` with idempotency (duplicate key → no second open row).
2. Keep **dual-write** `activity_task_spawn` progress for transition period.
3. Still write `task_spawn_log` audit.
4. **Never** require Forgejo / issue-core rest for this path.
Default sink remains `state-hub` for progress; ops_run is **additional**
primary for claim.
**Done when:** schedule fire or `/trigger` produces open `ops_run` for FI and
Binky-style definitions; tests with mocked sink.
---
### T04 — Claim / lease / expire worker helpers
```task
id: ACTIVITY-WP-0026-T04
status: todo
priority: high
```
Implement claim with `SELECT … FOR UPDATE SKIP LOCKED` (or equivalent) and
lease TTL (config: e.g. 15m). Expire job or on-claim sweep: `claimed` past
`lease_until``open` (retry) or `expired` after max attempts.
**Done when:** concurrent claim tests; lease expiry documented.
---
### T05 — Ops visibility: list + SLA signals
```task
id: ACTIVITY-WP-0026-T05
status: todo
priority: medium
```
- Extend `/ops/automations/status` (or sibling) with open/claimed/failed counts.
- Signal: “spawned/open without complete by SLA” (config, default 1h for daily).
- Document in runbook.
**Done when:** operator can see stuck ops_runs without SQL.
---
### T06 — Contract + consumer guide
```task
id: ACTIVITY-WP-0026-T06
status: todo
priority: medium
```
Update:
- `docs/task-emission-consumer-contract.md` — claim ops_run, not issue-core
- `docs/recurring-automations-playbook.md` — reference ACT-ADR-005
- `docs/issue-core-emission-boundary.md` — ops_run is not issue-core
- Use **Forgejo** naming only (no Gitea product references)
**Done when:** REIN-A-0002 can implement against the contract without guesswork.
---
### T07 — Railiance rollout + dual-path flag
```task
id: ACTIVITY-WP-0026-T07
status: todo
priority: medium
```
- Feature flag `OPS_RUN_QUEUE_ENABLED=true` on railiance.
- Deploy migration + API.
- Smoke: trigger FI definition → open ops_run visible.
- Coordinate REIN-A-0002 cutover; keep host timers until harness claim proven.
**Done when:** prod smoke checklist green; flag documented.
## Acceptance
- [ ] ACT-ADR-005 referenced in SCOPE gaps (G2 disposition → this WP)
- [ ] ops_run durable + claimable without Forgejo
- [ ] Emit dual-write progress for transition
- [ ] Docs use Forgejo-only language for self-hosted forge
- [ ] REIN-A-0002 unblocked
## Out of scope
- rein-aharness claim loop (REIN-A-0002)
- State Hub full write path for claim (read projection = STATE-WP-0078)
- issue-core internal SQLite “fake queue”
- Gitea compatibility