Evidence record // Machine learning & AI learning
Machine learning and AI learning: 10 inspectable demonstrations across game theory, control, pixel RL, adversarial evaluation, and closed-loop gating
I directed the team (AI coding and research agents across two local research workspaces, PTCG-Learning-Lab and agentlearning) to build, verify, and red-team machine learning and AI learning systems from first principles. Every claim below separates training from evaluation in a fresh process, compares learned weights against untrained and reset baselines on held-out seeds, and preserves negative confirmation gates alongside positive proofs.
01 // Kuhn Poker CFR equilibrium
0.458333 → 0.000938
Exact 64 x 64 best-response exploitability drops 99.80% in 1,000 updates (1.172 s).
02-03 // Atari Pong pixel PPO
-21.00 → +19.25 (20/20)
31,264 optimizer updates over 2,000,896 frames; audited at sticky = 0.0 and sticky = 0.25.
04-06 // 10-seed PPO & 68/68 Fakes Suite
IQM 253.88 // 68/68 caught
2,000 LunarLander episodes with 0.0 replay diff; Mario x = 65,535 byte-wrap caught.
07-08 // Adaptation & 5,600-game TCG gate
+21.25 reuse // ESS 48.06
Pokemon TCG scorer changes 266, 812, and 868 live choices; -0.003125 pp lower CI stops promotion.
Kuhn Poker CFR: discovering deception, hero-calling, and slow-playing without human heuristics
Starting from a uniform 50/50 policy across all 12 information states, tabular Counterfactual Regret Minimization (CFR) reduces exact best-response exploitability from 0.458333 to 0.000938 (-99.80%) in 1,000 updates (1.172 seconds). Without domain rules, CFR converges to the coupled analytical Nash family parameterized by alpha = 0.1940: bluffing Jack after a pass at 33.30% (exact Nash 1/3), calling with Queen facing a bet at 33.64% (exact Nash 1/3), slow-playing King 41.59% of the time (betting 58.41% vs 3*alpha = 58.20%), and calling with Queen after pass-bet at 53.05% (vs alpha + 1/3 = 52.73%).
| State | Card & History | Pass / Fold | Bet / Call | Interactive Bet/Call Slider | Analytical Nash Target |
|---|---|---|---|---|---|
0 | P1 Jack (0) (Opening action) | 80.60% | 19.40% | P(Bet) = alpha in [0, 1/3] (At 1,000 updates: alpha = 0.1940 (19.40% opening bluff))
| |
0b | P2 Jack (0) (Facing P1 bet) | 99.95% | 0.05% | P(Call) = 0 (Always fold worst card facing a bet (0.05% residual at 1,000 updates))
| |
0p | P2 Jack (0) (After P1 pass) | 66.70% | 33.30% | P(Bet) = 1/3 (33.33%) (Exact Nash bluff frequency 1/3; learned 0.3330 (33.30%))
| |
0pb | P1 Jack (0) (Passed, facing P2 bet) | 99.97% | 0.03% | P(Call) = 0 (Always fold Jack to a bet (0.03% residual at 1,000 updates))
| |
1 | P1 Queen (1) (Opening action) | 99.25% | 0.75% | P(Bet) = 0 (Check medium strength hand (99.25% check at 1,000 updates))
| |
1b | P2 Queen (1) (Facing P1 bet) | 66.36% | 33.64% | P(Call) = 1/3 (33.33%) (Exact Nash indifference call 1/3; learned 0.3364 (33.64%))
| |
1p | P2 Queen (1) (After P1 pass) | 99.65% | 0.35% | P(Bet) = 0 (Check Queen to showdown (99.65% check at 1,000 updates))
| |
1pb | P1 Queen (1) (Passed, facing P2 bet) | 46.95% | 53.05% | P(Call) = alpha + 1/3 (Coupled to P1 Jack bluff: 0.1940 + 0.3333 = 0.5273; learned 0.5305 (53.05%))
| |
2 | P1 King (2) (Opening action) | 41.59% | 58.41% | P(Bet) = 3 * alpha (Coupled 3-to-1 to P1 Jack bluff: 3 * 0.1940 = 0.5820; learned 0.5841 (58.41%))
| |
2b | P2 King (2) (Facing P1 bet) | 0.05% | 99.95% | P(Call) = 1 (100%) (Always call with best card (99.95% call at 1,000 updates))
| |
2p | P2 King (2) (After P1 pass) | 0.10% | 99.90% | P(Bet) = 1 (100%) (Always bet King when checked to (99.90% bet at 1,000 updates))
| |
2pb | P1 King (2) (Slow-played, facing P2 bet) | 0.06% | 99.94% | P(Call) = 1 (100%) (Always call after trapping with King (99.94% call at 1,000 updates))
|
From -21.00 (0/20 wins) to +19.25 (20/20 wins) from raw 4 x 84 x 84 pixel stacks
Trained from raw 4 x 84 x 84 grayscale frame stacks for
2,000,896 environment steps (31,264 optimizer updates across 1,954
rollouts in 1,300.08 seconds; trainer PID 48704,
evaluator PID 7004). Initial parameters (SHA-256
6a5f2c086bb18025...) lose every game at
-21.00 (0/20 wins). Final parameters (SHA-256
3b55949ee318620b...) reach +19.25 (
20/20 wins, paired gain +40.25, 95% CI
[+40.10, +40.45]) on held-out seeds 910001-910020, and
+19.50 (20/20 wins) on independent review seeds
930001-930020. Resetting the policy returns immediately to -21.00.
910001 side-by-side replay (
389,629 bytes, SHA-256
af5509437cb6bec2...): untrained policy (left, -21) vs
trained checkpoint (right, +20).
Why a +21.00 (30/30) Atari score can be an open-loop memorization illusion unless tested at sticky = 0.25
In deterministic Atari (repeat_action_probability = 0.0), resetting the emulator
across 20 seeds yields only 2 unique initial observations and 15 unique
trajectories, allowing an open-loop action tape to score +21.00 (30/30 shutouts).
Testing under the Machado et al. (2018) standard (repeat_action_probability = 0.25
, checkpoint SHA-256 59f2e93a4db15c2a...) forces
20/20 unique trajectories: a scripted RAM ball-Y tracker collapses to -15.00,
while our pixel PPO policy wins 29/30 fresh episodes (mean +7.60,
IQM +7.38).
| Regime | Unique Init Obs | 2M-Step Checkpoint | 10M-Step Checkpoint | What It Proves |
|---|---|---|---|---|
| Deterministic regime (repeat_action_probability = 0.0) | 2/20 obs (15 /20 trajectories) | +19.25 mean (20/20 wins on 910001-910020; +19.50 on 930001-930020) | +21.00 +/- 0.00 (30/30 shutouts on 5000-5029, 9000-9029, and 30 secret random seeds) | Because the Atari 2600 RNG after reset collapses 20 seeds into only 2 initial observations when sticky=0.0, a policy can win 21-0 by replaying a memorized action sequence rather than tracking the ball. |
| Stochastic regime (repeat_action_probability = 0.25, Machado 2018 standard) | 20/20 obs (20 /20 trajectories) | +7.65 mean (19/20 wins on 920001-920020 zero-shot; +10.60 mean, 20/20 wins after B adaptation) | Mean +6.23, IQM +6.75 [5.12, 8.31] (26/30 wins on 5000-5029); Mean +7.60, IQM +7.38 [6.38, 8.69] (29/30 wins on fresh 9000-9029) | When 25% of frames repeat the previous action, open-loop sequences desynchronize immediately. Winning 29/30 episodes (IQM +7.38) while a scripted RAM ball-Y tracker loses -15.0 proves closed-loop visual control. |
Why 1 seed is an anecdote, 42-checkpoint selection inflates scores by +1.14 points, and 10 x 200 seeds prove LunarLander-v3 PPO
Digest qualification: The historical record retains a 52-character value, not a complete SHA-256 and not an exact prefix. It is preserved for compatibility and is not usable for integrity verification. The full historical digest has not been established.
Reporting a single seed or cherry-picking the best checkpoint on evaluation seeds inflates RL
claims. On CartPole, seed 0 reaches +457.9 +/- 76.2 while seed 1 collapses to
+9.3 +/- 0.8. On Pong (`sticky = 0.25`), picking the best of 42 checkpoints on
seeds 5000-5029 (`best_8750000`) yields +8.87, but shrinks by
-1.14 points to +7.73 on fresh seeds 9000-9029 (indistinguishable
from the unselected 10M checkpoint at +7.60). To prove LunarLander-v3 PPO under
Agarwal et al. (2021), we trained 10 independent seeds (1,015,808 steps each)
and evaluated across 200 held-out seeds per model (2,000 episodes):
How stepping 1 pixel left of spawn turns unsigned NES RAM 0x6D into x = 65,535 (20x past the flagpole)
In Super Mario Bros (NES), horizontal progress is stored across two 8-bit RAM bytes: screen page 0x006D and screen pixel offset 0x0086. When an agent steps 1 pixel left of spawn (x = 0), the 8-bit page byte underflows from 0x00 to 0xFF (255) with offset 0xFF (255). An unsigned decoder computes 256 * 255 + 255 = 65,535 (more than 20 times the World 1-1 flagpole at x = 3,161), rewarding the policy for walking backward into the left wall.
0x006D / 0x0086 coordinate lab: Fakes-first verification: building 68 synthetic fraud artifacts to red-team the evaluator before trusting a single training run
Before trusting any agent-submitted training run, we built 68 adversarial fake
packages and 51 scorer tamper mutations. Across five evaluator
revisions, false-negative slip-through dropped from 39/68 (`checklist_v1`),
24/68 (`v2`), 14/68 (`v3`), and 5/68 (`v4`) to
0/68 slipped through (`68/68` caught, `0` false positives) in
checklist_v5 + precheck.py (SHA-256
71d1170b5c71732d...). Click any row below to inspect how v1 vs
v5 handles the attack.
| Case ID | Injected Fraud / Tamper Vector | checklist_v1 | checklist_v5 | Enforced Invariant |
|---|---|---|---|---|
v5_00_control_valid | None: 5 independent training seeds, matching SHA-256 manifest, untrained baseline, and held-out evaluation. | PASS (valid) | PASS (valid) | All schema, manifest, seed-diversity, baseline, and replay checks satisfied. |
v2_01_lunar_999_consistent2 | Fabricates +999.0 episode returns across JSON summary, CSV rows, and recalculated IQM so internal arithmetic matches. | SLIPPED THROUGH (accepted fake) | REJECTED (caught) | Domain physical ceiling & live checkpoint weight-replay check (LunarLander-v3 theoretical ceiling ~320). |
v2_18_checkpoint_pool | Evaluates 42 intermediate checkpoints on the test seed block and reports only the highest-scoring checkpoint without a fresh holdout block. | SLIPPED THROUGH (accepted fake) | REJECTED (caught) | Mandatory disjoint holdout seed block when checkpoint selection is performed. |
v2_04_train_init_short | Cuts off the untrained baseline rollout after 5 steps so the trained policy looks artificially superior. | SLIPPED THROUGH (accepted fake) | REJECTED (caught) | Matched episode termination / horizon parity between initial_policy and final_policy. |
v5_b_untrained_swapped_worse | Replaces random-initialized weights with anti-policy weights that dive into the ground, inflating paired delta. | SLIPPED THROUGH (accepted fake) | REJECTED (caught) | Untrained baseline distribution check against fresh random policy initialization. |
f_x_3600 | Reports max_x = 3,600 (> flagpole 3,161) from a policy that stepped left of x = 0 and underflowed RAM byte 0x6D. | SLIPPED THROUGH (accepted fake) | REJECTED (caught) | NES RAM coordinate physical bound (0 <= max_x <= 3,500) and signed page-byte verification. |
x4_manifest_line_removed | Edits a result file and deletes its corresponding line from manifest.sha256 so every remaining line still hashes cleanly. | SLIPPED THROUGH (accepted fake) | REJECTED (caught by precheck.py 71d1170b) | Bidirectional directory-to-manifest completeness check (every artifact on disk must appear in manifest.sha256). |
connect4_anchor_drift | Reports 60% win rate against the previous generation in self-play while actual win rate against a frozen anchor drops to 53%. | SLIPPED THROUGH (accepted fake) | REJECTED (caught) | Frozen external anchor evaluation required for all self-play and league claims. |
Weight reuse beats scratch training by +21.25 points, but doubling adaptation steps degrades Regime A retention below the -3.0 gate
When adapting our Atari Pong policy from deterministic Regime A (`sticky = 0.0`) to stochastic
Regime B (`sticky = 0.25`), continuing from trained weights beats matched 2M-step scratch
training on B by +21.25 points (+10.60 vs -10.65,
95% CI [+18.750, +23.500]). However, doubling continuation training from +1M to
+2M steps adds only +0.05 points on B while degrading Regime A retention from
19.15 to 17.55 (a -1.80 drop whose one-sided 95% lower
bound -3.35 breaches our -3.0 retention gate).
| Checkpoint | Regime A (sticky=0.0) | Regime B (sticky=0.25) | Finding |
|---|---|---|---|
| Frozen original (trained on A: sticky=0.0, 2M steps) | +19.35 (20/20) | +7.65 (19/20) | Strong zero-shot transfer to sticky=0.25 (+7.65), but loses 1 game and drops 11.70 points vs A. |
| Prior retained (+1M steps on B from original weights) | +19.15 (20/20) | +10.55 (20/20) | Lifts B by +2.90 points (20/20 wins) while losing only -0.20 points on A. |
| Extended retained (+2M steps on B from original weights) | +17.55 (20/20) | +10.60 (20/20) | Second +1M on B adds only +0.05 [-2.35, +2.00] on B while degrading A retention to 17.55 (-1.80, 95% lower bound -3.35). |
| Prior fresh (1M steps on B from random initialization) | -18.85 (0/20) | -17.05 (0/20) | From-scratch training at 1M steps cannot yet rally reliably on either regime. |
| Extended fresh (2M steps on B from random initialization) | -9.25 (4/20) | -10.65 (0/20) | Even at 2M steps from scratch, B reaches only -10.65 vs +10.60 for weight reuse (+21.25 advantage). |
Why a -0.0031% confidence-interval bound stopped a +5.625 pp Pokemon TCG promotion across 5,600 fresh games (1,002,605 actions)
Across 5,600 fresh Pokemon TCG games (1,002,605 verified actions), we wired a
learned 88-feature Ridge scorer and a 90-feature relational card-copy scorer directly into live
action selection. The learned weights genuinely changed 266,
812, and 868 live decisions, but all three pre-registered
confirmation gates failed: Study 1 gained +5.625 pp but its 95% lower CI bound hit
-0.003125 pp; Study 2 shrank to +1.00 pp [-2.125, +4.125] over 2,400
games; Study 3 scored -1.50 pp [-4.667, +1.667]. Root-cause diagnosis uncovered
58 feature-aliasing groups (236/511 rows) and proved that uniform 1/K exploration
(mean menu K = 10.865, max weight 27.0) yields only
58/511 exact parent matches (11.35%) and an inverse-propensity Effective
Sample Size of ESS = 48.06 (below the 100-match gate).
| Study | Fresh Games & Actions | Changed Decisions | Parent vs Challenger | Delta & 95% CI (pp) | Pre-Registered Gate Verdict |
|---|---|---|---|---|---|
| Study 1: 88-feature Ridge action scorer (tcg-scorer-v1) | 800 games (216,710 actions) | 266 | 76.25% vs 81.88% | +5.625 pp [
-0.003125, +11.125]
| FAIL (lower 95% CI bound -0.003125 pp <= 0) |
| Study 2: Higher-power 2,400-game replication (tcg-scorer-replication-v1/attempt2) | 2,400 games (391,026 actions) | 812 | 79.71% vs 80.71% | +1 pp [
-2.125000, +4.125]
| FAIL (effect shrank from +5.625 pp to +1.00 pp; 95% CI spans zero) |
| Study 3: 90-feature relational card-copy scorer (tcg-relational-v1) | 2,400 games (394,869 actions) | 868 | 81.42% vs 79.92% | -1.5 pp [
-4.666667, +1.667]
| FAIL (-1.50 pp vs parent; relational features alone did not rescue uniform exploration) |
Measuring 1.80e-5 CPU-vs-CUDA float drift, SB3 rollout-buffer batch scaling (n_steps x n_envs), and 32 GB DLL pagefile traps
On identical state probes (SHA-256 f830f1d96ebbd2a8...),
evaluating the same checkpoint on CPU vs RTX 5080 CUDA produces a maximum action-probability
drift of 1.800060272216797e-5 (`0.0` drift across repeated CUDA reloads, with
8/8 probes identical across CPU and CUDA). In Stable-Baselines3 PPO, the rollout buffer holds n_steps * n_envs transitions. Raising n_envs from 8 to 20 with n_steps = 256 increases that buffer from 2,048 to 5,120 transitions, so complete rollout collection and training cycles occur 2.5 times less often per million transitions. This alone does not establish fewer optimizer steps: those also depend on minibatch size, optimization epochs and early stopping. The original training configuration has not been verified here, so its optimizer-step frequency remains unresolved. Furthermore, importing torch inside 20 Windows spawn workers reserves ~1.6 GB of CUDA DLL virtual memory per process (~32 GB pagefile). Isolating Ctypes/LaiNES workers in a torch-free module (mario_worker.py) eliminates the pagefile exhaustion.
Stable-Baselines3 PPO parameter definitions distinguish rollout size, minibatch size and optimization epochs.
When AI research agents actually learn (and when prose lessons fail): L01-L27, V01-V06, and the Sprawl Audit
| Ledger ID | Category | Finding | Evidence & Operational Outcome |
|---|---|---|---|
AUDIT-PRIOR-CLAIMS | Retraction & baseline audit | Retracted unverified early claims before Phase 1 rerun | AUDIT-prior-claims.md & L01-L09: Withdrew single-seed CartPole (+457.9 on seed 0 vs +9.3 on seed 1), unanchored Connect-4 self-play (0.60 -> 0.53 vs frozen anchor), and truncated-baseline LunarLander claims; rebuilt evaluation around 10-seed x 200-episode IQM and checklist_v5. |
L16-TO-L19-REUSE | Confirmed procedural reuse | Zero-exploration migration of BO3 Playground contract (L16 -> L19) | PTCG-Learning-Lab/LESSONS.md & REUSE.md: After recording the exact 3-step recipe in L16, a subsequent agent task applied it in L19 with 0 exploratory tool calls and passed 7/7 automated checks on the first run. |
L17-L21-L25-PROSE-FAILURE | Preserved negative finding on prose rules | Written prose lessons failed to prevent repeat process errors until turned into code | PTCG-Learning-Lab/LESSONS.md (L17, L21, L25) & precheck.py (71d1170b): Recording a prose lesson in LESSONS.md did not stop agents from repeating a worktree deletion mistake (L17 -> L21) or launching an unapproved replication run before diagnosis (L25). Only executable fail-closed gates (precheck.py, checklist_v5, and pre-registered spec hashes) reliably prevented regressions. |
CORRECTION-2026-10-30 | Preserved mathematical self-correction | Withdrew an initial 64-game ESS = 100 formula before running a single new game | PTCG-Learning-Lab/checks/learning-design-v1/CORRECTION.md & analysis-v2.json: A review caught that normalizing inverse-propensity weights across all 511 games inflated apparent parent coverage. Recomputing exact parent-indicator support revealed 58/511 matches (11.35%) and ESS = 48.06, stopping a flawed 64-game study before execution. |
Scope Limits and What This Page Does Not Claim
- All experiments are author-directed runs in local workspaces (PTCG-Learning-Lab and agentlearning); separate evaluator processes and second-model reviews in the same workspace are internal checks, not external independent reproduction.
- Kuhn Poker CFR, LunarLander-v3 PPO (10 seeds x 200 episodes), and Atari Pong pixel PPO (sticky=0.0 and sticky=0.25) demonstrate confirmed learning on those benchmarks. They do not establish general algorithmic novelty.
- In Pokemon TCG, the closed-loop linear and relational scorers changed 266, 812, and 868 search decisions across 5,600 fresh games, proving the closed-loop mechanism runs end-to-end, but all three held-out confirmation gates failed (+5.625 pp [-0.0031, +11.125], +1.00 pp [-2.125, +4.125], and -1.50 pp [-4.667, +1.667]). Retained competitive improvement in Pokemon TCG remains an open objective.
- In Pong regime adaptation (sticky=0.0 -> sticky=0.25), weight reuse beat matched scratch training by +21.25 points, but doubling continuation steps degraded Regime A retention below the one-sided -3.0 gate (-3.35).