rein-aharness/docs/ops-run-claim-loop.md
tegwick 0f01c3e421 feat: route profiled ops runs through Glas
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a02b6f-7db1-7222-918b-e813a6bda38d
2026-08-22 23:55:29 +02:00

5.4 KiB

Ops run claim loop (REIN-A-0002)

Primary intake for activity-core scheduled automation.
Architecture: ACT-ADR-005 · activity-core ops-run-queue.md

Does not require issue-core or Forgejo for FI / Binky-style ops.

Flow

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:

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

# 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):

rein-aharness poll --source=issue-core
rein-aharness run --from-issue-core

Install claim-loop on railiance01 (user systemd)

# 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.

# 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:

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