Thibaut Forest Claude Fable 5 commited on
Commit
e4745d2
·
1 Parent(s): 60d65da

HEAD: T3 warmup gate - gated hazard order + alpha 1.4 boost - CV 1418.3

Browse files

While the training fleet's death ledger at the plan origin is below
WARMUP_RATE (0.0759/device = 35/461), Ft2RUL switches ORDER to a
censored-pool discrete-time hazard HGB and boosts appetite x1.4 via a
consume-once plan_tsroute.alpha_boost; past the gate the model is
byte-identical to the previous head (WARMUP_RATE=0 disables exactly,
verified 0.0 max cell diff vs 1541.3).

bgkf: 1418.3, -123.0 +/- 43.1 (2.9sigma), 14W/5L, Wilcoxon .0017.
COUNT-COUPLED (swaps 2.54 vs 2.27) - shipped on the user's CV-first
call for a public trigger. Scale harness: gate narrows as fleets grow
(8/4/0 active scenarios at 1x/2x/4x) - bounded downside. Rehearsal:
gate fired on exactly s_0-14, s_15+ byte-identical, 0 warnings.
Includes T3 lane reports (gm label-statistic axis closed: the exact
label is a lagging smoother; cold-start gating; scale validation) and
phases 65-66.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

EXPERIMENTS.md CHANGED
@@ -3391,3 +3391,58 @@ cv.py run_bgkf split_days/timing_weight hooks, ft2a10/head track the
3391
  deployed dials. Verified: cand ft2a10 = 1541.3, cell-exact (0.0) vs the
3392
  lane's lam2sp3 measurement; paired -4.11 +/- 1.41 (2.9sig) 25W/4L
3393
  p8.8e-6, swaps flat 2.27. New champion CV 1541.3.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3391
  deployed dials. Verified: cand ft2a10 = 1541.3, cell-exact (0.0) vs the
3392
  lane's lam2sp3 measurement; paired -4.11 +/- 1.41 (2.9sig) 25W/4L
3393
  p8.8e-6, swaps flat 2.27. New champion CV 1541.3.
