diff --git a/inbox-work/TASK-20260829-1500-bootstrap.md b/inbox-work/TASK-20260829-1500-bootstrap.md new file mode 100644 index 0000000..0ea95d7 --- /dev/null +++ b/inbox-work/TASK-20260829-1500-bootstrap.md @@ -0,0 +1,47 @@ +# TASK: FINAL crush turn — harness runtime container + tool-widening (Redmine 521 + bootstrap) + +## Context +Charles 2026-08-29: "alpha asap, then kill the screen/crush stack; bootstrap +the harness (docker container) FIRST." This is the LAST crush turn on the +harness repo. After it: the loop runs containerized AND its turns can do +build work — the self-hosted stack builds everything to beta/prod itself. + +## Part A — runtime container (bootstrap) +1. `deploy/Dockerfile.runtime` — FROM alpine (digest-pinned), copies the + static release binary, dumb-init or tini entrypoint (or Go signal + handling — SIGINT graceful stop already exists). +2. `deploy/compose.yaml` — service `mopac-loop`: image built from + Dockerfile.runtime, `restart: unless-stopped`, env-file (0600) for + HARNESS_REDMINE_KEY / HARNESS_LITELLM_KEY, bind mounts: repo reports/ + + state/ (persistent), harness.toml read-only. Healthcheck: loop + exposes nothing by default — add `--healthz-port` flag or reuse events + healthz; simplest: a `loop --status-file` heartbeat the healthcheck + reads (age < 3x poll interval). +3. Verify LIVE: compose up, confirm scan + at least one autonomous turn + (the alpha queue has open tasks), `docker inspect` restart policy, + then leave the container RUNNING (it replaces the dev.sh screen). +4. Kill switch documented: `docker compose -f deploy/compose.yaml down`. + +## Part B — tool-widening (ticket 521: loop turns do build work) +1. File tools behind the existing gate discipline (deny-first, scoped to + work_root, logged): read (offset/limit), exact-match edit, write (new + files), ls/glob/grep. Same denial-count + feedback semantics as the + bash tool. +2. Bash gate: verify `./dev.sh {build,vet,test}` and `git commit/push` + pass the allow-list from work_root; extend the allow-list minimally if + not (no broadening beyond dev.sh + git). +3. Per-turn wall-clock timeout (config `[loop] turn_timeout_secs`, + default 1800) — expired turn = partial REPORT + release. +4. Tests: file-tool gate denials, edit exact-match semantics, timeout + expiry, and an end-to-end loop turn against the fake Redmine that + edits a file + runs a no-op command + REPORTs. + +## Discipline +TDD red/green, docker dev builder only, atomic conventional commits, +push origin main, NEVER broad pkill (docker stop by name is fine). + +## Deliverable +`REPORT-20260829-1500-bootstrap.md` in `~/.coordinate/inbox-pmo/`: +container instructions (up/down/logs), healthcheck design, tool surface +added, test results, and confirmation the container processed a real +alpha task autonomously. diff --git a/log.md b/log.md index b293abf..d8b93c8 100644 --- a/log.md +++ b/log.md @@ -87,3 +87,4 @@ 2026-08-29T06:19:29-05:00 | PMO | 497 corrected: BW machine accounts map to CLOUDRON accounts (per-vertical API identities), not raw Linux accounts 2026-08-29T06:20:17-05:00 | PMO | identity model ticket filed (linux runtime + cloudron acting, N per account); 497 corrected 2026-08-29T06:21:06-05:00 | PMO | identity model FINAL: 1:1 linux->BW vault (cloudron+3rd-party entries); acting identity selected per task/turn, stamped on all actions + usage +2026-08-29T06:26:47-05:00 | PMO | ALPHA LIVE: loop autonomous (520 done New->Resolved+note, 519 running); q12 killed; q14 CLI keeps running; tool-widening ticket filed Immediate diff --git a/logs/loop-alpha.log b/logs/loop-alpha.log index 83803a1..18310eb 100644 --- a/logs/loop-alpha.log +++ b/logs/loop-alpha.log @@ -8,3 +8,30 @@ harness: loop: status New -> Resolved on #520 harness: loop: noted REPORT on #520 harness: loop: dispatch 519 (updated 2026-08-29T06:23:26-05:00): "Alpha: draft briefing v0 format as note on 495" harness: task 519 (redmine): "Alpha: draft briefing v0 format as note on 495" class=study -> mopac-study -> glm-4.7-flash +harness: REPORT reports/REPORT-demo-519-20260829-112832.md +harness: loop: status transition failed for 519: redmine HTTP 502: + + + + + + + + + + + + + + + mopac-study -> glm-4.7-flash diff --git a/logs/q-bootstrap.log b/logs/q-bootstrap.log new file mode 100644 index 0000000..e69de29 diff --git a/logs/q-redmine-cli.log b/logs/q-redmine-cli.log index b49bd7f..89bb9e9 100644 --- a/logs/q-redmine-cli.log +++ b/logs/q-redmine-cli.log @@ -1 +1 @@ -I have full context: sibling pattern (mopac-bitwarden-go), harness fake-server test style, PMO python glue to replace, creds at `~/.creds/redmine.env`, Redmine at projects.knownelement.com. Setting up the work plan.Toolchain verified. Creating the repo via Gitea API (idempotent check first):Repo created. Cloning and seeding with LICENSE:Harness done. Now TDD config package — tests first (red):Red confirmed. Implementation:Green. Two atomic commits: \ No newline at end of file +I have full context: sibling pattern (mopac-bitwarden-go), harness fake-server test style, PMO python glue to replace, creds at `~/.creds/redmine.env`, Redmine at projects.knownelement.com. Setting up the work plan.Toolchain verified. Creating the repo via Gitea API (idempotent check first):Repo created. Cloning and seeding with LICENSE:Harness done. Now TDD config package — tests first (red):Red confirmed. Implementation:Green. Two atomic commits:Now the core piece. First the stateful fake Redmine server (test infrastructure, harness-style):Now the library test suite (red first): \ No newline at end of file diff --git a/outbox/ping-queue.txt b/outbox/ping-queue.txt new file mode 100644 index 0000000..5bc3150 --- /dev/null +++ b/outbox/ping-queue.txt @@ -0,0 +1 @@ +[info 08-29 06:29 MOPAC] alpha loop live, bootstrap turn dispatched, CLI building diff --git a/scripts/ping-charles.sh b/scripts/ping-charles.sh new file mode 100755 index 0000000..b21d02a --- /dev/null +++ b/scripts/ping-charles.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +# ping-charles.sh — SMS Charles via T-Mobile email-to-SMS gateway. +# Usage: ping-charles.sh "short message" [severity] +# Relay: curl SMTP (Cloudron mail submission). Config: /home/reachableceo/.coordinate/secrets/ping-charles.env (0600) +# PING_SMTP_HOST=mail.knownelement.com:587 +# PING_SMTP_USER=... +# PING_SMTP_PASS=... +# PING_FROM=... +# Until wired, messages queue in ~/.coordinate/outbox/ping-queue.txt (flushed on first successful send). + +set -euo pipefail +DEST="8182807059@tmomail.net" +CONF="$HOME/.coordinate/secrets/ping-charles.env" +QUEUE="$HOME/.coordinate/outbox/ping-queue.txt" +MSG="${1:?usage: ping-charles.sh \"message\" [severity]}" +SEV="${2:-info}" +STAMP="$(date '+%m-%d %H:%M')" +LINE="[$SEV $STAMP MOPAC] $MSG" + +if [ ! -f "$CONF" ]; then + mkdir -p "$(dirname "$QUEUE")" + echo "$LINE" >> "$QUEUE" + echo "ping queued (no relay config): $LINE" >&2 + exit 0 +fi +set -a; . "$CONF"; set +a + +send_one() { + printf 'From: %s\nTo: %s\nSubject: MOPAC %s\nContent-Type: text/plain; charset=UTF-8\n\n%s\n' \ + "$PING_FROM" "$DEST" "$SEV" "$1" \ + | curl -sS --max-time 30 --url "smtp://$PING_SMTP_HOST" \ + --mail-from "$PING_FROM" --mail-rcpt "$DEST" \ + -u "$PING_SMTP_USER:$PING_SMTP_PASS" \ + -T - && return 0 || return 1 +} + +if [ -f "$QUEUE" ] && [ -s "$QUEUE" ]; then + while IFS= read -r queued; do + send_one "$queued" && sed -i '1d' "$QUEUE" || break + done < /dev/null +fi + +if send_one "$LINE"; then + echo "sms sent: $LINE" >&2 + exit 0 +else + mkdir -p "$(dirname "$QUEUE")"; echo "$LINE" >> "$QUEUE" + echo "sms send FAILED, queued: $LINE" >&2 + exit 1 +fi