Files
mopac-pmo/archive/TASK-20260828-2131-selfhost-core.md

3.2 KiB

TASK: MOPAC self-host core — harness loop replaces the bash stack

Context

Charles (2026-08-28 ~21:30): "get to be self hosting asap — smallest core that can bootstrap itself (redmine/gitea interaction fine, discourse later)" and "moving away from the brittle _crossfeed setup and doorbell PMO<->work channel". The MVP bar is already met (harness once --demo live, REPORT in reports/). This turn makes the harness drive ITSELF: Redmine is the SoR, the conductor is the worker, no bash middle layer.

What dies (list explicitly in REPORT, do not delete yet)

  • /home/_crossfeed/tooling/agent-stack/semaphore.sh + slot-file cap (replaced by in-process concurrency limit)
  • ~/.coordinate/scripts/queue-next.sh, queue-after.sh, pmo-heartbeat.sh (replaced by the loop + its own wake discipline)
  • dispatch-turn.sh doorbell screen -S reachableceo-PMO -X stuff — note: this doorbell is ALREADY dead (screen is named RCEO-PMO, message silently dropped). Mention in REPORT; deletion happens after Charles signs off.

Scope — smallest self-bootstrapping core

  1. harness loop subcommand (Go, stdlib only, chainable with once):
    • Poll Redmine intake (existing /issues.json query) on interval (config, default 120s). --once flag = single scan (cron-able).
    • For each new/updated task issue not yet processed: run the bounded turn (existing conductor), then write REPORT back (existing Redmine note writeback — currently stubbed; implement the note POST) and update issue status per config map (e.g. In Progress -> Done).
    • State: append-only JSONL (one event per dispatch/report), dedup by issue id + updated_on. No _crossfeed paths, no slot files.
    • v0 = SEQUENTIAL turns (one at a time). Concurrency knob can come later.
    • Gitea: optional REPORT commit step behind config flag (off by default). Discourse: out of scope.
  2. Credentials: harness.toml key_ref gains mpk: scheme → POST to keyproxy /v1/resolve (see ukrrs/mopac-keyproxy, built tonight). env: / file: refs keep working as fallback, so the loop runs with or without keyproxy up. Do not hardcode hosts — all in harness.toml.
  3. harness loop supervises ITS OWN turns; PMO (me) verifies REPORTs and archives. The loop logs one line per action to stdout + JSONL.

Build discipline

  • ALL DEV IN DOCKER: digest-pinned golang:1.26 builder, bind-mounted repo, Makefile build/vet/test through docker run. No host Go toolchain.
  • Docs standard applies (README status table + config reference update).
  • Tests: loop end-to-end against a fake Redmine HTTP server (pattern exists in internal/loop tests) — dispatch, dedup, writeback, status map.

Deliverables

  • Commits + push to ukrrs/MOPAC main.
  • REPORT-20260828-2131-selfhost-core.md in ~/.coordinate/inbox-pmo/: what runs, exact command to start the loop, what it replaces (the die list), how to roll back (just stop the loop, bash stack still present).

Added by PMO (21:30, post docs-turn rot finding)

  • Rename Go module path reachableceo/... -> ukrrs.com/mopac/... or similar (go.mod module line + all internal imports). Mechanical, verify with build+vet+test in the Docker builder. Cited as doc rot in docs-standards REPORT; folding here since it is a source change.