repo-manager/workplans/RMGR-WP-0020-bounded-freshness-runner.md
tegwick 656e027788 RMGR-WP-0020: T03 done, block on the Forgejo read token
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 692292@bnt-lap001
Assistant-Session: 89c7d8e8-e1b3-47eb-9411-a0ab227fca2f
2026-10-04 16:30:36 +02:00

8.4 KiB
Raw Blame History

id type title domain repo status owner created updated flavor related depends_on state_hub_workstream_id
RMGR-WP-0020 workplan Bounded Forge-sourced freshness runner via Activity Core infotech repo-manager blocked claude-repo-manager 2026-10-04 2026-10-04 implementation
STATE-WP-0094
STATE-WP-0086
RMGR-WP-0005
RMGR-WP-0017
RMGR-WP-0019
cda2296b-9fe0-5e9e-b630-7e143a3c2796

Status note

2026-10-04: the founder approved the T01–T03 design, so the status is now active. Approval covers source implementation only. Deployment, schedules and any non-dry-run fire stay gated behind T04/T05.

Goal

Close the freshness half of STATE-WP-0094-T05 with a supported, bounded and durable freshness runner. Activity Core schedules it. Repo Manager owns the engine and selection. State Hub only receives immutable Forge-commit projections.

Evidence of need: docs/evidence/2026-10-04-freshness-coverage.md. No C-rule definition is active; the only one (state-hub-consistency-sweep) is paused. Of 141 active repositories, 35 synced within 24 h, the median age is 109 h and the maximum is 949 h. Receipts are not durable.

Non-goals and constraints:

  • Not the legacy --all --remote sweep or POST /consistency/sweep/remote-all. ADR-002 forbids central reads or writes of workstation checkouts. state-hub-consistency-sweep stays paused and is retired, not repurposed.
  • No full-fleet fan-out per fire, and no for_each task spawning (lesson from daily-sbom-catchup / ACTIVITY-WP-0030).
  • No graph-edge receiving. That remains STATE-WP-0094-T05 edge contract work.
  • No change in this workplan is enabled, deployed or scheduled without founder authorization (T05).

Durable freshness receipt contract

id: RMGR-WP-0020-T01
status: done
priority: high
state_hub_task_id: "065ef2ad-82ff-5d02-80e1-717e4d1819af"

Define repo-manager.freshness-receipt.v1. It records the repository, the Forge commit read via the Forge API or ls-remote, the expected/derived commit from the State Hub repository-projection-reconcile.v1 receipt, the outcome (applied|noop|queued|failed|refused|skipped), the reason, the runner revision, the definition id/version, the fire id and UTC timestamps. Choose one durable read-only-queryable home: the Activity Core run artifact / ops-runs store, or the Repo Manager evidence sink. Do not use the State Hub DB. This closes the "last successful / failed receipt" gap.

Implementation note (2026-10-04): src/repo_manager/freshness_receipts.py. The store is an append-only JSON Lines file in the Repo Manager evidence sink, ~/.repo-manager/freshness-receipts.jsonl (override with RM_FRESHNESS_RECEIPTS_PATH or the freshness_receipts_path key in the dual-run config). It is the same persistence pattern as the mutation meter and the idempotency store, so it needs no new service. Appends are flock-serialised and fsynced. The store is queryable with rmgr freshness receipts [--repo] [--latest]. A receipt cannot be constructed without a verified primary hub. A deployed runner must mount this path on a persistent volume (T04).

Bounded Forge-sourced selector and runner

id: RMGR-WP-0020-T02
status: done
priority: high
state_hub_task_id: "8333b4a7-9d12-57a1-ba9d-d447a0ba26b5"

Add rmgr freshness run --limit N --max-seconds S [--dry-run]:

  1. Rank active repositories by the last durable receipt, with never-received repositories first. Before T01 data exists, fall back to backing_synced_at.
  2. For each of at most N, resolve the Forge HEAD and skip with noop when the last receipt already matches it.
  3. Otherwise reconcile from the immutable Forge commit (STATE-WP-0086 path), never from a checkout. Verify the receipt the way projection_sync does (primary, instance label, expected == derived == Forge commit).
  4. Persist one receipt per repository and stop at max_seconds.

--dry-run emits the plan only. Tests must cover the ranking, the noop and the refusals (RMGR-WP-0014/0019 holes stay refused, never forced).

Implementation note (2026-10-04): src/repo_manager/freshness_runner.py, CLI rmgr freshness run --limit N --max-seconds S [--dry-run]. Optional flags: --api-base, --expected-instance-label, --forge-base, --forge-owner, --receipts-path, --definition-id, --definition-version, --fire-id. The Forge HEAD is read over the Forgejo HTTP API (forgejo.coulomb.social/coulomb/<slug>, the same source the hub derives from). It does not use the workstation SSH alias in remote_url. Repositories that already have receipts rank by their latest receipt of any outcome, so refused holes cannot starve the rotation. The backing_synced_at fallback is capped at 25 % of the budget (at most 60 s). Snapshots that do not answer in time fall back to the /repos/ row's last_state_synced_at. Exit codes: 0 ok, 1 failed or aborted attempts, 2 bad arguments, 3 hub not verified primary (no receipts written), 4 hub unreachable. Tests: tests/test_freshness_runner.py.

Prepared ActivityDefinition (draft, disabled)

id: RMGR-WP-0020-T03
status: done
priority: medium
state_hub_task_id: "76925a63-ff7b-5487-bcb4-b7ed64a3add5"

Hand the draft below to the activity-core owner as a proposal. Do not edit activity-core from this repo. It needs a repo-manager context source (or the existing HTTP sink pointed at a Repo Manager endpoint) that calls T02.

id: repo-manager-bounded-freshness
name: Repo Manager Bounded Freshness
type: activity-definition
version: "0.1"
enabled: false          # founder flips after T05 proof; never in this workplan
owner: repo-manager
governance: custodian
status: proposed
trigger:
  type: cron
  cron_expression: "40 */2 * * *"   # every 2 h, offset from other fleet jobs
  timezone: UTC
  misfire_policy: skip
context_sources:
  - type: repo-manager
    query: freshness_run
    operation: repo_manager_freshness
    required: true
    params:
      limit: 6            # 141 repos / 6 per fire x 12 fires/day ~= 2-day full rotation
      max_seconds: 240
      apply: true         # declared bounded side-effect; manual runs require confirmation
    bind_to: context.freshness

Target SLA once enabled: every active, admissible repository has a receipt younger than 48 h. Non-admissible repositories appear as explicit refused holes.

Activation and rollback plan (founder review)

id: RMGR-WP-0020-T04
status: wait
blocking_reason: "Runner deployed in the Activity Core worker (Argo a86a143); apply trials need the read-only Forgejo token (draft CCR-2026-0034)"
priority: high
state_hub_task_id: "8941af6b-f2c1-5ad1-bb13-a757b3f71c12"

Activation, each step gated:

  1. Founder approves T01–T03 design. Deploy the Repo Manager runner image (pinned digest) and the context source; the definition ships with enabled: false.
  2. Run two manual fires with --dry-run, then two manual fires with apply, limit: 3. Verify receipts, zero task spawns and no checkout access.
  3. Founder flips enabled: true. Re-run python -m repo_manager.freshness after 48 h and accept when the 48 h coverage of admissible repositories is 100 %.

Rollback: set enabled: false (or status: paused) in the definition ConfigMap projection and restart nothing else. The runner holds no state beyond receipts, and projections are idempotent per commit (RMGR-WP-0017), so no data rollback is needed. To revert the runner itself, redeploy the previous image digest. Kill criteria: any receipt with an unverified primary, any checkout read, or a fire that exceeds max_seconds twice in a row.

Proof and handback to State Hub

id: RMGR-WP-0020-T05
status: wait
blocking_reason: "Proof follows the T04 trials and the memo 4 schedule"
priority: medium
state_hub_task_id: "f01fb241-8f35-52ba-9119-86c9cc359528"

After founder-authorized activation, record bounded freshness evidence: runner revision, active definition, per-repository Forge commit, last success and failed/queued receipts, and measured window. Return it to STATE-WP-0094-T05. Historical registrar holes stay listed until each is resolved.

2026-10-04 session close: workplan set to blocked. T03 done: the disabled definition repo-manager-bounded-freshness landed in activity-core (ACTIVITY-WP-0044, b2c6c56). The founder accepted memos 3 and 4 (approvals 4ae04c8e, b75752d1). rmgr e77f8b4 ships in the Activity Core worker image git-34d1dc5 (Argo a86a143), with receipts on PVC actcore-ops-artefacts. No fire has run. Apply trials need a read-only Forgejo token; the draft lane is in railiance-platform.