echo6-docs/engine/sweep.sh

226 lines
7.4 KiB
Bash
Raw Normal View History

#!/usr/bin/env bash
# sweep.sh — Echo6 Vault Engine orchestrator
#
# Usage:
# ./sweep.sh # default: process docs changed since last sweep
# ./sweep.sh --all # process every non-archived vault doc
# ./sweep.sh --dry-run # pass --dry-run to agent.py (no writes)
# ./sweep.sh --all --dry-run # both
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
VAULT_DIR="${SCRIPT_DIR}/../vault"
CONFIG="${SCRIPT_DIR}/config.yaml"
LAST_SWEEP="${SCRIPT_DIR}/.last-sweep"
CHANGELOG="${SCRIPT_DIR}/changelog.md"
LINT_REPORT="${SCRIPT_DIR}/lint-report.md"
# Parse args
OPT_ALL=0
OPT_DRY=0
for arg in "$@"; do
case "$arg" in
--all) OPT_ALL=1 ;;
--dry-run) OPT_DRY=1 ;;
*)
echo "[error] Unknown argument: $arg" >&2
echo "Usage: $0 [--all] [--dry-run]" >&2
exit 1
;;
esac
done
SWEEP_START="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
echo "==> Echo6 vault sweep — ${SWEEP_START}"
if [ "$OPT_DRY" -eq 1 ]; then
echo " (DRY-RUN mode — no files will be modified)"
fi
# ---------------------------------------------------------------------------
# Step 1 — GPU-busy guard
# ---------------------------------------------------------------------------
echo ""
echo "[1/4] GPU-busy guard..."
# Helper: run nvidia-smi, using docker exec ollama if host lacks it
_nvidia_smi() {
if command -v nvidia-smi &>/dev/null; then
nvidia-smi "$@" 2>/dev/null
elif command -v docker &>/dev/null && docker ps --format '{{.Names}}' 2>/dev/null | grep -q '^ollama$'; then
docker exec ollama nvidia-smi "$@" 2>/dev/null
else
return 1
fi
}
GPU_DEFERRED=0
DEFER_REASON=""
# Guard 1 — competing GPU processes (share the A4000)
# Note: peertube-runner is intentionally excluded — it's a persistent server
# daemon that's always running, so its mere presence doesn't mean the GPU is
# busy (only an active transcode job does, and that spawns ffmpeg, which is
# still caught here; actual peertube contention is caught by the util/VRAM
# guards below).
if pgrep -fa 'whisper|ffmpeg' >/dev/null 2>&1; then
COMPETING="$(pgrep -fa 'whisper|ffmpeg' | head -1)"
DEFER_REASON="competing GPU process: ${COMPETING}"
GPU_DEFERRED=1
fi
# Guard 2 — sustained GPU utilization > 60 %
if [ "$GPU_DEFERRED" -eq 0 ]; then
GPU_UTIL="$(_nvidia_smi --query-gpu=utilization.gpu --format=csv,noheader,nounits 2>/dev/null | head -1 | tr -d '[:space:]' || true)"
if [ -n "$GPU_UTIL" ] && [[ "$GPU_UTIL" =~ ^[0-9]+$ ]]; then
echo " GPU utilization: ${GPU_UTIL}%"
if [ "$GPU_UTIL" -gt 60 ]; then
DEFER_REASON="GPU utilization ${GPU_UTIL}% > 60%"
GPU_DEFERRED=1
fi
else
echo " nvidia-smi not available or could not read utilization — skipping util guard."
fi
fi
# Guard 3 (safety cap) — very high VRAM usage (>14000 MiB) — not triggered by vault-tagger itself (~8-10 GB)
if [ "$GPU_DEFERRED" -eq 0 ]; then
GPU_USED_MIB="$(_nvidia_smi --query-gpu=memory.used --format=csv,noheader,nounits 2>/dev/null | head -1 | tr -d '[:space:]' || true)"
if [ -n "$GPU_USED_MIB" ] && [[ "$GPU_USED_MIB" =~ ^[0-9]+$ ]]; then
echo " GPU VRAM used: ${GPU_USED_MIB} MiB (safety cap: 14000 MiB)"
if [ "$GPU_USED_MIB" -gt 14000 ]; then
DEFER_REASON="VRAM ${GPU_USED_MIB} MiB > 14000 MiB safety cap"
GPU_DEFERRED=1
fi
fi
fi
if [ "$GPU_DEFERRED" -eq 1 ]; then
echo " GPU busy — deferring sweep. Reason: ${DEFER_REASON}"
if [ "$OPT_DRY" -eq 0 ]; then
printf "\n## %s — sweep deferred (%s)\n" "$SWEEP_START" "$DEFER_REASON" >> "$CHANGELOG"
fi
exit 0
fi
echo " GPU OK — proceeding."
# ---------------------------------------------------------------------------
# Step 2 — Lint
# ---------------------------------------------------------------------------
echo ""
echo "[2/4] Running lint..."
python3 "${SCRIPT_DIR}/lib/lint.py" --report 2>&1 | tee /tmp/sweep-lint.log || {
echo "[warn] lint.py exited non-zero — continuing sweep"
}
echo " Lint complete. Report: ${LINT_REPORT}"
# ---------------------------------------------------------------------------
# Step 3 — Select docs and run agent
# ---------------------------------------------------------------------------
echo ""
echo "[3/4] Selecting documents..."
# Build file list
DOC_LIST=()
if [ "$OPT_ALL" -eq 1 ] || [ ! -f "$LAST_SWEEP" ]; then
if [ "$OPT_ALL" -eq 1 ]; then
echo " Mode: --all (full vault scan)"
else
echo " Mode: no .last-sweep marker found — treating as first run (full scan)"
fi
while IFS= read -r -d f; do
# Skip archive
if [[ "$f" == *"/archive/"* ]]; then
continue
fi
DOC_LIST+=("$f")
auto: docs sync 2026-06-18T18:00:10+00:00 Files changed: .gitignore CLAUDE.md credentials engine/.embcache.json engine/changelog.md engine/config.yaml engine/lib/__pycache__/agent.cpython-312.pyc engine/lib/agent.py engine/lib/lint.py engine/lint-report.md engine/sweep-full.log engine/sweep.sh vault/.obsidian/graph.json vault/.obsidian/workspace.json vault/INDEX.md vault/archive/projects/mmud/last-ember-chronicle.html vault/archive/projects/mmud/last-ember-howto.html vault/archive/projects/mmud/last-ember.html vault/archive/projects/mmud/mmud-phase5-prompt.md vault/archive/projects/mmud/mmud-phase6-prompt.md vault/archive/projects/mmud/mmud-prompts/mmud-prompts/01-update-planned.md vault/archive/projects/mmud/mmud-prompts/mmud-prompts/02-npc-nodes.md vault/archive/projects/mmud/mmud-prompts/mmud-prompts/03-darkcragg.md vault/archive/projects/mmud/mmud-prompts/mmud-prompts/04-dcrg-node.md vault/archive/projects/mmud/mmud-prompts/mmud-prompts/05-phase5.md vault/archive/projects/mmud/mmud-prompts/mmud-prompts/06-phase6.md vault/archive/projects/mmud/mmud-prompts/mmud-prompts/README.md vault/archive/projects/mmud/mmud-prompts/mmud-prompts/mmud-project.md vault/docs/hardware/environment.md vault/docs/hardware/ip-allocation.md vault/docs/matrix/archivist.md vault/docs/matrix/matrix_host.md vault/docs/matrix/mautrix_signal.md vault/docs/matrix/synapse.md vault/docs/matrix/synapse_retention_discovery.md vault/docs/navi/cc-rules.md vault/docs/navi/deployment.md vault/docs/navi/themes.md vault/docs/services/ots-setup.md vault/docs/services/services.md vault/docs/services/usenet.md vault/docs/software/authentik.md vault/docs/software/caddy.md vault/docs/software/dns.md vault/docs/software/geo-tools.md vault/docs/software/recon.md vault/docs/software/searxng.md vault/glossary.md vault/notes/echo6-landing-page-data-export.md vault/notes/ia-download-queue.md vault/plans/vaultwarden-plan.md vault/projects/advbbs-project.md vault/projects/argus.md vault/projects/deploy-livesync.md vault/projects/matrix-synapse-deployment.md vault/projects/meshtastic-headscale-runbook.md vault/projects/mmud-project.md vault/runbooks/add-peertube-channel.md vault/runbooks/authentik-access-groups.md vault/runbooks/authentik-create-invitation.md vault/runbooks/authentik-oidc-application.md vault/runbooks/authentik-upgrade.md vault/runbooks/ct-runbook.md vault/runbooks/edge2-access-reference.md vault/runbooks/expose-service-contabo.md vault/runbooks/expose-service-edge2.md vault/runbooks/expose-service-home.md vault/runbooks/headscale-onboard-node.md vault/runbooks/ia-cli-reference.md vault/runbooks/ia-download-mirror.md vault/runbooks/idahomesh-bridge-setup.md vault/runbooks/idahomesh-vpn-device-setup.md vault/runbooks/lxc-service-migration.md vault/runbooks/mailcow-create-mailbox.md vault/runbooks/meshmonitor-password-reset.md vault/runbooks/meshtastic-sidecar-node.md vault/runbooks/meshtasticd-sim-nodes-runbook.md vault/runbooks/nordvpn-lxc.md vault/runbooks/peertube-remote-runner.md vault/runbooks/pg-backup.md vault/runbooks/pi-nas-omv-runbook.md vault/runbooks/pipeline-patterns.md vault/runbooks/proxmox-create-ubuntu-vm.md vault/runbooks/proxmox-onboard-node.md vault/runbooks/recon-operations.md vault/runbooks/recon-service-integration.md vault/runbooks/syncthing-add-node.md vault/session-resume/SESSION-HANDOFF-meshai-test.md
2026-06-18 18:00:10 +00:00
done < <(find "${VAULT_DIR}" -name "*.md" -type f -print0 | sort -z) # -type f excludes symlinks (e.g. CLAUDE-baseline.md, vault/rules/)
else
LAST_TS="$(cat "$LAST_SWEEP")"
echo " Mode: incremental — docs modified since ${LAST_TS}"
# Compare file mtime to .last-sweep mtime (not contents)
while IFS= read -r -d f; do
if [[ "$f" == *"/archive/"* ]]; then
continue
fi
DOC_LIST+=("$f")
auto: docs sync 2026-06-18T18:00:10+00:00 Files changed: .gitignore CLAUDE.md credentials engine/.embcache.json engine/changelog.md engine/config.yaml engine/lib/__pycache__/agent.cpython-312.pyc engine/lib/agent.py engine/lib/lint.py engine/lint-report.md engine/sweep-full.log engine/sweep.sh vault/.obsidian/graph.json vault/.obsidian/workspace.json vault/INDEX.md vault/archive/projects/mmud/last-ember-chronicle.html vault/archive/projects/mmud/last-ember-howto.html vault/archive/projects/mmud/last-ember.html vault/archive/projects/mmud/mmud-phase5-prompt.md vault/archive/projects/mmud/mmud-phase6-prompt.md vault/archive/projects/mmud/mmud-prompts/mmud-prompts/01-update-planned.md vault/archive/projects/mmud/mmud-prompts/mmud-prompts/02-npc-nodes.md vault/archive/projects/mmud/mmud-prompts/mmud-prompts/03-darkcragg.md vault/archive/projects/mmud/mmud-prompts/mmud-prompts/04-dcrg-node.md vault/archive/projects/mmud/mmud-prompts/mmud-prompts/05-phase5.md vault/archive/projects/mmud/mmud-prompts/mmud-prompts/06-phase6.md vault/archive/projects/mmud/mmud-prompts/mmud-prompts/README.md vault/archive/projects/mmud/mmud-prompts/mmud-prompts/mmud-project.md vault/docs/hardware/environment.md vault/docs/hardware/ip-allocation.md vault/docs/matrix/archivist.md vault/docs/matrix/matrix_host.md vault/docs/matrix/mautrix_signal.md vault/docs/matrix/synapse.md vault/docs/matrix/synapse_retention_discovery.md vault/docs/navi/cc-rules.md vault/docs/navi/deployment.md vault/docs/navi/themes.md vault/docs/services/ots-setup.md vault/docs/services/services.md vault/docs/services/usenet.md vault/docs/software/authentik.md vault/docs/software/caddy.md vault/docs/software/dns.md vault/docs/software/geo-tools.md vault/docs/software/recon.md vault/docs/software/searxng.md vault/glossary.md vault/notes/echo6-landing-page-data-export.md vault/notes/ia-download-queue.md vault/plans/vaultwarden-plan.md vault/projects/advbbs-project.md vault/projects/argus.md vault/projects/deploy-livesync.md vault/projects/matrix-synapse-deployment.md vault/projects/meshtastic-headscale-runbook.md vault/projects/mmud-project.md vault/runbooks/add-peertube-channel.md vault/runbooks/authentik-access-groups.md vault/runbooks/authentik-create-invitation.md vault/runbooks/authentik-oidc-application.md vault/runbooks/authentik-upgrade.md vault/runbooks/ct-runbook.md vault/runbooks/edge2-access-reference.md vault/runbooks/expose-service-contabo.md vault/runbooks/expose-service-edge2.md vault/runbooks/expose-service-home.md vault/runbooks/headscale-onboard-node.md vault/runbooks/ia-cli-reference.md vault/runbooks/ia-download-mirror.md vault/runbooks/idahomesh-bridge-setup.md vault/runbooks/idahomesh-vpn-device-setup.md vault/runbooks/lxc-service-migration.md vault/runbooks/mailcow-create-mailbox.md vault/runbooks/meshmonitor-password-reset.md vault/runbooks/meshtastic-sidecar-node.md vault/runbooks/meshtasticd-sim-nodes-runbook.md vault/runbooks/nordvpn-lxc.md vault/runbooks/peertube-remote-runner.md vault/runbooks/pg-backup.md vault/runbooks/pi-nas-omv-runbook.md vault/runbooks/pipeline-patterns.md vault/runbooks/proxmox-create-ubuntu-vm.md vault/runbooks/proxmox-onboard-node.md vault/runbooks/recon-operations.md vault/runbooks/recon-service-integration.md vault/runbooks/syncthing-add-node.md vault/session-resume/SESSION-HANDOFF-meshai-test.md
2026-06-18 18:00:10 +00:00
done < <(find "${VAULT_DIR}" -name "*.md" -type f -newer "$LAST_SWEEP" -print0 | sort -z) # -type f excludes symlinks
fi
TOTAL="${#DOC_LIST[@]}"
echo " ${TOTAL} document(s) selected."
if [ "$TOTAL" -eq 0 ]; then
echo " Nothing to process — vault is up to date."
if [ "$OPT_DRY" -eq 0 ]; then
date -u +%Y-%m-%dT%H:%M:%SZ > "$LAST_SWEEP"
printf "\n## %s — sweep complete (0 docs selected)\n" "$SWEEP_START" >> "$CHANGELOG"
fi
echo "==> Done."
exit 0
fi
echo ""
echo " Processing..."
N_PROCESSED=0
N_WRITTEN=0
N_FLAGGED=0
N_ERRORS=0
AGENT_DRY_FLAG=""
if [ "$OPT_DRY" -eq 1 ]; then
AGENT_DRY_FLAG="--dry-run"
fi
for doc in "${DOC_LIST[@]}"; do
echo ""
echo " --- $(basename "$doc") ---"
set +e
python3 "${SCRIPT_DIR}/lib/agent.py" $AGENT_DRY_FLAG "$doc" 2>&1
EXIT_CODE=$?
set -e
N_PROCESSED=$(( N_PROCESSED + 1 ))
if [ "$EXIT_CODE" -eq 0 ]; then
N_WRITTEN=$(( N_WRITTEN + 1 ))
else
echo " [warn] agent.py exited ${EXIT_CODE} for ${doc}"
N_ERRORS=$(( N_ERRORS + 1 ))
fi
done
# ---------------------------------------------------------------------------
# Step 4 — Update marker + changelog summary
# ---------------------------------------------------------------------------
echo ""
echo "[4/4] Finalising..."
SWEEP_END="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
if [ "$OPT_DRY" -eq 0 ]; then
date -u +%Y-%m-%dT%H:%M:%SZ > "$LAST_SWEEP"
{
printf "\n## %s — sweep run\n" "$SWEEP_START"
printf "- end: %s\n" "$SWEEP_END"
printf "- mode: %s\n" "$([ "$OPT_ALL" -eq 1 ] && echo "--all" || echo "incremental")"
printf "- docs selected: %d\n" "$TOTAL"
printf "- processed: %d | written: %d | flagged: %d | errors: %d\n" \
"$N_PROCESSED" "$N_WRITTEN" "$N_FLAGGED" "$N_ERRORS"
} >> "$CHANGELOG"
echo " .last-sweep updated. Changelog appended."
else
echo " [DRY-RUN] .last-sweep NOT updated. Changelog NOT written."
fi
echo ""
echo "==> Sweep complete."
echo " Docs selected : ${TOTAL}"
echo " Processed : ${N_PROCESSED}"
echo " Written/OK : ${N_WRITTEN}"
echo " Errors : ${N_ERRORS}"
if [ "$OPT_DRY" -eq 1 ]; then
echo " [DRY-RUN] No vault docs were modified."
fi