Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions scripts/dashboard-loop.prompt.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
GraphDone autonomous continuous-improvement iteration. The live test dashboard (http://localhost:3199, `npm run dashboard`) is your guiding light. Repos: Core = /home/scubasonar/Code/graphdone-repos/GraphDone-Core (work on branch dev), Cloud = /home/scubasonar/Code/graphdone-repos/GraphDone-Cloud (branch main). Read the memory index at /home/scubasonar/.claude/projects/-home-scubasonar-Code-graphdone-repos/memory/MEMORY.md first. Do ONE short, safe, well-scoped iteration, then stop and exit.

CONTEXT: The mechanical upkeep (keeping the dashboard server alive + running test suites to feed it fresh data) has ALREADY been done by the shell driver before you were invoked. Your job is the judgment part: look at the data and make at most one concrete improvement.

STEPS:
1. Read the current dashboard state: `curl -s http://localhost:3199/api/state`. Identify the newest runs, any failing/warn checks, and any regression vs prior runs in the metric trends (FPS, pass-rate, durations, drift).
2. If there is a CONCRETE, low-risk, well-scoped fix or improvement (a failing/warn check to fix, a flaky test to stabilize, a clear perf regression, a small dashboard enhancement): implement it. Write or adjust a test first where feasible. Run the relevant gate — when you touch data, the graph, or deletion paths run `TEST_URL=http://localhost:3127 npm run test:smoke` (only if the dev stack at :3127 is up; if it is not up, restrict yourself to changes provable by `node --test` / unit tests and do NOT claim the smoke gate passed).
3. If (and only if) your change is complete and its gate is green, commit on a feature branch and merge to dev via the branch-protection bypass:
gh api -X DELETE repos/GraphDone/GraphDone-Core/branches/dev/protection/required_pull_request_reviews
gh pr merge <n> --merge --delete-branch
gh api -X PATCH repos/GraphDone/GraphDone-Core/branches/dev/protection/required_pull_request_reviews -F required_approving_review_count=1
Use scoped `git add <paths>` (NEVER `git add -A`). End the commit message with:
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4. Record anything durable in memory (one file per fact, update MEMORY.md index).

HARD CONSTRAINTS (never violate):
- NEVER deploy to Cloudflare (do not run scripts/deploy.sh) — deploys are user-triggered only.
- NEVER read, log, expose, or commit secrets (ADMIN_PASSWORD, GUEST_JWT_SECRET, RESEND key); admin login stays secret-backed only; no end-user passwords.
- NEVER weaken auth, tenant isolation, or any security control. Tear down any test fixtures you create.
- NEVER merge anything that fails THE GATE or whose gate you could not actually run.
- If there is nothing safe and concrete to do this iteration, do nothing further and exit cleanly. Do not invent risky or speculative work. Brevity over volume.
63 changes: 63 additions & 0 deletions scripts/dashboard-loop.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/usr/bin/env bash
# Autonomous continuous-improvement loop for GraphDone, driven by cron so it
# survives session limits AND reboots. Each invocation (cron passes "once"):
# 1. ensures the live test dashboard server is up (the guiding light)
# 2. self-checks the dashboard (its own unit tests)
# 3. runs ONE bounded headless `claude -p` improvement iteration — the agent
# decides whether to run a real test suite (which is what feeds the
# dashboard fresh run data); the driver never fabricates synthetic runs.
# Single-instance via flock. Set NO_AGENT=1 to skip step 3 (mechanical only).
#
# bash scripts/dashboard-loop.sh once
set -uo pipefail

CORE="/home/scubasonar/Code/graphdone-repos/GraphDone-Core"
STATE="$CORE/test-artifacts/dashboard"
LOG="$STATE/loop.log"
LOCK="$STATE/loop.lock"
PORT="${DASHBOARD_PORT:-3199}"
AGENT_TIMEOUT="${AGENT_TIMEOUT:-1500}"

# cron runs with a minimal env — put nvm node + ~/.local/bin (claude) on PATH
export PATH="/home/scubasonar/.nvm/versions/node/v20.20.2/bin:/home/scubasonar/.local/bin:$PATH"
export NODE_NO_WARNINGS=1

mkdir -p "$STATE"
log() { echo "[$(date -Is)] $*" >> "$LOG"; }

# single instance: bail if a previous iteration is still running
exec 9>"$LOCK"
if ! flock -n 9; then log "skip: previous iteration still running"; exit 0; fi

cd "$CORE" || { log "fatal: cannot cd $CORE"; exit 1; }
log "── iteration start (pid $$) ──"

health() { curl -s -o /dev/null -w '%{http_code}' "http://localhost:$PORT/api/state" 2>/dev/null; }

# 1) keep the dashboard alive
if [ "$(health)" != "200" ]; then
log "dashboard down → starting"
nohup npm run dashboard >/tmp/gd-dash.log 2>&1 & disown
for _ in 1 2 3 4 5 6; do sleep 1; [ "$(health)" = "200" ] && break; done
fi
log "dashboard health=$(health)"

# 2) self-check the dashboard tool (cheap; does NOT fabricate runs)
if node --test tests/lib/dashboard/ >>"$LOG" 2>&1; then log "dashboard lib tests ok"; else log "dashboard lib tests FAILED"; fi

# 3) one bounded headless agent iteration (skippable)
if [ "${NO_AGENT:-0}" = "1" ]; then
log "NO_AGENT=1 → skipping agent iteration"
elif command -v claude >/dev/null 2>&1 && [ -f "$CORE/scripts/dashboard-loop.prompt.txt" ]; then
log "agent iteration start (timeout ${AGENT_TIMEOUT}s)"
if timeout "$AGENT_TIMEOUT" claude -p "$(cat "$CORE/scripts/dashboard-loop.prompt.txt")" \
--permission-mode bypassPermissions >>"$LOG" 2>&1; then
log "agent iteration done"
else
log "agent iteration ended (rc=$?)"
fi
else
log "no claude CLI or prompt file → mechanical upkeep only"
fi

log "── iteration complete ──"
12 changes: 12 additions & 0 deletions tests/lib/dashboard/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,15 @@ Read-only and **bound to `127.0.0.1`** (no LAN exposure, no auth surface). No
command-execution endpoints. Media is served only from within a run's own report
directory (real-path checked, traversal/symlink-escape rejected) and only for an
allowlist of media extensions; `/static/` serves a fixed three-file allowlist.

## Autonomous loop (`scripts/dashboard-loop.sh`)

So the dashboard keeps living and improving across sessions/reboots, a cron-driven
loop runs `scripts/dashboard-loop.sh once` every ~30 min (system crontab, tagged
`# graphdone-dashboard-loop`). Each iteration: (1) ensures the dashboard server is
up, (2) self-checks it (`node --test tests/lib/dashboard/`), (3) runs ONE bounded
headless `claude -p` improvement iteration against `scripts/dashboard-loop.prompt.txt`.
A `flock` lock prevents overlap; the agent prompt has hard constraints (never
deploy, never touch secrets, never merge a failing gate). Run mechanical-only with
`NO_AGENT=1 bash scripts/dashboard-loop.sh once`. Disable the loop by removing the
`graphdone-dashboard-loop` lines from `crontab -e`. Logs: `test-artifacts/dashboard/loop.log`.
Loading