[object Object]

← back to AbramsEgo

harness: restart-forever build loop (isolated queue + launchd plist), pm2 keep-alive, seeded build tasks, gated ship memo

e8bcb58b591b7b54a43f415f8b37e1304a548456 · 2026-07-01 10:18:09 -0700 · Steve (AbramsEgo)

Files touched

Diff

commit e8bcb58b591b7b54a43f415f8b37e1304a548456
Author: Steve (AbramsEgo) <steve@designerwallcoverings.com>
Date:   Wed Jul 1 10:18:09 2026 -0700

    harness: restart-forever build loop (isolated queue + launchd plist), pm2 keep-alive, seeded build tasks, gated ship memo
---
 build-queue/tasks/01-cost-trend-chart.md |  9 +++++
 build-queue/tasks/02-breakeven-target.md |  9 +++++
 build-queue/tasks/03-revenue-plumbing.md | 20 +++++++++++
 scripts/build-loop.sh                    | 58 ++++++++++++++++++++++++++++++++
 4 files changed, 96 insertions(+)

diff --git a/build-queue/tasks/01-cost-trend-chart.md b/build-queue/tasks/01-cost-trend-chart.md
new file mode 100644
index 0000000..7d71d41
--- /dev/null
+++ b/build-queue/tasks/01-cost-trend-chart.md
@@ -0,0 +1,9 @@
+# AbramsEgo build task 01 — cost trend chart + per-model breakdown
+
+Add to AbramsEgo (~/Projects/AbramsEgo) a **7-day cost trend** and **per-model breakdown**:
+
+1. In `server.js`, extend the cost collector: add a `collectCostSeries()` that buckets `~/.claude/cost-ledger.jsonl` by day (last 7d) → `[{day, tokens, energy, total}]`, and a per-`api`/model breakdown for 30d. Expose in the snapshot under `cost.series7d` and `cost.byModel`.
+2. In `public/index.html`, add a "Cost trend · 7d" card rendering a **pure-SVG line/area chart** (no libraries) of daily total cost, and a small per-model bar list. Match the existing dark theme + var names.
+3. Keep it read-only. Verify: boot on PORT 9773, `curl -u admin:'DW2024!' /api/cost` shows `series7d` with 7 buckets. Commit `feat: 7d cost trend + per-model breakdown`.
+
+Do NOT deploy. Local only.
diff --git a/build-queue/tasks/02-breakeven-target.md b/build-queue/tasks/02-breakeven-target.md
new file mode 100644
index 0000000..959f5ee
--- /dev/null
+++ b/build-queue/tasks/02-breakeven-target.md
@@ -0,0 +1,9 @@
+# AbramsEgo build task 02 — energy attribution + break-even target
+
+Make the self-funding P&L sharper (~/Projects/AbramsEgo):
+
+1. `server.js`: add env `ENERGY_ATTRIB_PCT` (default 100) so energy cost can be attributed as a fraction of machine draw to AbramsEgo/Claude; apply it in `energyCost()`. Add a computed **break-even target**: `pnl.breakEvenUsdPerDay` = today's cost, and `pnl.breakEvenUsdPerMonth`. Add `pnl.runwayNote` string.
+2. `public/index.html`: in the P&L card, show a "Break-even: $X/day · $Y/mo" line and a progress bar of `selfFundingPct` toward 100%. Color: bad<50, warn<100, good>=100.
+3. Read-only. Verify boot + `curl -u admin:'DW2024!' /api/pnl` shows `breakEvenUsdPerDay`. Commit `feat: energy attribution + break-even target in P&L`.
+
+Do NOT deploy. Local only.
diff --git a/build-queue/tasks/03-revenue-plumbing.md b/build-queue/tasks/03-revenue-plumbing.md
new file mode 100644
index 0000000..dbdff36
--- /dev/null
+++ b/build-queue/tasks/03-revenue-plumbing.md
@@ -0,0 +1,20 @@
+# AbramsEgo build task 03 — revenue-engine plumbing (gated go-live drafted)
+
+Build the SAFE plumbing for the 4 revenue engines in ~/Projects/AbramsEgo. Do NOT
+wire any real money mechanism — those are gated.
+
+1. `server.js`: add an authed `POST /api/revenue/record` that appends a dated income
+   entry `{engine, amount, source, ts}` to `data/revenue-ledger.jsonl`, and make
+   `collectRevenue()` sum per-engine amounts from that ledger into the snapshot so
+   windows (today/week/month) reflect real dated income (update `buildPnL` to use
+   dated revenue per window instead of the flat total).
+2. `public/index.html`: for each engine card add a "turn-on checklist" tooltip and a
+   status pill that reads from `revenue.json` (`gated`/`live`).
+3. For EACH engine, DRAFT a go-live memo to `~/.claude/yolo-queue/pending-approval/`
+   describing the exact gated steps (Stripe product+price / affiliate signup+placement /
+   billing rail / ad-network account), each with an officer APPROVE/REVISE/BLOCK block.
+   Do NOT execute any of them.
+4. Verify boot + posting a test income row moves `pnl.today.revenue` > 0. Commit
+   `feat: revenue ledger plumbing + gated engine go-live drafts`.
+
+Local only. No deploy, no keys, no publish.
diff --git a/scripts/build-loop.sh b/scripts/build-loop.sh
new file mode 100755
index 0000000..f63aaa4
--- /dev/null
+++ b/scripts/build-loop.sh
@@ -0,0 +1,58 @@
+#!/usr/bin/env bash
+# AbramsEgo build loop — isolated, restart-forever autonomous builder.
+#
+# Mirrors the proven ~/.claude/yolo-queue/loop.sh contract but on a DEDICATED
+# queue (this repo's build-queue/) so it never fights the DW catalog loop:
+#   - picks the lowest-numbered .md task from build-queue/tasks/
+#   - runs it through `claude --print --permission-mode acceptEdits` (Opus)
+#   - archives to build-queue/done/ on success, build-queue/failed/ on error
+#   - exits 75 (non-zero) when MORE tasks remain  -> launchd KeepAlive respawns
+#     immediately ("if it stops, always restart right away")
+#   - exits 0 (clean) when the queue is EMPTY or STOP is set -> launchd rests
+#   - refuses to start if DISABLED marker present (durable off switch)
+#
+# Halt:  touch build-queue/STOP      (graceful, one-shot)
+# Off:   touch build-queue/DISABLED  (durable; rm to re-enable)
+set -u
+
+ROOT="$(cd "$(dirname "$0")/.." && pwd)"
+Q="$ROOT/build-queue"
+CLAUDE_BIN="${CLAUDE_BIN:-$HOME/.local/bin/claude}"
+MODEL="${ABRAMSEGO_MODEL:-claude-opus-4-8}"
+MAX_TURNS="${MAX_TURNS:-40}"
+PER_TASK_TIMEOUT="${PER_TASK_TIMEOUT:-900}"   # 15 min hard budget per task
+COOLDOWN="${COOLDOWN:-20}"
+mkdir -p "$Q"/{tasks,done,failed,logs}
+LOG="$Q/loop.log"
+ts(){ date '+%F %T'; }
+
+[ -f "$Q/DISABLED" ] && { echo "[$(ts)] DISABLED — refusing to start. rm $Q/DISABLED to re-enable." | tee -a "$LOG"; exit 0; }
+[ -f "$Q/STOP" ]     && { echo "[$(ts)] STOP present — graceful halt." | tee -a "$LOG"; exit 0; }
+
+# singleton (mkdir lock)
+LOCK="$Q/.loop.lock"
+if ! mkdir "$LOCK" 2>/dev/null; then
+  OLD=$(cat "$LOCK/pid" 2>/dev/null)
+  if [ -n "$OLD" ] && kill -0 "$OLD" 2>/dev/null; then echo "[$(ts)] another loop live ($OLD) — exit." | tee -a "$LOG"; exit 0; fi
+  rm -rf "$LOCK"; mkdir "$LOCK" 2>/dev/null || exit 0
+fi
+echo $$ > "$LOCK/pid"; trap 'rm -rf "$LOCK"' EXIT INT TERM
+
+TASK=$(ls "$Q"/tasks/*.md 2>/dev/null | sort | head -1)
+if [ -z "${TASK:-}" ]; then echo "[$(ts)] queue empty — resting (exit 0)." | tee -a "$LOG"; exit 0; fi
+
+name=$(basename "$TASK")
+echo "[$(ts)] START $name" | tee -a "$LOG"
+PREFIX="You are the AbramsEgo autonomous builder working in $ROOT. Use /dtd to DECIDE any judgment call. Do only SAFE, REVERSIBLE work: edit code, add features/tests, commit with git (author steve@designerwallcoverings.com). NEVER run a GATED action (deploy to Kamatera, DNS, domain registration, Stripe/pricing/publish, send-to-list, spend, remote push) — instead draft it to ~/.claude/yolo-queue/pending-approval/ with an officer APPROVE/REVISE/BLOCK block. Commit after each success. TASK FOLLOWS:\n\n"
+LOGF="$Q/logs/${name%.md}.$(date +%s).log"
+
+if cat <(printf '%b' "$PREFIX") "$TASK" | timeout "$PER_TASK_TIMEOUT" "$CLAUDE_BIN" --print --permission-mode acceptEdits --max-turns "$MAX_TURNS" --model "$MODEL" >"$LOGF" 2>&1; then
+  mv "$TASK" "$Q/done/$name"; echo "[$(ts)] DONE $name -> $LOGF" | tee -a "$LOG"
+else
+  mv "$TASK" "$Q/failed/$name"; echo "[$(ts)] FAIL $name (rc=$?) -> $LOGF" | tee -a "$LOG"
+fi
+
+sleep "$COOLDOWN"
+# more tasks? exit non-zero so launchd respawns us immediately; else clean rest.
+if ls "$Q"/tasks/*.md >/dev/null 2>&1 && [ ! -f "$Q/STOP" ]; then exit 75; fi
+exit 0

← 04aa741 AbramsEgo v0.1: self-funding agent command center — live col  ·  back to AbramsEgo  ·  build-loop: tell worker the server runs under pm2 (avoid por 194ddb9 →