Retention: model_snapshots live + base64 SSH key support
RETENTION (Phase 2, priority zero). History starts compounding tonight. migration 025 model_snapshots — APPLIED to prod. Append-only, one row per graded prop PER SIDE PER CYCLE, with a unique index on (snapshot_id, player_key, stat, line, side) so a retried cycle cannot duplicate. RLS on, service-role writes only. What it captures that the ledger never did: - features jsonb — the model's INPUTS. Without these a backtest can only grade our own homework; with them any future model can be replayed against the exact conditions this one faced. - REFUSALS (refused + refusal_reason). The ledger drops them, so a gate refusing props that would have WON is invisible — unmeasurable lost edge. Captured via a new onGraded hook in gradeSlateService that fires with BOTH sides before any filtering. - grade_11, the pre-collapse grade. The 4-letter map throws away the entire live C-/C/C+/B- range. - model_version + code_sha on every row. ledger_entries mixes pre/post-fix grades with no marker and cannot be separated retroactively. - p_win / ev_pct / fair_odds / takeable / value — none of which any permanent store held. Wiring: analyzeViaEngine1 attaches _features/_grade_11 (underscore = internal); gradeSlateService fires onGraded then STRIPS them so they never reach a cache or API payload; snapshotService builds rows and persists best-effort. Retention reuses the LEDGER's dateET/gameIdFor helpers so rows share the ledger's natural key exactly — otherwise the settle pass could never join outcomes onto them. Rows are written BEFORE the empty- slate early return: a slate that refused everything is exactly the case worth recording. CONTRACT HELD: retention is injectable and every path is caught. persist() returns errors, never throws; a missing Supabase client is SKIPPED, not an error. A retention failure can never break a snapshot. BACKUP: backup-db.sh now accepts BACKUP_SSH_KEY as base64 (recommended — survives env-var newline mangling, which is how injected SSH keys usually break silently) OR raw PEM, detected by decoding and looking for the PEM header. Verified both forms detect correctly against a real generated key. Suite 279/3325 green, build exit 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SmNjJAwEnqHPtXbvSZR8kA
This commit is contained in:
+17
-2
@@ -76,8 +76,23 @@ RSYNC_SSH="ssh -p ${BACKUP_SSH_PORT:-23} -o StrictHostKeyChecking=accept-new -o
|
||||
if [ -n "${BACKUP_SSH_KEY:-}" ]; then
|
||||
SSH_KEY_FILE="$(mktemp)"
|
||||
chmod 600 "${SSH_KEY_FILE}"
|
||||
# Accept the key with literal \n escapes (how env vars usually carry it).
|
||||
printf '%b\n' "${BACKUP_SSH_KEY}" | sed -e 's/[[:space:]]*$//' > "${SSH_KEY_FILE}"
|
||||
# Accept EITHER form (Session 64):
|
||||
# 1. base64 (recommended — `base64 -w0`; survives any env-var mangling of
|
||||
# newlines, which is the usual way an injected SSH key silently breaks)
|
||||
# 2. raw PEM with literal \n escapes
|
||||
# Detect base64 by trying to decode and checking for the PEM header.
|
||||
DECODED="$(printf '%s' "${BACKUP_SSH_KEY}" | base64 -d 2>/dev/null || true)"
|
||||
case "${DECODED}" in
|
||||
*"PRIVATE KEY"*)
|
||||
printf '%s\n' "${DECODED}" > "${SSH_KEY_FILE}"
|
||||
echo "ssh key: base64-decoded"
|
||||
;;
|
||||
*)
|
||||
printf '%b\n' "${BACKUP_SSH_KEY}" | sed -e 's/[[:space:]]*$//' > "${SSH_KEY_FILE}"
|
||||
echo "ssh key: used raw (not base64)"
|
||||
;;
|
||||
esac
|
||||
chmod 600 "${SSH_KEY_FILE}"
|
||||
RSYNC_SSH="${RSYNC_SSH} -i ${SSH_KEY_FILE}"
|
||||
fi
|
||||
|
||||
|
||||
Reference in New Issue
Block a user