harness: scratch graduation pipeline (push side + spec) + flarum first test case
- graduation-push.sh: tar+scp scratch -> BEAST graduation-inbox over Tailscale (decoupled from /save, soft-fail if BEAST off). Tested: 241 files -> BEAST. - docs/graduation-pipeline.md: full spec (push -> Ollama triage on BEAST GPU via API -> reviewed sanitize+git-mv). Secrets never enter git; ride the encrypted link to BEAST only. - tmp-promotion-check.sh: rewritten pure-builtin (0.4s) after the per-file grep/fork loop hung /save for 4 min on Windows at ~240 scratch files. Deep triage moves to the pipeline. - forum-post: GRADUATED the canonical flarum poster from scratch -> skills/forum-post/scripts/flarum-post.py (s9e markdown->XML + DB insert machinery), with the hardcoded IX SSH + Flarum DB passwords swapped to vault lookups. First pipeline test case. - Vaulted the Flarum DB cred (services/flarum-community.sops.yaml) + sanitized the two plaintext copies in forum-post.md. - errorlog: logged the WSL-stub correction + BEAST-Ollama-CPU(vram=0) finding + the promotion-check hang, all via the new log helper. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
55
.claude/scripts/graduation-push.sh
Normal file
55
.claude/scripts/graduation-push.sh
Normal file
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env bash
|
||||
# graduation-push.sh — ship this machine's scratch (tmp/, temp/, .claude/tmp/) to BEAST's
|
||||
# graduation inbox over Tailscale+SSH, for async Ollama triage. Part of the scratch
|
||||
# graduation pipeline (see .claude/TEMP_GRADUATION.md / graduation-pipeline spec).
|
||||
#
|
||||
# DECOUPLED from /save and SOFT-FAIL: if BEAST is off/unreachable it warns and exits 0 —
|
||||
# it must never block a save or a commit.
|
||||
#
|
||||
# Transport: tar the scratch -> scp ONE tarball to guru@<beast>:graduation-inbox/<machine>/.
|
||||
# Secrets in scratch (e.g. hardcoded creds) ride the WireGuard-encrypted Tailscale/SSH link
|
||||
# and land ONLY on BEAST (a trusted fleet box) — they never enter git. The graduation step
|
||||
# sanitizes secrets (-> vault lookups) before anything is committed to a permanent home.
|
||||
#
|
||||
# BEAST: guru@100.101.122.4 (key auth as `guru`), default SSH shell = cmd.exe, home C:\Users\guru.
|
||||
set -u
|
||||
ROOT="${CLAUDETOOLS_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}"
|
||||
SSH="/c/Windows/System32/OpenSSH/ssh.exe"; [ -x "$SSH" ] || SSH="ssh"
|
||||
SCP="/c/Windows/System32/OpenSSH/scp.exe"; [ -x "$SCP" ] || SCP="scp"
|
||||
BEAST="guru@100.101.122.4"
|
||||
CTO=8
|
||||
|
||||
MACHINE="$(jq -r '.machine_name // .hostname // empty' "$ROOT/.claude/identity.json" 2>/dev/null)"
|
||||
[ -z "$MACHINE" ] && MACHINE="$(hostname)"
|
||||
STAMP="$(date -u +%Y%m%dT%H%M%SZ)"
|
||||
|
||||
# Collect existing scratch dirs + count files.
|
||||
DIRS=(); for d in tmp temp .claude/tmp; do [ -d "$ROOT/$d" ] && DIRS+=("$d"); done
|
||||
[ "${#DIRS[@]}" -eq 0 ] && { echo "[INFO] graduation-push: no scratch dirs — nothing to send"; exit 0; }
|
||||
N=$(find "${DIRS[@]/#/$ROOT/}" -type f ! -name '.gitkeep' ! -name '.grad-*.tgz' 2>/dev/null | wc -l | tr -d ' ')
|
||||
[ "${N:-0}" -eq 0 ] && { echo "[INFO] graduation-push: scratch empty — nothing to send"; exit 0; }
|
||||
|
||||
# Reachability gate (fast, BatchMode so it can't prompt).
|
||||
if ! "$SSH" -o BatchMode=yes -o ConnectTimeout=$CTO "$BEAST" "exit 0" >/dev/null 2>&1; then
|
||||
echo "[WARN] graduation-push: BEAST ($BEAST) unreachable — skipped; scratch stays local" >&2
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Tar to an OS-temp path OUTSIDE the scratch dirs (avoid taring our own tarball).
|
||||
TARBALL="$(mktemp -t gradpush-XXXXXX 2>/dev/null).tgz"; [ -z "$TARBALL" ] && TARBALL="/tmp/gradpush-$STAMP.tgz"
|
||||
if ! tar -czf "$TARBALL" -C "$ROOT" "${DIRS[@]}" 2>/dev/null; then
|
||||
echo "[WARN] graduation-push: tar failed" >&2; rm -f "$TARBALL" 2>/dev/null; exit 0
|
||||
fi
|
||||
|
||||
# Ensure remote per-machine inbox (cmd.exe: mkdir makes intermediate dirs; 2>nul ignores 'exists').
|
||||
"$SSH" -o ConnectTimeout=$CTO "$BEAST" "mkdir graduation-inbox\\$MACHINE 2>nul & exit 0" >/dev/null 2>&1
|
||||
|
||||
REMOTE="graduation-inbox/$MACHINE/scratch-$STAMP.tgz"
|
||||
if "$SCP" -o ConnectTimeout=$CTO -q "$TARBALL" "$BEAST:$REMOTE" 2>/dev/null; then
|
||||
echo "[OK] graduation-push: sent $N scratch file(s) -> BEAST:$REMOTE"
|
||||
else
|
||||
echo "[WARN] graduation-push: scp to BEAST failed" >&2
|
||||
bash "$ROOT/.claude/scripts/log-skill-error.sh" "graduation-push" "scp of scratch tarball to BEAST failed (machine=$MACHINE)" >/dev/null 2>&1
|
||||
fi
|
||||
rm -f "$TARBALL" 2>/dev/null
|
||||
exit 0
|
||||
@@ -27,40 +27,17 @@ mapfile -t FILES < <(
|
||||
echo "[INFO] Promotion check: ${#FILES[@]} file(s) in scratch dirs (gitignored — NOT committed)."
|
||||
echo " Graduate anything worth keeping before it's lost. Guide: .claude/TEMP_GRADUATION.md"
|
||||
|
||||
# PURE-BUILTIN loop (no per-file subprocesses) — `basename`/`wc` forks ×N hung this for
|
||||
# 20s+ on Windows Git-bash at ~240 scratch files (fork is expensive). Flag scripts by
|
||||
# extension only; deep triage (doc size, "is it referenced", what's it for) is deferred to
|
||||
# the async Ollama graduation pass (see TEMP_GRADUATION.md). Keep this O(N) and fork-free.
|
||||
candidates=0
|
||||
for f in "${FILES[@]}"; do
|
||||
base="$(basename "$f")"
|
||||
reason=""
|
||||
|
||||
# Script-like files: reusable automation worth a permanent home.
|
||||
case "$base" in
|
||||
*.py|*.sh|*.ps1|*.psm1|*.js|*.rb|*.pl) reason="script" ;;
|
||||
case "${f##*/}" in
|
||||
*.py|*.sh|*.ps1|*.psm1|*.js|*.rb|*.pl|*.php)
|
||||
echo " [GRADUATE?] $f (script)"
|
||||
candidates=$((candidates + 1)) ;;
|
||||
esac
|
||||
|
||||
# Substantial docs (audit reports, dossiers) — size threshold ~4 KB.
|
||||
if [ -z "$reason" ]; then
|
||||
case "$base" in
|
||||
*.md|*.csv)
|
||||
sz=$(wc -c < "$f" 2>/dev/null || echo 0)
|
||||
[ "${sz:-0}" -ge 4096 ] && reason="doc ($((sz/1024))KB)"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Referenced in a session log / doc → clearly load-bearing.
|
||||
# Scope to markdown only and skip heavy build/dep/vcs trees — a bare `grep -r`
|
||||
# over projects/ (Rust target/, node_modules/, .git) hangs for minutes per file.
|
||||
if grep -rqlF "$f" --include='*.md' \
|
||||
--exclude-dir=.git --exclude-dir=node_modules --exclude-dir=target \
|
||||
--exclude-dir=dist --exclude-dir=build --exclude-dir=.next --exclude-dir=vendor \
|
||||
session-logs/ clients/ projects/ 2>/dev/null; then
|
||||
reason="${reason:+$reason, }referenced"
|
||||
fi
|
||||
|
||||
if [ -n "$reason" ]; then
|
||||
echo " [GRADUATE?] $f ($reason)"
|
||||
candidates=$((candidates + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$candidates" -eq 0 ]; then
|
||||
|
||||
Reference in New Issue
Block a user