chore: daily data inventory refresh (2026-06-11)
Deploy Log
Every deploy across all products, automatically logged.
June 2026
chore: daily data inventory refresh (2026-06-10)
chore: daily data inventory refresh (2026-06-09)
chore: daily data inventory refresh (2026-06-08)
chore: daily data inventory refresh (2026-06-07)
chore: daily data inventory refresh (2026-06-06)
chore: daily data inventory refresh (2026-06-05)
chore: daily data inventory refresh (2026-06-04)
chore: daily data inventory refresh (2026-06-03)
chore: daily data inventory refresh (2026-06-02)
chore: daily data inventory refresh (2026-06-01)
May 2026
chore: daily data inventory refresh (2026-05-31)
chore: daily data inventory refresh (2026-05-30)
chore: daily data inventory refresh (2026-05-29)
chore: daily data inventory refresh (2026-05-28)
chore: daily data inventory refresh (2026-05-27)
chore: daily data inventory refresh (2026-05-26)
chore: daily data inventory refresh (2026-05-25)
Fix stale source-health labels on experiments dashboard
Phase 0 SEO treatments deployed via additive isolation 2026-05-22 11:55 EDT (Heroku v972 = 795c778 'SEO Phase 0 treatments (additive)'). Verified prod 2026-05-24: titles say 2026, AggregateOffer schema present, /compare returns
Lessons: GDA deploy target Heroku-vs-Coolify check
RFD: GSC verdict math unsafe for sparse CTR
chore: daily data inventory refresh (2026-05-24)
chore: daily data inventory refresh (2026-05-23)
Add 90d/180d time ranges + full period selector on experiments page
Re-anchor exp_gda_001/002 to real prod treatment start (2026-05-22)
Wire ai_referrals validator source to live GA4
Verified prod: GSC read endpoints shipped as additive-only isolation (Heroku v967 = 4ba61f1), deployed ~7h ago. /snapshots returns 2,952 rows, /aggregates works, marketing-bot probe WIRED, all 6 GSC trials
GDA asked whether to deploy the GSC endpoint now (overriding the CTR-readout gate) or hold to 2026-06-03. Verified current prod first: it has ROLLED BACK to master — GSC endpoint 404, draft endpoint 404, no JSON-LD, titles still
chore: daily data inventory refresh (2026-05-22)
Harden OAuth return-redirect against open-redirect token leak
Rules for links in Reddit posts/comments, grounded in the Reddit/AI-search corpus: Reddit links are nofollow+ugc (zero SEO juice) — value is branded search + AI citation + clickstream. One deep UTM'd link at the post bottom,
Make Run-validator button self-service (one-time GitHub authorize)
chore: daily data inventory refresh (2026-05-21)
Charles's "step back" insight: instead of scattered best-X pages, build buyer-journey HUBS ("The New AR-15 Owner", "PCC Owner") that chain the sub-decisions (which rifle/optic/can/light/trigger...) into one page,
Add Run-validator button + daily XmR cron; refresh GSC endpoint spec
The auto-researcher project (~/Documents/auto-researcher/) already built ~70% of this agent — the full research engine, with 4 shipping-ready posts. Its review flagged the build directive + 8 empirically-grounded
Charles challenged whether the earlier "authority first, near-zero new pages for 60 days, prune the dead" guidance matched the SEO corpus. It didn't — two parts had drifted to generic SEO caution:
The two scaling engines for the 1M-users plan, both niche-agnostic (parameterized by a property adapter so they're not gun-specific). Grounded in the full marketing-corpus review (Cody SEO, Reddit/AI-search
Now that GA4 is live (111 AI sessions/30d baseline, 110 ChatGPT), close the measurement loop:
Charles's draft review surfaced GDA-side data defects (a $1,799 Razor HD scope bundled under the $400 UH-1 sight; M855A1 priced by box not per round; SR-15 lower-receiver kits blended into the rifle price). Those live
Every recommendation section now opens with a live price card sourced from GDA's deal tracker (current / 30d-low / deal-count / GDA link). This is GDA's differentiator vs truegunvalue/gun.deals — live tracked
Farm-out spec for the deep research-synthesis Reddit format, templated from Charles's r/MP5 "Hunt for the Perfect Short MP5K Can" post (9.9k views, 42 upvotes, 49 comments). This is the lever the citation baseline
chore: daily data inventory refresh (2026-05-20)
WebSearch proxy for AI citation sets, 2026-05-19. Headline: gundealalerts.com is NOT cited for any sampled price query. truegunvalue.com dominates the "current price/value/trends 2026" intent we targeted; gun.deals (competitor)
Turns "is this singing for AI bots?" from faith into a number. - api/analytics/ai-referrals.js: queries GA4 for sessions sourced from
The repost-corrected script rejected superseded drafts via direct curl to GDA, which bypassed the ledger-logging path — so 8 blended drafts still showed 'draft' on /drafts.html though they were rejected on GDA. Append
Charles caught 6 fanout fact blocks that blended incompatible SKUs into one price range: complete rifles with lowers/parts (KAC SR-15 "$1,060-$3,000"), new with refurbished (Vortex AMG), optics-ready with
The original fanout threshold (>50 chars, >=10 impressions, 0 clicks) only caught 1 query. Real GDA GSC data analysis showed the fanout space is much larger at a looser threshold.
The agent now logs every successful draft POST to learnings/drafts.jsonl, and the new /drafts.html page lists pending drafts with one-click Approve/Reject backed by GDA's existing /api/internal/drafts/<id>/{approve,reject}
Two GitHub Actions workflows that close the autonomous loop: 1. gsc-snapshot-cron.yml — daily 06:00 UTC
Merges parallel work from ~/Documents/marketing_bot/ working tree into the session, then adds the marketing-bot READ side that lights up automatically when GDA ships the GSC snapshot endpoint.
Chart.js with responsive+maintainAspectRatio:false needs a positioned, fixed-height parent to size against. Without it, canvas inherits the parent's auto height, Chart.js resizes the canvas, parent grows, canvas
The original signals (sessions, users, signups) had no data in GDA's snapshots collection. Confirmed via direct snapshot query: signups: 0 points
End-to-end cross-property write loop is now real: marketing-bot's gda/gsc-refresh agent can POST drafts to GDA's new /api/internal/drafts endpoint. Smoke-tested against production — draft drf_o9tr6xaw
12 consolidations merge true duplicates across files while preserving every distinct insight. Added new Theme 6 (Feed & Food Sources) from the new feed-and-food-sources skill. 32K words, ~1,900 lines.
- New L4 skill: mule-deer-multi-year-unit-mastery (Expert tier capstone with 5-year compounding curve framework) - New L2 skill: mule-deer-trail-camera-strategy (water seeps, mineral
The app was recreated; old webhook UUID returned 404 silently breaking auto-deploy on push. Confirmed new UUID via Coolify API: Application marketing-bot-img -> i19tphkv2ylq7pulqg6vfvgz
- New L1 skill: mule-deer-terrain-features-and-tactics — 58KB reference defining water/saddle/cover/rim-rock/benches/burns/basin/drainages with deer use patterns, mechanism, and hunting tactics for each.
pnpm 11 turned ignored build scripts into a fatal install error, which broke the Docker build (corepack pulled latest). Pin to the version that's been working and explicitly approve the three
Includes all third-wave additions (Denning, Holden, Dioni) and the deepening edits to existing files. ~30K words, organized for truck-the-night-before navigation with a re-ranked Make-or-Break
The Link>Card pattern produced an inline <a> wrapping a block <div>, which is invalid HTML — left-click navigation broke silently on some browsers (right-click "open in new tab" still worked because the
- New domains/mule-deer/ (39 skills, 185 edges, 7 categories: behavior, e-scouting, terrain, glassing, stalking, public-land, shot-craft) - Hunting domain renamed to "Coyote & Predator Hunting" (67 skills)
- 39 mule deer skill files across 7 categories (behavior, e-scouting, terrain, glassing, stalking, public-land, shot-craft) sourced from ~60 transcripts: Hartsky, Brady Miller, Robby Denning, Marlon Holden,
chore: daily data inventory refresh (2026-05-19)
chore: daily data inventory refresh (2026-05-18)
chore: daily data inventory refresh (2026-05-17)
chore: daily data inventory refresh (2026-05-16)
chore: daily data inventory refresh (2026-05-15)
chore: daily data inventory refresh (2026-05-14)
chore: daily data inventory refresh (2026-05-13)
chore: daily data inventory refresh (2026-05-12)
chore: daily data inventory refresh (2026-05-11)
chore: daily data inventory refresh (2026-05-10)
chore: daily data inventory refresh (2026-05-09)
chore: daily data inventory refresh (2026-05-08)
chore: daily data inventory refresh (2026-05-07)
chore: daily data inventory refresh (2026-05-06)
chore: daily data inventory refresh (2026-05-05)
chore: daily data inventory refresh (2026-05-04)
chore: daily data inventory refresh (2026-05-03)
chore: daily data inventory refresh (2026-05-02)
chore: daily data inventory refresh (2026-05-01)
Resolve inventory merge conflicts
April 2026
chore: daily data inventory refresh (2026-04-29)
chore: daily data inventory refresh (2026-04-28)
Goal extractor: - Pull <command-args> body when the first message is a slash command (was capturing the framework caveat instead of the user's prompt).
The key fallback in claudeops_supabase.py was the pre-rotation key (iat: 1774551995), which silently 401'd against self-hosted Supabase since the key rotation on 2026-03-30. Hook sync had been broken for
Lives at /conductor on the existing dashboard. One row per session: project, status, goal (first user prompt verbatim, with on-demand Haiku 4.5 summarize), file/commit/push counts, resolved PR URLs (via gh CLI), and a click-through
chore: daily data inventory refresh (2026-04-27)
chore: daily data inventory refresh (2026-04-26)
chore: daily data inventory refresh (2026-04-25)
chore: daily data inventory refresh (2026-04-24)
Walk-forward backtest across 26 leagues, 4 treatments (xg=0, 0.05, 0.1, 0.2). xgWeight=0.2 beats control: OVERS +3.99pp, UNDERS +3.31pp, SIDES +1.18pp CLV. Effect is monotonic with no plateau. AH flat (~9.7% CLV across all treatments).
Copy-pasteable prompt for dropping into sessions that need to check the data inventory before answering data questions. Self-contained (works without the SessionStart hook), lists all 24 sources, names the
Signal count grew from 11→17 (2^17=131K combos), causing 5,958 combos per chunk × 6K bets × betKeys/profits arrays to exceed 8GB heap.
Post-mortem on the variance-lookback-short-symmetric rejection (registered + tested + rejected 2026-04-17). Key points recorded for future-me:
Pre-registered 2026-04-17 to test whether lowering VARIANCE_LOOKBACK (10 → 5/6/7/8) would preserve CLV while enabling coverage for early- season bets that can't hit 10-match history. Tested via
On-demand endpoint (no schedule) to compute league-specific venue calibration for v3 Sofascore xG. Runs on main server where sofascore_xg and derived v3 cache both live. Trigger manually with CRON_SECRET when needed.
Adds --xg-weight passthrough in job-registry and compute-worker so factorial jobs can run against xG solver variant caches.
HFA signal was null for 9 leagues because DEFAULT_LEAGUES in hfa-monitor.ts was a 19-league whitelist. getCurrentHFA() gated on this before even checking files — so bets in ucl, mls, austria-bl, arg-b-nacional,
7 additional seasons of Marcel 2-1-1-5 projections from Understat data. Combined with TM availability backfill (in progress), this gives the marcel-injury-impact signal 10 seasons of coverage instead of 2.
Inventory audit found that fotmob-match-xg already ships 269 files covering 50 leagues, including the 4 leagues I'd previously marked as "at data ceiling" for FF(xG)/Context/DynXG:
- Add marcelInjuryImpactEnabled config to EvalConfig with threshold (15%) and dampening (50%) - Compute Marcel xG missing fraction in data-loader from TM availability + Marcel projections - Lambda adjustment: reduce attack lambda when >15% of projected xG is missing
- Generate Marcel 2-1-1-5 projections for 2024/2025/2026 seasons - 2,412 players across Big 5 leagues (2,045 with age data) - Validation: +17.1% RMSE improvement over naive (npxG/90)
Round 8 left the Bundesliga Squad/GK coverage at 9/11 because Hamburg and Augsburg weren't in reep/team-bridge at all — resolveTeamTmId couldn't hop from canonical name to TM ID for either, even though the
- Extract core compute logic into lib/sofascore-shot-xg.ts (shared by script + cron) - Add /api/cron/compute-shot-xg endpoint (09:00 UTC daily, after accumulate-xg) - Cron entry added to Hetzner server
After rounds 6 + 7 unlocked Big-5 squad-history data, coverage jumped to 93/182 but still missed 9 Championship bets. Not a reader bug — the new resolveTeamTmId handles all the fuzzy name variants. It was estimateRound
The first pass of the scrape produced full roster data but empty availability matrices for 10 La Liga + 17 Ligue 1 + 3 Bundesliga teams (matchdayCount: 0, availability: []). Manual re-test of Monaco showed
The inventory discipline check surfaced that `data/availability/raw/` is an un-registered orphan directory — the Championship signal coverage has been running off 24 hand-placed files in a dir that `generate-data-inventory.ts`
Second retro today. The morning one was about overclaiming without measurement. This afternoon's is about reaching for parallelism before verifying unit cost matches the spec.
Swaps MODEL_PATH from xg_pre_shot_v1_compact.json to xg_universal_v3_compact.json. v3 model trained on FotMob+Understat combined data (211K shots), properly calibrated with base_logit=-2.195 (goal rate 0.098). v1 was miscalibrated on Sofascore coords
One-variable change: flip leagueExclude from ["segunda", "la-liga", "ligue-2"] to [] in DEFAULT_EVAL_CONFIG at lib/backtest/bet-evaluator.ts:294.
2026-04-15 session: observed rate of ~240 snapshots/hour across 8 parallel workers on cloud-lab. Spec said 5 seconds per solve; actual was 60-200s. Two causes identified from the actual log:
So that design specs and decision records committed alongside inventory changes (like docs/specs/inventory-gate-hook.md from the previous commit) are auto-staged by data:ship and don't need a separate follow-up commit.
Problem: The existing SessionStart hook printed only a source count summary. Sessions still grepped the codebase to find data sources instead of reading the inventory, because the inventory itself wasn't in their initial context —
First full audit after the sofascore swap turned up SP-Mismatch misses for Club Brugge v Anderlecht, Gent v Mechelen (belgian-pro). Investigation: the sofascore DB key `belgianPro` contains SWEDISH ALLSVENSKAN data, not
Round-3 audit left 5 signals below ceiling because their source data (understat-shots) was Big-5 only and frozen at 2024. The inventory review turned up sofascore_shots in Supabase with 368k shots across
Honest post-incident retro of yesterday's session. The proximate bugs (stale fire script, no state seed, no rsync-on-failure) are real and fixed in 1616a76c. The deeper issue is process: I treated job.status
2026-04-15 retro: cloud-lab was silently running Apr-7 code (88 commits behind origin/main) for 8 days because the fire script's `git pull --ff-only` was aborting on every single job ("Please commit your changes or stash them
Walks 24 sources (was 19). Plus two generator improvements: 1. Walker now handles single-file paths (for data/signal-registry.json which
- Add enrichment?: number to Multipliers (default 1.0, pass-through) - Include enrichmentMultiplier in SizingResult breakdown - Log enrichmentMultiplier on every PaperBet sizing record
chore: daily data inventory refresh (2026-04-15)
Inventory robustness: validation chokepoint + data:ship + pre-commit hook + marcel/player-finishing sources
0deb97aThe other agent shipped broken inventory state last session because the system relied on discipline ("run the tests before committing") instead of enforcement. This commit adds three layers that make broken state structurally
Real proof the getOrCompute() pipeline works end-to-end on production data: - Inputs: sofascore_shots (156,794 rows in Supabase) + xg_pre_shot_v1_compact.json
compute-sofascore-shot-xg: drop per-shot from cached output (23MB → ~300KB)
Three changes: 1. CLAUDE.md gains a "How to use the inventory — the workflow" subsection with
Results: - 6-config sweep: all positive sizing lift (+0.45% to +0.63%) - Walk-forward: 3/3 OOS folds positive (+0.52% to +12.58%)
Red-team battery (2026-04-14) found two guardian bypasses in the submit endpoint:
chore: daily data inventory refresh (2026-04-14)
Several data/ subdirs are gitignored (footystats-matches, betexplorer-odds, squad-history/raw, etc.) because they're huge local caches. The previous generator walked them naively, so the committed INVENTORY.json's coverage
Adds a version-controlled priority task list at data/cloud-lab/roadmap.json that any cloud-lab submission must match. Sessions that try to queue unapproved work get a 403 with a link back to docs/specs/cloud-lab-guardian-spec.md.
Two UX issues surfaced: 1. The expanded comparison table (click any signal row) sorted rows by
Round-2 showed AggV2 flat at 96/181 despite adding the `*2025.json` files. Reason: for calendar-year leagues (arg-primera, brazil-a, mls), the bets we're scoring are from April 2026 — the 2025 files only contain matches
Second pass on signal coverage after the first unblock commit: 1. findTeamInMap now tries canonical→full (miToCanonicalName) too.
Makes the web container's queue.ts handle ONLY cloud-lab jobs and the compute-worker container handle ONLY local jobs. Both read the same data/compute-jobs/jobs.json but filter on job.target so they never pick
After landing the data-unblock commit and re-running the audit: 1. GK change: 0/33 on Championship even though Squad strength works 33/33
The coverage audit on the first fresh 181-bet shadow-backfill found five signals at 0% coverage — Squad, GK change, AggV2, SP-xGA regression, and SP-mismatch — plus ShotXG at 34% and HFA at 78%. Root cause was not the
Replaces the hardcoded N≥1000, +0.5pp practical-significance floor, p<0.10 bootstrap threshold, 3pp matchday-interleave tolerance, and 1% suspicious-N dedup heuristic with data-driven equivalents:
Phase 0 (cloud-lab VM): fix LAST=0 idle-check bug that was killing the VM 5 min after every boot. /root/auto-shutdown.sh and /root/watchdog.sh now handle missing /tmp/last-job-activity explicitly (initialize + exit 0)
The 2026-04-13 /gauntlet freeze had a second half: even after fixing the backfill script to read from Supabase, every redeploy reverted the in-image shadow-backfill.json to whatever snapshot was committed to git (Apr 7, 72
20 signals with positive marginal mined from registry + factorial. Key finds from 2,048-combo factorial: - contextXg: +0.39% main effect (biggest single boost)
Experiments (all validated walk-forward on 2025): - Age-conditional weighting: wins keyPasses/90 only (+0.05%) - Asymmetric 30+: no consistent win
Two gaps surfaced on /track: FS All showed "—" for 17 of 181 settled bets (164/181 coverage). Root causes were both naming-related.
Root cause: scripts/backfill-shadow-signals.ts read data/paper-trade/ledger.json directly from the filesystem. In the container this file is frozen at whatever shipped in the Docker image, while production writes go to Supabase. Result:
Append new section to docs/specs/edge-hunt.md describing how to promote the 11 Bayesian priors hypotheses into the pod-shop factorial search.
Post-mortem on 8 settlement failures from the same root cause. Hardcoded subset vs verified map, symptom-patching loop, architectural fix.
Path C (xG calibration, 136K shots): - H9 player finishing: Brier -0.00040 direction correct but BELOW -0.002 threshold - H10 opponent context: +0.00002 (hurts slightly) REJECTED
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Expanded footystats-to-cache-team-map.json from 588 → 1257 entries covering all 40+ leagues (UCL, Belgian Pro, Brazil, Argentina, Uruguay, Serie C/D, MLS, Liga MX, J-League, Austrian BL, Eerste Divisie, etc.)
Root cause of recurring settlement failures: settler had a hardcoded 100-entry team map while a verified 588-team JSON map existed unused. Every unmapped team silently failed to settle.
Infrastructure: - Phase 0: runWithoutSignal mapping for marcel-early-prior, squad-value-early-season, promoted-team-lambda-adjust, squad-value-regularization signals
FootyStats returns "Angers SCO" but settler expected "Angers". Also added Saint-Étienne, Nîmes, Châteauroux accent mappings.
Details the UUID-vs-integer label filter bug that made 15+ consecutive deploys show red while production ran fine, and the no-op rollback that hid it.
Fitted LOWESS age curves from 789 player-seasons. No-age wins 5/5 stats. Survivorship bias hides decline — 34+ players are +16.5% above average because only elite survive. Key next steps: backfill to 11 seasons,
Auto-publishes deploy info to dev.imprevista.com/blog on successful deploy. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Auto-publishes deploy info to dev.imprevista.com/blog on successful deploy. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Auto-publishes deploy info to dev.imprevista.com/blog on successful deploy. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Auto-publishes deploy info to dev.imprevista.com/blog on successful deploy. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Auto-publishes deploy info to dev.imprevista.com/blog on successful deploy. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Auto-publishes deploy info to dev.imprevista.com/blog on successful deploy. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Auto-publishes deploy info to dev.imprevista.com/blog on successful deploy. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Auto-publishes deploy info to dev.imprevista.com/blog on successful deploy. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Auto-publishes deploy info to dev.imprevista.com/blog on successful deploy. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Auto-publishes deploy info to dev.imprevista.com/blog on successful deploy. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Auto-publishes deploy info to dev.imprevista.com/blog on successful deploy. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Auto-publishes deploy info to dev.imprevista.com/blog on successful deploy. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cross-product deploy changelog deployed at dev.imprevista.com