3394
+
3395
+ ## Phase 65 - CV frontier 1398.0 (user CV-first directive): warmup-gated alpha 1.4 x gated hazard order, both at deaths_seen<35
3396
+
3397
+ User directive: CV is the reported/optimized metric; count-coupled
3398
+ candidates are KEPT (not auto-shipped) as champions-in-waiting. Frontier
3399
+ config st_hz100t35_g16: at origins where the planned fleet's recorded
3400
+ deaths <= T number fewer than 35 (~s_0-15 on bgkf), ORDER = discrete-time
3401
+ hazard HGB (censored-pool trained, c14/c17 machinery) replacing the 3-way
3402
+ blend, and planner alpha = 1.4; everywhere else byte-identical to the
3403
+ 1541.3 head. CV 1398.0, -143.3 +/- 37.4 (3.8sig), 16W/0L, Wilcoxon <.001,
3404
+ swaps 2.53/cell (vs 2.27; count-coupled by design).
3405
+
3406
+ Decomposition of the -143.3: gated alpha 1.4 width-16 alone -125.4
3407
+ (4.0sig, clean bowl: width 6/8/10/12/14/16/20/24/32/48 ->
3408
+ 1501/1483/1462/1436/1429/1416/1414/1438/1475/1502; level bowl 1.3/1.4/
3409
+ 1.45/1.5/1.6 -> 1476/1436-1403/1416/1427-1449/1490 with 1.4 optimal at
3410
+ every width); hazard order adds -13 at thr20 and -18 at thr35 (16W/0L
3411
+ final). EC break-even expansion (teammate mechanism through our pricing)
3412
+ REFUTED: margin 1.0/1.2/1.5 monotone worse (+17/+29/+46), so the count
3413
+ posture belongs to gated alpha, not EC top-ups. Teammate public analysis
3414
+ (1486.57 vs our 1575.31): +191 swaps -> late -353, early +316, ops -53;
3415
+ pure operating-point difference, no luck involved either way.
3416
+
3417
+ Caveats attached to 1398.0: gate widths selected on the shared 48
3418
+ scenarios (multiplicity); deployment rule re-expressed as deaths_seen<35
3419
+ (plan-time observable, FLOOR_GATE family); the composite harness carries
3420
+ exact-rank-tie noise vs a deployed-class implementation (16/240 cells,
3421
+ documented in t3_coldstart.md) - a real ship needs the gate inside the
3422
+ deployed class with non-gated origins byte-identical. Pending: t3_gm
3423
+ label-statistic lane; synthetic-scale validation of the count-coupled
3424
+ family (user-commissioned; key question = does the gate close early on
3425
+ large fleets, bounding the downside). Drivers /tmp/c19, /tmp/c20
3426
+ (volatile); arm CSVs /tmp/cv_bgkfcand_c19_*, c20_*.
3427
+
3428
+ ## Phase 66 - SHIPPED the warmup gate (user call): head CV 1418.3
3429
+
3430
+ Deployed form of the phase-65 frontier inside Ft2RUL (user approved the
3431
+ ship for a public trigger): rate-form gate WARMUP_RATE=0.0759 deaths per
3432
+ training-fleet device (= 35/461; a fixed count fires wider on the CV's
3433
+ 4/5-size fold ledgers than on deployment's full ledger - measured 1429.7
3434
+ fixed vs 1418.3 rate), TRAIN-ledger observable seen = deaths recorded <=
3435
+ plan origin; while gated: ORDER = censored-pool discrete-time hazard HGB
3436
+ (_DiscHaz, person-period buckets, depth3/lr.06/250it), appetite x1.4 via
3437
+ a consume-once plan_tsroute.alpha_boost. eol column derived from
3438
+ origin+days_to_eol when absent (the cached parquet ships no eol; exact,
3439
+ 0 diff on 8206 dying rows - this bug made the first rehearsal silently
3440
+ inert). VERIFIED: cand ft2a10 = 1418.3, -123.0 +/- 43.1 (2.9sig, 14W/5L,
3441
+ Wilcoxon .0017, 19 changed origins, swaps 2.54); WARMUP_RATE=0 control
3442
+ bit-exact vs the 1541.3 head (0.0 max cell diff); rehearsal: gate fired
3443
+ on exactly s_0-14 (+2..+5 swaps each), s_15+ byte-identical, 0 warnings,
3444
+ 41s. Delta vs the phase-65 composite 1398.0 (~+20) = the composite's
3445
+ harness-only advantages (full-table hazard pool, literal-16 gate, tie
3446
+ noise); the deployed number is the honest one. COUNT-COUPLED - public
3447
+ trigger arbitrates; previous head remains 60d65da (1541.3/public
3448
+ lineage 1575.31) via WARMUP_RATE=0.
agent_reports_bgkf/t3_coldstart.md ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # T3 COLD-START lane: order adapted to training-information richness
2
+
3
+ Agent: T3 cold-start (c17). Date 2026-08-23 (deadline day). Work dir /tmp/c17/
4
+ (driver run.py, analysis analyze.py, gate diagnostic gate_diag.py). Arm CSVs at
5
+ /tmp/cv_bgkfcand_c17_*.csv. Reference /tmp/cv_bgkfcand_ft2a10.csv = deployed
6
+ head 60d65da at 1541.3 (asserted). All arms: values/counts from primary
7
+ logbag(vm7)@alpha1.0 untouched (order-only via run_bgkf blend hook at w 1.0,
8
+ count-flat by construction; swaps 2.275 vs ref 2.267 on every arm),
9
+ split_days=3, timing_weight=2.0.
10
+
11
+ ## The gate (plan-time observable, no forward reads)
12
+
13
+ deaths_seen(origin) = number of unique TRAIN-fleet devices with recorded
14
+ eol <= the plan origin date. Computed at fit time from the fold-train pool
15
+ (dev_eol ledger, exactly walk_forward's / submit.py FLOOR_GATE's family),
16
+ evaluated per predict() on the origin timestamp the rows carry. A death is
17
+ observable from the moment it happens; the train fleet's labels are fully
18
+ known when a foreign fleet is served -- deployment-legal both ways.
19
+ Gate g = 1[deaths_seen < 20] (FLOOR_GATE parity). Firing pattern (fold
20
+ crossings of 20 at origins [11,12,9,12,9]): all of warmup s_0-7 plus a
21
+ 2-4-origin bleed into early panel (s_8-11). deaths_seen at s_0 is 1-3,
22
+ s_7 is 12-15. Hazard source = c14's DiscHazHGB (discrete-time hazard HGB,
23
+ person-period buckets, censored pool via mc.censored_pairs at max origin
24
+ filtered to train devices -- byte-identical to run_bgkf's own hazard branch).
25
+
26
+ ## Baseline calibration (matters for reading the table)
27
+
28
+ cs_champ (my composite replication of the deployed order, champion weights,
29
+ no gate) scores +18.5 +/- 15.0 vs the deployed ref with only 16/240 cells
30
+ differing, dominated by 4 large sign-mixed jumps (+546, +280, +277, -248):
31
+ the documented exact-rank-tie flips (submit.py's own comment: one flipped tie
32
+ moved a cell by 539; the blend hook's extra rank layer resolves ties
33
+ differently than Ft2RUL's internal sort). So:
34
+ - "vs ref" carries ~15-cost-unit tie-flip noise unrelated to the mechanism;
35
+ - "vs cs_champ" is the clean paired mechanism read (same code path, plans
36
+ bitwise identical at every non-gated origin -- deltas exactly 0 there);
37
+ - "ship-proj" = delta vs ref restricted to gate-active origins (s_0-11),
38
+ exact 0 elsewhere: what a deployed-class implementation would score, since
39
+ the gate leaves non-gated origins byte-identical to the deployed head.
40
+
41
+ ## Results (48 paired all-building scenario totals)
42
+
43
+ vs deployed ref 1541.3 | per-block = per-origin mean within block
44
+
45
+ | arm | SCORE | vs ref | vs cs_champ (clean) | ship-proj | W/L(ref) | wilc(ref) | swaps | warmup | panel | nearend |
46
+ |------------|--------|---------------|-------------------------|----------------|----------|-----------|-------|--------|-------|---------|
47
+ | cs_champ | 1559.8 | +18.5+/-15.0 | -- | -- | 6W/8L | p0.391 | 2.275 | +34.6 | +15.5 | +13.6 |
48
+ | cs_hz10 | 1556.5 | +15.2+/-15.9 | -3.3+/-4.0 (0.8s) | ~0 | 7W/10L | p0.548 | 2.275 | +38.8 | +10.0 | +13.6 |
49
+ | cs_hz20 | 1547.0 | +5.7+/-18.3 | -12.8+/-9.5 (1.3s) | -7.0+/-11.6 | 9W/10L | p1.000 | 2.275 | -18.5 | +10.0 | +13.6 |
50
+ | cs_hz35 | 1549.9 | +8.6+/-18.2 | -9.8+/-12.5 (0.8s) | -4.1+/-11.5 | 9W/10L | p0.738 | 2.275 | -27.0 | +16.1 | +13.6 |
51
+ | cs_hz60 | 1520.0 | -21.3+/-23.1 | -39.7+/-20.1 (2.0s) | -34.0+/-17.9 (1.9s) | 14W/10L | p0.331 | 2.275 | -149.0 | +2.6 | +13.6 |
52
+ | cs_hz100 | 1514.7 | -26.6+/-28.0 | -45.1+/-25.5 (1.8s) | -39.3+/-23.9 (1.6s) | 14W/11L | p0.474 | 2.275 | -177.9 | +1.9 | +13.6 |
53
+ | cs_hz20t30 | 1540.6 | -0.7+/-15.1 | -19.2+/-15.6 (1.2s) | -- | 9W/11L | p0.985 | 2.275 | -18.5 | +1.0 | +13.6 |
54
+ | cs_ramp | 1564.9 | +23.5+/-15.8 | +5.1+/-4.4 (0W/3L) | ~0 | 6W/10L | p0.231 | 2.275 | +38.8 | +21.7 | +13.6 |
55
+ | cs_wnup | 1552.7 | +11.4+/-20.4 | -7.0+/-13.4 (0.5s) | -- | 8W/10L | p0.609 | 2.275 | +42.3 | +3.8 | +13.6 |
56
+ | cs_lbdown | 1552.1 | +10.8+/-17.6 | -7.7+/-8.5 (0.9s) | -- | 8W/10L | p0.671 | 2.275 | +38.6 | +3.8 | +13.6 |
57
+
58
+ Arm rules: cs_hzNN = (1-g*w)*champ_order + g*w*rank(DiscHazHGB), w=NN/100,
59
+ g=1[deaths_seen<20]; cs_hz20t30 same at thr=30 (fires to s_14-18);
60
+ cs_ramp w_hz=.20*max(0,1-deaths_seen/20); cs_wnup gated weights .40/.40/.20;
61
+ cs_lbdown gated .40/.36/.24 (no-hazard controls). Weights round(.,12).
62
+
63
+ ## Best arm per-block detail (clean pairing vs cs_champ)
64
+
65
+ cs_hz60: warmup -183.6/origin, panel -12.9/origin (ALL of it at the gated
66
+ bleed s_8-11; s_12+ exactly 0), nearend exactly 0. 8W/3L on the 11 changed
67
+ origins, wilcoxon p0.083.
68
+ cs_hz100: warmup -212.4/origin, panel -13.7 (gated bleed only), nearend 0.
69
+ 8W/4L, p0.151. Per-origin vs ref: s_0 -422, s_1 -879, s_2 -107, s_3 +119,
70
+ s_4 +8, s_5 +156, s_6 -234, s_7 -64, s_8 -17, s_9 +210, s_10 -190, s_11 -468.
71
+ Gains are largest where deaths_seen is smallest (s_0/s_1: 1-3 deaths seen) --
72
+ the dose matches the mechanism, cell by cell.
73
+
74
+ ## Mechanism evidence
75
+
76
+ 1. Dose-response monotone in gated hazard weight: warmup (vs ref) +39 / -19 /
77
+ -27 / -149 / -178 per origin at w = .10/.20/.35/.60/1.0.
78
+ 2. cs_hz100's warmup -177.9/origin REPLICATES the T2 hazard lane's
79
+ independently measured "warmup -178/cell for hazard-alone" (c14 h_alone,
80
+ old dials) -- the number was predicted before this campaign ran.
81
+ 3. Hazard-specific: the no-hazard gated controls (cs_wnup +7.7, cs_lbdown
82
+ +4.1 warmup) capture none of the warmup gain -- it is not "less logbag at
83
+ cold start", it is the hazard model.
84
+ 4. Concentration exactly as claimed: every point of gain sits inside the
85
+ gate-active origins; s_12+ and nearend are bitwise-identical plans.
86
+ 5. Count-flat: order-only by construction, swaps 2.275 vs 2.267 ref, every arm.
87
+ 6. Dead as predicted: cs_hz10/cs_ramp too weak (small hazard weight is
88
+ worse than none: at cold start the champ sources are the noise, so
89
+ diluting them slightly just perturbs ties); widening the gate to thr=30
90
+ (cs_hz20t30) adds nothing beyond its s_8-11 share.
91
+
92
+ ## Tier verdict
93
+
94
+ New mechanism (per-origin gated order adaptation) => bar is 2sigma paired +
95
+ concentration + controls. cs_hz60: 2.0sigma on the clean pairing
96
+ (-39.7+/-20.1), 1.9sigma on the ship projection (-34.0+/-17.9, 8W/3L gated
97
+ origins, wilcoxon p0.083), concentration textbook, controls pass.
98
+ cs_hz100: bigger point estimate (-45.1 clean / -39.3 ship-proj) at
99
+ 1.6-1.8sigma. Raw vs-ref sig is only ~1sigma for both because tie-flip noise
100
+ at non-gated origins contaminates that comparison; a deployed-class
101
+ implementation would not carry it.
102
+
103
+ VERDICT: cs_hz60 MEETS the bar at exactly 2.0sigma (clean pairing) with all
104
+ three qualitative gates passed; cs_hz100 is the better point estimate but
105
+ sits below 2sigma. Caveats for the main session: (i) the 2.0sigma is the max
106
+ of a 5-point weight sweep -- mitigated but not eliminated by the monotone
107
+ dose-response and the pre-registered T2 warmup number; (ii) the effect lives
108
+ on 11-12 origins (effective n small, sign-mixed 8W/3-4L); (iii) warmup block
109
+ public relevance depends on the held-out scenarios actually containing
110
+ early-history origins. Recommend ship-candidate = gated hazard at w=.60
111
+ (or the .60-1.0 shelf; the curve is flat-to-improving there), gate
112
+ deaths_seen<20 on the train eol ledger, implemented inside the deployed class
113
+ so non-gated origins stay byte-identical. Hazard model must ship with the
114
+ pickle (Docker/sklearn 1.7.2 retrain lock applies).
115
+
116
+ ## Not retested (already dead) / files
117
+
118
+ Global hazard 4th source, global slot replacement, warmup-gated ALPHA
119
+ (count-coupled), per-building rank norm, boundary cascade, flat simplex --
120
+ untouched. Driver /tmp/c17/run.py; gate diagnostic /tmp/c17/gate_diag.py
121
+ (per-fold deaths_seen curves); tie audit in analyze output above.
agent_reports_bgkf/t3_gm.md ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # T3 gm campaign: exact EOL label statistic, replicated — no delivery wins
2
+
3
+ LABEL-STATISTIC agent, 2026-08-23. Champion = head 60d65da (CV 1541.3,
4
+ order .50 lb / .30 wiener / .20 gapHGB, split_days=3, timing_weight=2.0).
5
+ Reference /tmp/cv_bgkfcand_ft2a10.csv (asserted 1541.3). All arms paired,
6
+ per-origin, 48 origins. Work dir /tmp/c16/ (validate*.py, build_gmfeat.py,
7
+ run.py, run2.py, gmfeat.parquet, simfeat.parquet, cv_bgkfcand_c16_*.csv).
8
+
9
+ ## 1. Mechanism replication: 81/82 exact death dates, 0 false positives
10
+
11
+ The competition EOL label is deterministic from telemetry. Re-derived recipe
12
+ (from dataset/train/battery_metrics.parquet vs dataset/train/eol_times.csv,
13
+ 82 non-null deaths — one more than the remembered 81):
14
+
15
+ 1. Gate readings to temperature strictly inside (10, 30) — exclusive bounds
16
+ (same as features.py daily_voltage).
17
+ 2. Daily median voltage per device; a day is VALID only if it has
18
+ **>= 5 gated readings** (minn=6 breaks 7 devices, minn=4 breaks 8).
19
+ 3. Reindex valid daily medians onto the full CALENDAR grid (crossings land
20
+ on no-data days as old high days fall out of the window — evaluating
21
+ only on observation days loses ~9 devices).
22
+ 4. Rolling 7-calendar-day median, **min_periods=3** (window needs >= 3
23
+ valid days; pandas interpolated median for even counts).
24
+ 5. EOL = first day where the rolling value <= 2.40.
25
+
26
+ Score: **81/82 exact day-level matches, 0 date mismatches, 0 survivor false
27
+ positives**. The 82nd (d_cfdd8b69ddd4) dies on the day its statistic equals
28
+ exactly float32(2.4)=2.4000000954; comparing <= float32(2.4) fixes it but
29
+ makes d_9ba303a913ad cross 37 days early at the same float value — the
30
+ original generator's tie-at-threshold semantics are unresolvable from data
31
+ (irrelevant at feature level). Journey: naive gated-daily-median cal7 recipe
32
+ = 60/82; +calendar-grid eval = 61; +minn5/min_periods3 = 81. Pooled-reading
33
+ median rejected (38/82). The dip-recovery "mid-life swap" mismatches
34
+ (-72..-10 days) were all artifacts of low-count winter days polluting the
35
+ window; the n>=5 validity rule removes them exactly.
36
+
37
+ ## 2. Feature cache (leak-free, repo day<=origin convention)
38
+
39
+ /tmp/c16/gmfeat.parquet — 77,267 rows keyed (device_id, origin_idx), all 168
40
+ origins (48 real + synthetic), 100% real-origin coverage: gm_last, gm_gap
41
+ (=gm_last-2.40), gm_age, gm_slope_7/21 (polyfit on the rolling-median series,
42
+ >=3 pts), gm_dtc (gap/max(-slope21,1e-4), clip [0,400]), gm_cov_30.
43
+ /tmp/c16/simfeat.parquet — sim_k: exact days-to-label-crossing if the last
44
+ valid daily-median level persists (window arithmetic simulation), else 400.
45
+
46
+ Signal autopsy (real origins, n=19,890, 454 dying rows):
47
+ - AUC dies42: gm_gap 0.939, gm_dtc 0.852 — vs v_corr_last 0.945, v_min_7
48
+ 0.961. **The exact label statistic is a lagging smoother; our existing
49
+ proxies anticipate it.** Median dying-device within-origin rank: v_min_7
50
+ 10 < gm_gap 14.
51
+ - gm_dtc (slope extrapolation of a piecewise-constant median) is noisy:
52
+ corr with days_to_eol only 0.15, median abs error 13-18d. NOT
53
+ near-deterministic.
54
+ - sim_k IS sharp where it fires: 99 flagged rows, 57% die in window (25x
55
+ base rate), median 3 days to death — but recall is only 12% of dying rows
56
+ and the champion already ranks them high.
57
+
58
+ ## 3. Arms (all kind='logbag' variant='vm7' blend=(arm,1.0) p_star=1.0,
59
+ split_days=3, timing_weight=2.0; paired vs 1541.3; order-only, counts flat)
60
+
61
+ | arm | what | SCORE | delta±SE | sig | W/L | wilcoxon | swaps |
62
+ |-----------|--------------------------------------------------|--------|--------------|-----|-------|----------|-------|
63
+ | parity | order3 re-implementation, no gm | 1541.3 | +0.00, maxabs 0.00 | — | — | — | 2.27 |
64
+ | g_hgb | (a) GM5 added to gap-HGB features | 1567.6 | +26.3±18.6 | 1.4 | 9/8 | .705 | 2.27 |
65
+ | g_wnr | (b) WienerFHT with d = gm_gap (fallback v_corr) | 1608.9 | +67.6±35.1 | 1.9 | 19/20 | .176 | 2.26 |
66
+ | g_dtc405 | (c) 4th source rank(-gm_dtc) w=.05 | 1655.4 | +114.1±33.6 | 3.4 | 11/24 | .002 | 2.28 |
67
+ | g_dtc410 | (c) same, w=.10 | 1734.5 | +193.2±47.9 | 4.0 | 16/27 | .001 | 2.25 |
68
+ | g_both | (d) = a+b | 1639.4 | +98.1±35.0 | 2.8 | 18/24 | .034 | 2.26 |
69
+ | g_ctl | (e) control: GM5 in logbag order-primary | 1541.3 | +0.0±35.0 | 0.0 | 17/12 | .670 | 2.26 |
70
+ | g_boost05 | creative: +.05 surgical boost where sim_k<=6 | 1543.1 | +1.8±22.1 | 0.1 | 6/5 | .898 | 2.27 |
71
+ | g_boost10 | creative: same, .10 | 1549.3 | +8.0±22.8 | 0.3 | 5/7 | .733 | 2.27 |
72
+
73
+ Per-block deltas: no arm wins nearend s_42-47 either (best g_hgb -1.0,
74
+ noise); losses concentrate in the panel block. Parity control is exact, so
75
+ deltas are pure gm effects. Swaps flat at ~2.27 everywhere.
76
+
77
+ ## 4. Verdict
78
+
79
+ **Mechanism validated (81/82), delivery comprehensively negative. No
80
+ candidate. Nothing meets any tier bar; do not ship.** The bold idea's
81
+ premise half-holds: the label IS deterministic and now fully reproducible —
82
+ but exactness at the barrier adds no ordering value because the statistic
83
+ lags the raw voltage features the champion already uses (v_min_7 leads it by
84
+ construction: min <= median-of-medians). The interesting control result:
85
+ GM5 inside the logbag order-primary is a perfect wash (+0.0), unlike GAP4
86
+ which cost +123 — gm features are benign but redundant.
87
+
88
+ Reusable for future work (not this deadline): the exact recipe in section 1
89
+ (e.g., deployment-side exact-crossing detection / label-consistent
90
+ truncation). It is computable in submit.py's split pipeline from raw
91
+ telemetry with ~15 lines (groupby day-median with count filter, reindex,
92
+ rolling(7, min_periods=3).median()).
agent_reports_bgkf/t3_scale_counts.md ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # t3: count-coupled candidates on the synthetic scaling harness
2
+
3
+ **Question.** Are the alpha-1.4 candidates' gains stable as fleets grow (more
4
+ batteries, more failures), and does the death-count gate on gated14 bound its
5
+ downside at scale? Robustness layer on top of the bgkf CV (champ 1541.3 /
6
+ flat14 1501.9 / gated14 ~1483).
7
+
8
+ **Setup.** Harness rebuilt from `agent_reports_bgkf/artifacts/{synth,scale_run}.py`
9
+ into `/tmp/c21/` (the /tmp/teamrepo clone was wiped; team `features.py` now
10
+ imported from `/Users/THIF/repos/BatterySwapAI2026-rul-mgt-zero`). Fleets at
11
+ k=1/2/4 = 461/922/1844 devices via time-shifted building clones, seed 7,
12
+ clone-0 = untouched original; EOLs derived from shifted telemetry by the
13
+ official mechanism — clone-0 check 0 mismatches at every k (75/75 checkable
14
+ deaths). Evaluation is deployment-faithful CURRENT head: one static
15
+ `submit.Ft2RUL` fit on the packaged train table (3-way order .50/.30/.20),
16
+ planner dials `split_days=3, timing_weight=2.0, ec_margin=0.8, ec_pmax=25,
17
+ floor_scale=2.5` with `recent_deaths=0` (static path: train_uniq=82 >=
18
+ FLOOR_GATE=20, floor inert exactly as in deployment). Arms differ ONLY in
19
+ `plan_tsroute.alpha`; features/predictions shared per scenario, so deltas are
20
+ exactly paired. All 48 scenarios per (k, arm). EARLY/LATE wired from each
21
+ scenario's settings.
22
+
23
+ - champ: alpha 1.0 everywhere (shipped head)
24
+ - flat14: alpha 1.4 everywhere
25
+ - gated14: alpha 1.4 while deaths_seen(T) < 20 else 1.0, with deaths_seen(T)
26
+ = # devices in the planned fleet with recorded eol <= T (plan-time
27
+ observable, FLOOR_GATE-family rule; no forward reads)
28
+
29
+ **Anchor.** k=1 champ = 1489.8, far below the old-champion anchor ~1762.5 and
30
+ in the current head's bgkf neighborhood (1541.3; the harness prices derived
31
+ EOLs + a template travel matrix, so a ~50pt offset is expected). champ
32
+ swaps/scen 10.8, swaps/death 1.16, recall 0.569 — deployment-plausible.
33
+
34
+ ## Results (mean fleet total per scenario; delta = candidate - champ, paired)
35
+
36
+ ### k=1 (461 devices)
37
+ | arm | score | delta ± SE | W/L | swaps/scen | late-d/scen | early-d/scen | warmup Δ/scen |
38
+ |---|---|---|---|---|---|---|---|
39
+ | champ | 1489.8 | — | — | 10.8 | 95.4 | 640.4 | — |
40
+ | flat14 | 1388.0 | -101.9 ± 52.9 | 20/23 | 14.3 | 69.8 | 939.9 | -537.9 |
41
+ | gated14 | 1400.2 | -89.6 ± 38.5 | 7/1 (8 nonzero) | 11.5 | 85.3 | 672.3 | -537.9 |
42
+
43
+ Block scores (champ/flat14/gated14): warmup s0-7 2314/1776/1776, panel s8-41
44
+ 1463/1442/1463, nearend s42-47 541/566/541. flat14 Wilcoxon p=0.27 (nonzero
45
+ n=43); gated14 p=0.016 (n=8). flat14 panel Δ -21.8/scen, nearend Δ +25.5/scen.
46
+
47
+ ### k=2 (922 devices)
48
+ | arm | score | delta ± SE | W/L | swaps/scen | late-d/scen | early-d/scen | warmup Δ/scen |
49
+ |---|---|---|---|---|---|---|---|
50
+ | champ | 3142.1 | — | — | 21.2 | 205.4 | 1306.2 | — |
51
+ | flat14 | 2934.6 | -207.5 ± 82.8 | 27/21 | 27.6 | 154.1 | 1937.4 | -741.3 |
52
+ | gated14 | 3054.9 | -87.2 ± 42.9 | 4/0 (4 nonzero) | 21.6 | 195.8 | 1324.9 | -523.0 |
53
+
54
+ Blocks: warmup 3473/2732/2950, panel 3426/3284/3426, nearend 1092/1221/1092.
55
+ flat14 Wilcoxon p=0.092; panel Δ -141.3/scen, nearend Δ +129.4/scen.
56
+
57
+ ### k=4 (1844 devices)
58
+ | arm | score | delta ± SE | W/L | swaps/scen | late-d/scen | early-d/scen | warmup Δ/scen |
59
+ |---|---|---|---|---|---|---|---|
60
+ | champ | 7053.4 | — | — | 39.5 | 480.4 | 2602.2 | — |
61
+ | flat14 | 6458.8 | -594.6 ± 167.9 | 26/22 | 52.2 | 356.1 | 3910.1 | -2080.1 |
62
+ | gated14 | 7053.4 | 0.0 (identical) | 0/0 | 39.5 | 480.4 | 2602.2 | 0.0 |
63
+
64
+ Blocks: warmup 8433/6353/8433, panel 7609/7169/7609, nearend 2062/2573/2062.
65
+ flat14 Wilcoxon p=0.0077; panel Δ -440.0/scen, nearend Δ +510.6/scen.
66
+
67
+ ## Gate-closure trajectory (the KEY question)
68
+
69
+ The gate closes strictly earlier as the fleet grows — deaths accumulate ~k
70
+ times faster while the threshold stays at 20:
71
+
72
+ | k | deaths_seen at s_0 | gate-active scenarios | gate closes | gated-vs-champ Δ (all-48 mean) |
73
+ |---|---|---|---|---|
74
+ | 1 | 3 | 8 (s_0-s_7) | s_8 (seen 20) | -89.6 ± 38.5 |
75
+ | 2 | 10 | 4 (s_0-s_3) | s_4 (seen 25) | -87.2 ± 42.9 |
76
+ | 4 | 21 | 0 (never opens) | pre-s_0 | 0.0 (byte-identical plans) |
77
+
78
+ Per-scenario gated deltas on gate-active origins — k=1: s_0 +145.2, s_1
79
+ -820.5, s_2 -215.5, s_3 -683.3, s_4 -754.4, s_5 -439.1, s_6 -1306.2, s_7
80
+ -229.0 (7W/1L). k=2: s_0 -1357.9, s_1 -864.5, s_2 -1000.3, s_3 -961.1
81
+ (4W/0L). Outside the gate window gated14 reproduces champ exactly at every
82
+ scale (identical alpha on identical predictions => identical plans).
83
+
84
+ ## Verdict
85
+
86
+ - **gated14: STABLE, with downside bounded by construction — converges to
87
+ champ at large fleets.** The gate is doing exactly what it was designed to
88
+ do: it fires only in the genuinely count-starved era, that era shrinks as
89
+ the fleet grows (8 -> 4 -> 0 scenarios at 1x/2x/4x), and at 4x gated14 IS
90
+ the shipped champ (zero exposure). Where it does fire it captures most of
91
+ flat14's warmup gain (7W/1L and 4W/0L; single worst scenario +145) while
92
+ taking none of flat14's panel/nearend exposure.
93
+ - **flat14: mean-IMPROVING with scale, but tail-driven and regime-skewed.**
94
+ Per-device delta grows (-0.221 -> -0.225 -> -0.322 per device; Wilcoxon p
95
+ 0.27 -> 0.092 -> 0.0077), and at scale the gain spreads from warmup into
96
+ the panel block (-21.8 -> -141.3 -> -440.0/scen). But the NEAREND block
97
+ degrades just as systematically (+25.5 -> +129.4 -> +510.6/scen), W/L
98
+ stays ~even at every scale (20/23, 27/21, 26/22), and it buys the gain
99
+ with +32% swaps and +50% early-days at 4x. Its mean edge is real in this
100
+ harness but concentrated in a minority of high-prevalence origins — a
101
+ higher-variance bet, not a uniform dominance.
102
+ - Interpretation for the ship decision: the scaling axis does NOT expose a
103
+ hidden failure mode in gated14 — its worst case at any scale is "equals
104
+ champ". flat14's late-era over-inclusion (nearend +510/scen at 4x) is the
105
+ scale-amplified liability the bgkf mean hides.
106
+
107
+ **Caveats.** Derived-EOL harness (ghost low-voltage clones shared equally by
108
+ all arms), template travel matrix, single seed (7) for clone shifts; the
109
+ harness overstates absolute deltas relative to bgkf even at k=1 (-102 here vs
110
+ -39.4 on bgkf for flat14), so read trajectories, not magnitudes. Per the
111
+ tiered adoption bar this is a report-only robustness layer, not an adoption
112
+ adjudication.
113
+
114
+ **Artifacts.** /tmp/c21/{synth.py,scale3.py,summarize.py},
115
+ /tmp/c21/scale_k{1,2,4}_all.csv, /tmp/c21/fleet_k{1,2,4}.pkl,
116
+ logs /tmp/c21/{synth,run}_k*.log. Repo artifacts untouched.
batteryswap_example/models_cv.py CHANGED
@@ -1455,7 +1455,12 @@ def plan_tsroute(pred, included, locations, start_time, day_q,
1455
  # (the harness itself drops eol <= origin devices) and estimate the
1456
  # coming window's prevalence without trusting the probability scale.
1457
  psum = max(psum, plan_tsroute.floor_scale * plan_tsroute.recent_deaths)
1458
- n = int(round(plan_tsroute.alpha * psum))
 
 
 
 
 
1459
  order_col = 'rank_score' if 'rank_score' in pred.columns else 'p42'
1460
  chosen = pred[order_col].nlargest(n).index
1461
  if plan_tsroute.p_be > 0:
@@ -1531,6 +1536,7 @@ def plan_tsroute(pred, included, locations, start_time, day_q,
1531
  plan_tsroute.alpha = 0.8
1532
  plan_tsroute.split_days = 0
1533
  plan_tsroute.timing_weight = 1.0 # scales timing curves in optimize_days' objective
 
1534
  plan_tsroute.p_be = 0.0
1535
  plan_tsroute.floor_day = 0
1536
  plan_tsroute.p_conf = 0.5
 
1455
  # (the harness itself drops eol <= origin devices) and estimate the
1456
  # coming window's prevalence without trusting the probability scale.
1457
  psum = max(psum, plan_tsroute.floor_scale * plan_tsroute.recent_deaths)
1458
+ # consume-once appetite boost: armed by the model's predict (T3 warmup
1459
+ # gate) for the plan that immediately follows, then self-resets so a
1460
+ # fallback path can never inherit a stale boost
1461
+ boost = plan_tsroute.alpha_boost
1462
+ plan_tsroute.alpha_boost = 1.0
1463
+ n = int(round(plan_tsroute.alpha * boost * psum))
1464
  order_col = 'rank_score' if 'rank_score' in pred.columns else 'p42'
1465
  chosen = pred[order_col].nlargest(n).index
1466
  if plan_tsroute.p_be > 0:
 
1536
  plan_tsroute.alpha = 0.8
1537
  plan_tsroute.split_days = 0
1538
  plan_tsroute.timing_weight = 1.0 # scales timing curves in optimize_days' objective
1539
+ plan_tsroute.alpha_boost = 1.0 # consume-once; see the count line above
1540
  plan_tsroute.p_be = 0.0
1541
  plan_tsroute.floor_day = 0
1542
  plan_tsroute.p_conf = 0.5
batteryswap_example/submit.py CHANGED
@@ -253,6 +253,47 @@ def _gap_features(frame):
253
  return out
254
 
255
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
256
  class Ft2RUL:
257
  """Faithful deployment of cv.run_bgkf(kind='logbag', variant='vm7',
258
  blend=('wiener', 0.20)): primary = SklearnRUL('logbag') fit with
@@ -292,6 +333,28 @@ class Ft2RUL:
292
  max_depth=2, learning_rate=0.06, max_iter=300,
293
  l2_regularization=1.0, random_state=0
294
  ).fit(X[self._gap_cols], train['dies42'])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
295
  return self
296
 
297
  def predict(self, rows):
@@ -304,9 +367,28 @@ class Ft2RUL:
304
  rows = rows.copy()
305
  rows['vmin7_rank'] = (rows['v_min_7'].rank(pct=True)
306
  if 'v_min_7' in rows.columns else np.nan)
 
 
 
 
 
 
307
  g = _gap_features(rows) if self.gap is not None else None
308
  with self._swap():
309
  pred = self.primary.predict(rows)
 
 
 
 
 
 
 
 
 
 
 
 
 
310
  rk = pred['p42'].rank(pct=True)
311
  w3 = FT2_GAPW if g is not None else 0.0
312
  # round the derived primary weight: 1-0.30-0.20 is 0.4999...94 in
@@ -390,6 +472,24 @@ TIMING_WEIGHT = float(os.environ.get('BATTERYSWAP_TIMING_WEIGHT', '2.0'))
390
  # the champion. Warm-up CV: 4256 -> 2380 at k=2.5, winning 8/8 origins.
391
  FLOOR_SCALE = float(os.environ.get('BATTERYSWAP_FLOOR', '2.5'))
392
  FLOOR_GATE = int(os.environ.get('BATTERYSWAP_FLOOR_GATE', '20'))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
393
  # Phase 28: cliff-gated appetite for the static held-out path. The static
394
  # foreign fit undercounts exactly where a knee is already visible in the raw
395
  # hourly readings (>=35% of the last 7 days' readings below 2.45 V, >=20
@@ -703,6 +803,9 @@ def adaptive_split_plans(split, split_path, log=print, deadline=None):
703
  log(f' WARNING {scenario["name"]}: ft2 wiener params failed '
704
  f'({e}); wiener source dropped this scenario')
705
  feats = feats.reindex(locs['battery'])
 
 
 
706
 
707
  pred = model.predict(feats)
708
  if static_model is not None and cliff_counts is not None:
 
253
  return out
254
 
255
 
256
+ _HZ_BUCKETS = [(0, 7), (7, 14), (14, 21), (21, 28), (28, 42), (42, 63), (63, 84)]
257
+
258
+
259
+ class _DiscHaz:
260
+ """Discrete-time hazard HGB on person-period (pair, bucket) rows from
261
+ the censored pool of whatever pair table fit() receives (durations
262
+ censored at its last origin); order source P(die<=42d) = 1 - prod(1-h_k).
263
+ Censoring is what the plain classifiers can't see - this model's edge is
264
+ cold-start origins, which is the only place the warmup gate lets it act."""
265
+
266
+ def fit(self, train):
267
+ pool = models_cv.censored_pairs(train, train['origin'].max())
268
+ self.cols = list(models_cv.FEATURES)
269
+ dur = pool['duration'].values
270
+ ev = pool['event'].values.astype(bool)
271
+ Xb = pool[self.cols].values.astype(float)
272
+ Xs, ys, mids = [], [], []
273
+ for lo, hi in _HZ_BUCKETS:
274
+ died = ev & (dur > lo) & (dur <= hi)
275
+ use = (dur >= hi) | died
276
+ Xs.append(Xb[use])
277
+ ys.append(died[use])
278
+ mids.append(np.full(int(use.sum()), (lo + hi) / 2.0))
279
+ from sklearn.ensemble import HistGradientBoostingClassifier
280
+ self.clf = HistGradientBoostingClassifier(
281
+ max_depth=3, learning_rate=0.06, max_iter=250,
282
+ l2_regularization=1.0, random_state=0, early_stopping=False,
283
+ ).fit(np.column_stack([np.vstack(Xs), np.concatenate(mids)]),
284
+ np.concatenate(ys))
285
+ return self
286
+
287
+ def predict(self, rows):
288
+ Xb = rows[self.cols].values.astype(float)
289
+ n = len(rows)
290
+ surv = np.ones(n)
291
+ for lo, hi in _HZ_BUCKETS[:5]: # buckets covering 0-42d
292
+ X = np.column_stack([Xb, np.full(n, (lo + hi) / 2.0)])
293
+ surv = surv * (1.0 - self.clf.predict_proba(X)[:, 1])
294
+ return pd.DataFrame({'p42': 1.0 - surv}, index=rows.index)
295
+
296
+
297
  class Ft2RUL:
298
  """Faithful deployment of cv.run_bgkf(kind='logbag', variant='vm7',
299
  blend=('wiener', 0.20)): primary = SklearnRUL('logbag') fit with
 
333
  max_depth=2, learning_rate=0.06, max_iter=300,
334
  l2_regularization=1.0, random_state=0
335
  ).fit(X[self._gap_cols], train['dies42'])
336
+ self.hazard, self.dev_eol = None, None
337
+ if ('eol' not in train.columns and 'days_to_eol' in train.columns
338
+ and 'origin' in train.columns):
339
+ # the cached parquet ships days_to_eol but not eol (cv.load_table
340
+ # adds it at load time); the derivation is exact (verified 0 diff
341
+ # on all 8206 dying rows)
342
+ train = train.copy()
343
+ train['eol'] = train['origin'] + pd.to_timedelta(
344
+ train['days_to_eol'], unit='D')
345
+ if WARMUP_RATE > 0 and 'eol' in train.columns and 'device_id' in train.columns:
346
+ try:
347
+ self._gate_thr = WARMUP_RATE * train['device_id'].nunique()
348
+ self.dev_eol = (train[['device_id', 'eol']].dropna()
349
+ .drop_duplicates('device_id')
350
+ .set_index('device_id')['eol'])
351
+ with self._swap():
352
+ self.hazard = _DiscHaz().fit(train)
353
+ except Exception:
354
+ # warmup gate is an overlay: a broken hazard fit degrades to
355
+ # the plain (previous-head) behavior, never to a crash
356
+ traceback.print_exc()
357
+ self.hazard = None
358
  return self
359
 
360
  def predict(self, rows):
 
367
  rows = rows.copy()
368
  rows['vmin7_rank'] = (rows['v_min_7'].rank(pct=True)
369
  if 'v_min_7' in rows.columns else np.nan)
370
+ gate = False
371
+ if (self.hazard is not None and self.dev_eol is not None
372
+ and 'origin' in rows.columns):
373
+ # T3 warmup gate: TRAIN-ledger deaths recorded <= this plan origin
374
+ seen = int((self.dev_eol <= rows['origin'].max()).sum())
375
+ gate = seen < self._gate_thr
376
  g = _gap_features(rows) if self.gap is not None else None
377
  with self._swap():
378
  pred = self.primary.predict(rows)
379
+ if gate:
380
+ try:
381
+ rk = self.hazard.predict(rows)['p42'].rank(pct=True)
382
+ models_cv.plan_tsroute.alpha_boost = WARMUP_ALPHA
383
+ vals = np.sort(pred['p42'].values)[::-1]
384
+ out = pred.copy()
385
+ out['p42'] = pd.Series(
386
+ vals, index=rk.sort_values(ascending=False).index
387
+ ).reindex(out.index)
388
+ return out
389
+ except Exception:
390
+ traceback.print_exc()
391
+ models_cv.plan_tsroute.alpha_boost = 1.0
392
  rk = pred['p42'].rank(pct=True)
393
  w3 = FT2_GAPW if g is not None else 0.0
394
  # round the derived primary weight: 1-0.30-0.20 is 0.4999...94 in
 
472
  # the champion. Warm-up CV: 4256 -> 2380 at k=2.5, winning 8/8 origins.
473
  FLOOR_SCALE = float(os.environ.get('BATTERYSWAP_FLOOR', '2.5'))
474
  FLOOR_GATE = int(os.environ.get('BATTERYSWAP_FLOOR_GATE', '20'))
475
+ # T3 warmup gate (2026-08-23 night, user CV-first directive): while the
476
+ # TRAINING fleet's ledger shows < WARMUP_GATE recorded deaths at the plan
477
+ # origin (plan-time observable, FLOOR_GATE family; the full train ledger
478
+ # crosses 35 at s_15), the ORDER switches to a censored-pool discrete-time
479
+ # hazard HGB and the appetite is boosted x WARMUP_ALPHA via a consume-once
480
+ # plan_tsroute.alpha_boost. bgkf: gated alpha 1.4 alone -125.4 (4.0sigma,
481
+ # width bowl clean 6..48); + gated hazard order -143.3 (3.8sigma, 16W/0L,
482
+ # Wilcoxon <.001) = CV 1398.0 vs the 1541.3 head. Scale harness: the gate
483
+ # narrows as fleets grow (8/4/0 gate-active scenarios at 1x/2x/4x) so the
484
+ # downside is bounded - on fleets past the gate this is byte-identical to
485
+ # the previous head. WARMUP_GATE=0 disables both channels exactly.
486
+ # The gate threshold is a RATE (deaths per training-fleet device), anchored
487
+ # at 35 deaths / 461 devices: a fixed count fires wider on the CV's 4/5-size
488
+ # fold ledgers than on the full deployment ledger (measured: fixed 35 ->
489
+ # 22 changed origins, 15W/7L, -111.6; rate form matches the width-16 bowl
490
+ # optimum on both). WARMUP_RATE=0 disables the gate entirely.
491
+ WARMUP_RATE = float(os.environ.get('BATTERYSWAP_WARMUP_RATE', '0.0759'))
492
+ WARMUP_ALPHA = float(os.environ.get('BATTERYSWAP_WARMUP_ALPHA', '1.4'))
493
  # Phase 28: cliff-gated appetite for the static held-out path. The static
494
  # foreign fit undercounts exactly where a knee is already visible in the raw
495
  # hourly readings (>=35% of the last 7 days' readings below 2.45 V, >=20
 
803
  log(f' WARNING {scenario["name"]}: ft2 wiener params failed '
804
  f'({e}); wiener source dropped this scenario')
805
  feats = feats.reindex(locs['battery'])
806
+ # plan-origin timestamp for the T3 warmup gate (the cv table carries
807
+ # this column natively; the split pipeline adds it here)
808
+ feats['origin'] = T
809
 
810
  pred = model.predict(feats)
811
  if static_model is not None and cliff_counts is not None: