Thibaut Forest Claude Opus 5 (1M context) commited on
Commit
4e17cd1
·
1 Parent(s): 25432c8

Phase 46: overfitting audit - luck bounds, certain pool; ADOPT cert-first (k=3) ordering, all-48 1661.7 -> 1623.7, 3W/0L/45T

Browse files
Files changed (2) hide show
  1. EXPERIMENTS.md +127 -0
  2. batteryswap_example/submit.py +43 -0
EXPERIMENTS.md CHANGED
@@ -2368,3 +2368,130 @@ a signal J2W computes that is outside this feature space entirely, or the
2368
  forward-reading of the split file that phase 43(a) verified and declined.
2369
 
2370
  Standing champion: **3c7b883 at 1661.45**. Deadline 2026-08-23.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2368
  forward-reading of the split file that phase 43(a) verified and declined.
2369
 
2370
  Standing champion: **3c7b883 at 1661.45**. Deadline 2026-08-23.
2371
+
2372
+ ## Phase 46 - the blind champion itself, as a whole: REJECTED (lineage fully adjudicated)
2373
+
2374
+ Question (user, 2026-08-22): the blind-repo multi-agent loop's shared champion
2375
+ (`9a969a6a7dfe.py`, blind full-40 1566.7) - reusable here? The headline number
2376
+ is not on our scale (its own 40-origin harness; ~55 of its margin was extra cut
2377
+ depth), and most pieces were already adjudicated: CV discipline ported (40),
2378
+ `vrel`/`vmin7_rank` as ADDITIONS rejected (41), in-fleet pooling a public wash
2379
+ (42), trip-merge already native as building batching + group-day search
2380
+ (phases 1/14). One piece had never been tested: the champion's exact 4-feature
2381
+ set **as a replacement ranker** - `FEATURES=[last_voltage, vmin7, n_obs, vrel]`,
2382
+ mapped here to `[v_last, v_min_7, n_30, vrel]` (`blind4` in cv.VARIANTS).
2383
+
2384
+ ### Fixed-N ablation: a real but small head-ordering gain
2385
+
2386
+ Paired vs base, deaths caught, bootstrap 95% CI. `v_last_only` cross-check
2387
+ reproduces phase 41 (-39 [-56,-21] native N=12 vs published -39 [-57,-22]).
2388
+
2389
+ | variant | native N=8 | native N=12 | static N=8 | static N=12 |
2390
+ |---|---|---|---|---|
2391
+ | `blind4` | +11 [-1,+23] | +9 [-8,+26] | **+27 [+7,+47]** | +14 [-2,+31] |
2392
+ | `blind3` (no vrel) | +14 | +10 | +22 | +12 |
2393
+
2394
+ One CI of eight excludes zero (static N=8), signs consistent at N<=12 on both
2395
+ instruments, and rec@30 / miss-rank get WORSE (57 -> 65 native): the small set
2396
+ trades deep tail for head. `blind3` matching `blind4` everywhere settles the
2397
+ phase-41 question from the other side: **vrel is not load-bearing; the gain is
2398
+ the small set itself** - 4 features against effective n ~80 death-batteries,
2399
+ where 13 features overfit slightly (phase 44's C-sweep said the same thing
2400
+ through the regularisation knob).
2401
+
2402
+ ### Full harness: the ordering gain does not survive the planner
2403
+
2404
+ | all48 | total | sched | caught/454 | early | late |
2405
+ |---|---|---|---|---|---|
2406
+ | base | 1661.7 | 13.4 | 291 | 473.1 | 987.5 |
2407
+ | blind4, alpha 0.8 | 1765.4 | 14.6 | 298 | 584.0 | 948.3 |
2408
+ | blind4, count-matched alpha 0.7342 | 1740.7 | 13.7 | 290 | 530.1 | 972.3 |
2409
+
2410
+ Raw: +103.8/origin - the 4-feature calibration inflates `sum(p42)`, the exact
2411
+ depth confound phase 41 diagnosed, in mirror image. Count-matched: **+79.1
2412
+ [-65,+231], 22W/26L, catches identical (290 vs 291)** - the +9 fixed-N catches
2413
+ vanish under the real inclusion rule (topsum + break-even top-up + floor is not
2414
+ top-N), and the whole residual is placement: +57 early-days/origin, because
2415
+ day placement integrates the p-curve and a 4-feature curve is cruder than a
2416
+ 13-feature one. Ordering is only half the score's job here; timing is the
2417
+ other half, and the small set is worse at it.
2418
+
2419
+ **Verdict: reject; nothing in the blind lineage is left unported or untested.**
2420
+ The blind champion's margin decomposes fully into (a) its own harness's depth
2421
+ and threshold tuning - non-transferable, appetite closed; (b) mechanisms we
2422
+ already have natively; (c) a head-ordering effect real at fixed N but worth
2423
+ zero catches and negative cost through this planner. Runnable via `blind4` /
2424
+ `blind3` in `cv.VARIANTS`.
2425
+
2426
+ Standing champion: **3c7b883 at 1661.45**. Deadline 2026-08-23.
2427
+
2428
+ ## Phase 46 - the overfitting question, audited; and the certainty-first adoption
2429
+
2430
+ User hypothesis: the leaders are overfitting the public board. Audited with a
2431
+ 4-way measurement (certain-pool size, our capture of it, the fix on both
2432
+ instruments, luck bounds). Verdict: **partially right, and the audit found a
2433
+ real local gain on the way.**
2434
+
2435
+ ### How much of the board is luck
2436
+
2437
+ The board keeps each account's best public score, so it best-of-N selects
2438
+ every team's luckiest config. From our own per-origin variance (sd 838 over 48
2439
+ origins -> sd of the 48-scenario mean = 121), the expected luck of keeping the
2440
+ best of N equal-skill submissions is 217/260/296/338/367 points for
2441
+ N=5/10/20/50/100.
2442
+
2443
+ - **J2W's 210-point lead over rank 2 needs only N~4.5.** The ordering WITHIN
2444
+ the leading pack is plausibly board luck.
2445
+ - **The pack's ~500-point lead over us would need N~5300.** Not luck.
2446
+
2447
+ ### The pack's edge is real - and it is not legitimately reachable by us
2448
+
2449
+ The certain pool (devices whose gated daily median has sustained < 2.40 V for
2450
+ 3 consecutive gated readings BEFORE the origin, i.e. the label rule already
2451
+ fired but the EOL has not landed - the wobble/lag population):
2452
+
2453
+ | rule at origin | flagged/scen | die in-window | die by +90d | never die |
2454
+ |---|---|---|---|---|
2455
+ | 2.40 sustained-3 confirmed | 1.06 | 94.4% | 100% | **0** |
2456
+ | 2.45 zone (days_below_245>=0) | 21.8 | 24.6% | - | 62% |
2457
+
2458
+ So the free-certainty pool is ~1/scenario, of which our champion already ranks
2459
+ 42/51 in the top 12. The leaders' ~2.2 extra catches/scenario cannot be built
2460
+ from any past-only certainty rule we can construct: the implied J2W marginal
2461
+ precision is 0.69-0.88 (robust across all 9 inversion assumptions), on picks
2462
+ that for us measure ~0% transfer precision. Either they hold a tail-ranking
2463
+ signal outside this feature space entirely, or they are reading outcomes (the
2464
+ phase-43a forward-read, whose ceiling - 84% recall, 1.6 misses/scen - they sit
2465
+ comfortably below at 2.45 misses; or battery-toggling against public, ~1
2466
+ battery learned per submission pair). Given phases 41-45 measured every
2467
+ legitimate axis at ~zero, the balance of evidence is that the top of the board
2468
+ is exploiting the setup, not merely lucky. Unprovable from here; recorded as
2469
+ an assessment, not a fact.
2470
+
2471
+ ### ADOPTED: certainty-first ordering (cert-first, k=3)
2472
+
2473
+ The audit's by-product. Force confirmed-crossing devices to the top of the
2474
+ ranking (rank_score = 2 + p42; count and timing untouched):
2475
+
2476
+ | | all-48 | panel | W/L/T | mechanism |
2477
+ |---|---|---|---|---|
2478
+ | first-reading rule (LEAKS 2 future readings) | -88.2 | -124.5 | 6/0/42 | struck |
2479
+ | **confirmed rule (past-only)** | **-37.9** | **-53.6** | **3/0/45** | adopted |
2480
+ | k=1 / k=2 confirmation | +290.7 / +199.5 | - | 9/30 / 9/31 | recoveries flood the head |
2481
+
2482
+ The confirmation depth is load-bearing and equals the label rule's own sustain
2483
+ parameter - k=3 is the unique safe point. Fixed-N instruments agree: native +9
2484
+ catches at N=12 (6W/0L), deployment gate +6 (6W/0L, ties elsewhere) - loss-free
2485
+ on every instrument at every N tested. The wins are label-lag devices our
2486
+ model under-ranks BECAUSE they recovered (v_last back up) - exactly the wobble
2487
+ population phase 39 characterised.
2488
+
2489
+ Deployment note: at runtime `iterate_scenarios` hands truncated data, so the
2490
+ rule is past-only by construction there; the local strict measurement is the
2491
+ honest predictor. Wired into submit.py (`BATTERYSWAP_CERT_FIRST`, default on,
2492
+ both branches; stacks on top of cliff's rank_score if that is ever enabled;
2493
+ p42_sum untouched so the count rule is byte-identical).
2494
+
2495
+ Local champion is now **cert-first at 1623.7** (from 1661.7). Public look not
2496
+ yet spent; submission is the user's call. Expected public value if the wobble
2497
+ rate transfers: ~-35 +/- (fires 0.46 devices/scenario locally).
batteryswap_example/submit.py CHANGED
@@ -152,6 +152,35 @@ CLIFF_SCALE = float(os.environ.get('BATTERYSWAP_CLIFF_SCALE', '1.0'))
152
  CLIFF_FRAC = 0.35
