# Ops run claim loop (REIN-A-0002) **Primary intake** for activity-core scheduled automation. **Architecture:** ACT-ADR-005 · activity-core [ops-run-queue.md](../../activity-core/docs/ops-run-queue.md) Does **not** require issue-core or Forgejo for FI / Binky-style ops. ## Flow ```text activity-core emit_tasks → INSERT ops_run (open, optional harness_profile_ref) + activity_task_spawn │ ▼ rein-aharness claim-loop POST /ops-runs/claim harness_profile_ref present? ─ yes → Glas ExecutionRequest → gateway │ no └─→ select_approach(labels, definition, hint) → legacy executor POST complete | fail(+reopen) ``` `harness_profile_ref` is authoritative under ACT-ADR-006. A profiled row never consults `approach_hint`, `select_approach`, or a legacy executor, including after profile resolution refusal. The claim worker sends the complete `GatewayResult` to Activity Core; Activity Core persists its allowlisted `ExecutionEvidence` and drops direct tool output/error. Profile-absent rows keep the legacy coexistence path until the migration flag is enabled upstream. ## Environment | Variable | Default | Meaning | | -------- | ------- | ------- | | `ACTIVITY_CORE_URL` | `http://127.0.0.1:8010` | actcore-api base | | `ACTIVITY_CORE_WORKER_TOKEN` | unset | `X-Worker-Token` / Bearer | | `AGENT_HARNESS_WORKER_ID` | `rein-aharness@hostname` | claim owner | | `AGENT_HARNESS_OPS_LABELS` | `automated` | claim label filter | | `AGENT_HARNESS_OPS_LABELS_MODE` | `any` | `any` or `all` | | `AGENT_HARNESS_OPS_LEASE_SECONDS` | `900` | claim lease | | `AGENT_HARNESS_CLAIM_INTERVAL` | `30` | empty-queue poll interval | | `AGENT_HARNESS_REPO_MAP` | `{}` | JSON slug→checkout path | | `AGENT_HARNESS_REPO_ROOTS` | `~:~/work` | slug search roots | Example map on railiance01: ```bash export AGENT_HARNESS_REPO_MAP='{ "freedom-intelligence":"/home/tegwick/freedom-intelligence", "binky-control":"/home/tegwick/binky-control" }' export ACTIVITY_CORE_URL=http://127.0.0.1:8010 # or ClusterIP via tunnel export ACTIVITY_CORE_WORKER_TOKEN=… # from actcore-runtime-secret ``` ## Approach registry | Match | Approach | | ----- | -------- | | labels `research-brief` / `freedom-intelligence` or fi-daily* | `fi-research-brief` | | labels `rhythm` / `binky-daily` or binky-daily* | `brief-daily` | | labels `binky` + `weekly-review` or binky-weekly-review* | `brief-weekly` | | labels `mail-intake` | `mail-scan` then `mail-triage` | | labels `agent-session` | agentic `run` (TaskSpec) | | none | fail ops_run (no silent drop) | For a profile-absent legacy row, optional `approach_hint` overrides matching. It has no effect when `harness_profile_ref` is present. Add a row: edit `rein_aharness/approaches.py` (`APPROACH_RULES` + `execute_approach`) and tests in `tests/test_approaches.py`. Prefer cheapest correct path (structured llm-connect before open-ended agent session). ## CLI ```bash # Peek open ops_runs + selected approach rein-aharness poll --source=ops-run --no-claim # Claim one, execute approach, complete/fail rein-aharness run --from-ops-run # or rein-aharness poll --source=ops-run # Continuous worker (systemd) rein-aharness claim-loop rein-aharness claim-loop --once --verbose # Dry-run: claim then fail+reopen without domain work rein-aharness poll --source=ops-run --dry-run ``` Legacy issue-core (external tickets only): ```bash rein-aharness poll --source=issue-core rein-aharness run --from-issue-core ``` ## Install claim-loop on railiance01 (user systemd) ```bash # From rein-aharness checkout on railiance01 python3 -m venv .venv .venv/bin/pip install -e . -e ../llm-connect -e ../sand-boxer -e ../glas-harness ./deploy/scripts/install-claim-loop-user.sh # Or manually: mkdir -p ~/.config/systemd/user cp deploy/systemd/rein-aharness-claim-loop.service ~/.config/systemd/user/ # Edit Environment= paths / token in the unit or env file systemctl --user daemon-reload systemctl --user enable --now rein-aharness-claim-loop.service journalctl --user -u rein-aharness-claim-loop -f ``` ### Host access to cluster services (no port-forward) On railiance01 (single-node k3s), set **k8s://** pseudo-URLs in `claim-loop.env` (see `deploy/systemd/claim-loop.env.example`). The `~/bin/rein-aharness-claim` wrapper resolves them to Service ClusterIPs at start. Canon: activity-core `docs/llm-connect-host-access.md`. ```bash # migrate existing env ./deploy/scripts/refresh-claim-loop-k8s-urls.sh # reinstall wrapper ./deploy/scripts/install-claim-loop-user.sh systemctl --user restart rein-aharness-claim-loop.service ``` **Deprecated:** long-lived `kubectl port-forward` to 127.0.0.1:8010 / :8080. Legacy note (break-glass only) if ClusterIP routing is broken: ```bash kubectl -n activity-core port-forward svc/actcore-api 8010:8010 export ACTIVITY_CORE_URL=http://127.0.0.1:8010 ``` ## Cutover from host timers (T05) Host oneshot timers (FI 07:35, Binky rhythm) remain **break-glass** until: 1. claim-loop is running and healthy 2. **5 clean weekday cycles** (or 5 forced triggers) with open→claimed→succeeded 3. Then disable or demote timers — see `deploy/docs/timer-cutover.md` Until then dual-path is intentional: schedule truth is activity-core; timers are a safety net if claim-loop is down. ## Labels required on definitions | Domain | Labels (min) | | ------ | ------------ | | Freedom Intelligence | `automated`, `research-brief` (already on FI rule) | | Binky daily | `automated`, `rhythm` | | Binky mail | `automated`, `mail-intake` |