feat(pmo): ping-charles SMS script (curl SMTP relay, offline queue, 0600 config)
This commit is contained in:
@@ -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.
|
||||
@@ -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
|
||||
|
||||
@@ -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: <!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height" />
|
||||
|
||||
<!-- generated from dashboard/public/img/app_down.svg -->
|
||||
<link rel="icon" href="da...
|
||||
harness: loop: note writeback failed for 519: redmine HTTP 502: <!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height" />
|
||||
|
||||
<!-- generated from dashboard/public/img/app_down.svg -->
|
||||
<link rel="icon" href="da...
|
||||
harness: loop: refresh failed for 519: redmine HTTP 502: <!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height" />
|
||||
|
||||
<!-- generated from dashboard/public/img/app_down.svg -->
|
||||
<link rel="icon" href="da... (a re-dispatch may follow)
|
||||
harness: loop: dispatch 518 (updated 2026-08-29T06:23:26-05:00): "Alpha: triage open MOPAC tickets — next-action note on each New ticket"
|
||||
harness: task 518 (redmine): "Alpha: triage open MOPAC tickets — next-action note on each New ticket" class=study -> mopac-study -> glm-4.7-flash
|
||||
|
||||
@@ -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:
|
||||
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):
|
||||
@@ -0,0 +1 @@
|
||||
[info 08-29 06:29 MOPAC] alpha loop live, bootstrap turn dispatched, CLI building
|
||||
Executable
+50
@@ -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
|
||||
Reference in New Issue
Block a user