153
  CLIFF_V = 2.46
154
  CLIFF_MIN_N = 20
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155
 
156
 
157
  def build_split_table(locations, eol_times, scen_starts, daily, dall, betas,
@@ -246,6 +275,7 @@ def adaptive_split_plans(split, split_path, log=print, deadline=None):
246
  locations, timeseries, eol_times, scenarios = load_best_effort(split_path, log)
247
  scen_starts = [pd.Timestamp(s['start_time']) for s in scenarios]
248
  daily = daily_voltage(timeseries)
 
249
  dall = daily_all(timeseries)
250
  betas = weekly_betas(dall)
251
 
@@ -399,6 +429,19 @@ def adaptive_split_plans(split, split_path, log=print, deadline=None):
399
  if n_cliff:
400
  log(f' {scenario["name"]}: cliff boost x{CLIFF_SCALE} on '
401
  f'{n_cliff} knee devices')
 
 
 
 
 
 
 
 
 
 
 
 
 
402
  plan_tsroute.alpha = ALPHA_STATIC if static_model is not None else ALPHA
403
  plan_tsroute.ec_margin = EC_MARGIN
404
  plan_tsroute.ec_pmax = EC_PMAX
 
152
  CLIFF_FRAC = 0.35
153
  CLIFF_V = 2.46
154
  CLIFF_MIN_N = 20
155
+ # Phase 46: certainty-first ordering. The label process (reverse-engineered,
156
+ # 81/82 exact) stamps EOL ~3d after the gated daily-median voltage first
157
+ # sustains < 2.40 V for 3 consecutive gated readings. A device whose crossing
158
+ # is FULLY CONFIRMED in the past yet is still in the fleet is a label-lag /
159
+ # wobble case: locally 54 such flags, 51 die in-window, 3 die by +90d, ZERO
160
+ # never die. Boosting them to the top of the ordering (rank only - count and
161
+ # timing untouched) is -37.9 on the all-48 cost harness, 3W/0L/45T. The
162
+ # confirmation depth is load-bearing: k=1 (+291) and k=2 (+200) admit
163
+ # recoveries; k=3 - the label rule's own sustain - is the unique safe point.
164
+ # Past-only by construction: the flag requires all 3 readings <= T.
165
+ CERT_FIRST = os.environ.get('BATTERYSWAP_CERT_FIRST', '1') != '0'
166
+ CERT_V = 2.40
167
+ CERT_SUS = 3
168
+
169
+
170
+ def cert_confirm_days(daily):
171
+ """Per device: the day of the CERT_SUS-th consecutive gated daily-median
172
+ reading below CERT_V (the confirmation day), NaT-free Series. A device is
173
+ certainty-flagged at origin T iff its confirmation day <= T, which uses
174
+ only readings at or before T."""
175
+ out = {}
176
+ for dev, g in daily.sort_values(['device_id', 'day']).groupby(
177
+ 'device_id', sort=False):
178
+ b = (g['v'] < CERT_V).to_numpy()
179
+ for i in range(len(b) - CERT_SUS + 1):
180
+ if b[i:i + CERT_SUS].all():
181
+ out[dev] = g['day'].iloc[i + CERT_SUS - 1]
182
+ break
183
+ return pd.Series(out, dtype='datetime64[ns]')
184
 
185
 
186
  def build_split_table(locations, eol_times, scen_starts, daily, dall, betas,
 
275
  locations, timeseries, eol_times, scenarios = load_best_effort(split_path, log)
276
  scen_starts = [pd.Timestamp(s['start_time']) for s in scenarios]
277
  daily = daily_voltage(timeseries)
278
+ cert_days = cert_confirm_days(daily) if CERT_FIRST else pd.Series(dtype='datetime64[ns]')
279
  dall = daily_all(timeseries)
280
  betas = weekly_betas(dall)
281
 
 
429
  if n_cliff:
430
  log(f' {scenario["name"]}: cliff boost x{CLIFF_SCALE} on '
431
  f'{n_cliff} knee devices')
432
+ if CERT_FIRST and len(cert_days):
433
+ ch = cert_days.reindex(pred.index)
434
+ cert = (ch.notna() & (ch <= T)).to_numpy()
435
+ if cert.any():
436
+ # rank only: stacks on top of whatever rank_score exists
437
+ # (cliff, if enabled); p42 and p42_sum are untouched, so the
438
+ # inclusion count and every timing curve stay byte-identical
439
+ base_rs = (pred['rank_score'] if 'rank_score' in pred.columns
440
+ else pred['p42']).astype(float).to_numpy().copy()
441
+ base_rs[cert] += 2.0
442
+ pred['rank_score'] = base_rs
443
+ log(f' {scenario["name"]}: cert-first boost on '
444
+ f'{int(cert.sum())} confirmed-crossing devices')
445
  plan_tsroute.alpha = ALPHA_STATIC if static_model is not None else ALPHA
446
  plan_tsroute.ec_margin = EC_MARGIN
447
  plan_tsroute.ec_pmax = EC_PMAX