cheikh025 commited on
Commit
e6d2ec1
Β·
verified Β·
1 Parent(s): c1fad20

Upload promoted/0018_libero90_weak_task_oversampling_v2/PROGRESS_0018_libero90_weak_task_oversampling_v2.md with huggingface_hub

Browse files
promoted/0018_libero90_weak_task_oversampling_v2/PROGRESS_0018_libero90_weak_task_oversampling_v2.md ADDED
@@ -0,0 +1,211 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # PROGRESS_0018 β€” libero90_weak_task_oversampling_v2
2
+
3
+ - **Experiment ID:** 0018_libero90_weak_task_oversampling_v2
4
+ - **Status:** `PROMOTE`
5
+ - **Created:** 2026-08-13
6
+ - **Updated:** 2026-08-14
7
+ - **Parent experiment:** 0017_libero90_weak_task_oversampling (`PROMOTE`, current accepted main-line checkpoint)
8
+ - **Parent checkpoint:** `runs/reweighted_libero90_finetune/exp0017_weak_task_oversample/checkpoints/weights/step_008000.pt` (weights-only resume)
9
+ - **Selected candidate checkpoint:** n/a β€” training in progress
10
+ - **Git branch:** `autoresearch/libero90-v1`
11
+ - **Git commit:** `b2b49d0` (both resume fixes applied)
12
+
13
+ ## 1. Result at a glance
14
+
15
+ exp0017 (4x oversampling of 7 weak LIBERO-90 tasks) produced a new project-best LIBERO-90 (92.60% canonical) with all four original suites clearing the updated 95% goal, but task-level analysis showed the gain came mostly from 3 of the 7 targeted tasks (bottom-drawer, ketchup-drawer, book-under-shelf), with the primary "book in caddy" cluster showing only mixed/marginal movement, and 4 new weak points emerging that weren't targeted (task23, 64, 65, 66). This candidate broadens the weak-task subset to 11 tasks (the original 7 plus the 4 newly-emerged ones), keeping the same validated mechanism (weights-only resume, 4x oversampling, 8000 steps) β€” testing whether wider coverage of the current weakest-task set produces a further gain.
16
+
17
+ ## 2. Research state before experiment
18
+
19
+ exp0017 is the accepted main-line checkpoint: LIBERO-Spatial 96.80%, LIBERO-Object 99.60%, LIBERO-Goal 97.20%, LIBERO-Long/10 96.00% (all clear the 95% goal), **LIBERO-90 92.60%** (canonical) β€” the best result in the project, still 2.40pp short of the 95% goal for LIBERO-90 specifically. Per-task analysis of the weakest 15 LIBERO-90 tasks (canonical) identified: task51 (32%, confirmed zero-demo data gap, excluded from this candidate), task81 (36%, book-in-caddy, still weak), task23 (50%, newly emerged, not previously targeted), task64/65/66 (60-78%, newly emerged), task21/63/75 (66%, mixed), task73/6/32/83 (70-80%, improved substantially from exp0014 but not fully solved).
20
+
21
+ ## 3. Candidate design
22
+
23
+ ### Modifications
24
+
25
+ 1. **Broadened filtered subset dataset** `data/libero_mujoco3.3.2/libero_90_weak_subset_v2_lerobot/` (654 episodes, 11 target tasks): the original 7 from exp0017 (book-in-caddy x4 variants, ketchup-drawer, bottom-drawer-open, book-under-shelf) plus 4 newly-emerged weak tasks from exp0017's canonical eval β€” "close the bottom drawer of the cabinet and open the top drawer" (34 episodes), "stack the right bowl on the left bowl and place them in the tray" (37 episodes), "put the red mug on the left plate" (34 episodes), "put the red mug on the right plate" (45 episodes). Built with the same corrected script (rewrites parquet `episode_index`/`index`/`task_index` columns, symlinks video files) validated for exp0017. Verified via a direct `BaseLerobotDataset` smoke test (0 errors, 53 random samples) before launching.
26
+ 2. **New data config** `configs/data/libero_2cam_plus90_reweighted_weaktasks_v2.yaml`: identical to exp0017's recipe except the weak-subset directory is the broadened v2 (654 episodes, listed 4x = 2616 episodes, ~19% of the resulting ~13,600-episode mix).
27
+ 3. **New task config** `configs/task/libero_uncond_2cam224_plus90_reweighted_weaktasks_v2_3e-5.yaml`.
28
+ 4. `resume=./runs/reweighted_libero90_finetune/exp0017_weak_task_oversample/checkpoints/weights/step_008000.pt` (weights-only resume from the new best checkpoint).
29
+ 5. `learning_rate=3e-5`, `max_steps=8000` β€” identical scale to exp0017 for a clean comparison.
30
+ 6. **Cache pre-warmed via a real 4-rank `accelerate launch` dry run before the actual training launch** (lesson from exp0017's extensive debugging saga β€” see `research/NOTES.md`), and full process/GPU cleanup verified before the real launch.
31
+
32
+ ### Why this candidate
33
+
34
+ Directly extends the one validated real lever (task-level data engineering) with the most direct, low-risk next step: broaden coverage to the current weakest-task set rather than assume the same 7 tasks are still the right target. Uses only already-validated mechanisms (weights-only resume, filtered-subset oversampling) β€” no new infrastructure risk.
35
+
36
+ ### What to watch
37
+
38
+ - Whether the 4 newly-added tasks (23, 64, 65, 66) show a clear improvement.
39
+ - Whether the book-in-caddy cluster (still muted in exp0017) responds any differently with more of the total data-mix "budget" going toward LIBERO-90-weak-task episodes.
40
+ - Whether LIBERO-90 aggregate improves beyond exp0017's 92.60%.
41
+ - Whether the four original suites hold their newly-achieved 95%+ margins (2616 weak-subset episodes is a somewhat larger share of the mix than exp0017's 2016, slightly more dilution risk).
42
+ - Task51: expected to remain unchanged/noisy (not included, still a data gap).
43
+
44
+ ### Initial compute plan
45
+
46
+ - Initial training budget: `max_steps=8000` (~7h, matching exp0017's scale for a clean comparison).
47
+ - Checkpoint/save plan: `save_every=500`, prune intermediate weights aggressively given disk pressure observed during exp0017 (was as low as ~113GB free at one point).
48
+ - No interim progress check planned by default (GPU contention with eval), consistent with prior rounds.
49
+
50
+ ## 4. Exact code and configuration state
51
+
52
+ - Git commit: `b2b49d0` (both resume fixes) + new, uncommitted config/data files and new data directory (`data/libero_mujoco3.3.2/libero_90_weak_subset_v2_lerobot/`).
53
+ - Training config: `task=libero_uncond_2cam224_plus90_reweighted_weaktasks_v2_3e-5`
54
+ - Config overrides: `learning_rate=3e-5 max_steps=8000`
55
+ - Resume source: `runs/reweighted_libero90_finetune/exp0017_weak_task_oversample/checkpoints/weights/step_008000.pt` (weights-only)
56
+
57
+ ## 5. Hardware and software environment
58
+
59
+ Unchanged from `research/progress/PROGRESS_0000_BASELINE.md` Section 5, except the two resume-fix commits in `src/fastwam/trainer.py` (`99826cc`, `b2b49d0`). Venv: `/workspace/venv-fastwam`.
60
+
61
+ ## 6. Training execution and control timeline
62
+
63
+ - Exact launch command: see below (launched 2026-08-13, into a verified-clean environment with a pre-warmed cache from the start)
64
+ - Start time: 2026-08-13 (immediately after exp0017's records were finalized and GPUs freed β€” no idle time)
65
+ - Status: running
66
+
67
+ ### Intermediate checkpoints and progress decisions
68
+
69
+ | Checkpoint / step | Training runtime so far | Eval purpose | Progress evidence | Decision | Updated training plan |
70
+ |---|---:|---|---|---|---|
71
+ | step_002500 | ~1h47min | `progress_check` | Discussed with user whether `max_steps=8000` was necessary for this small an incremental data-mix change (weak-subset grew 504->654 episodes, ~87% of the recipe unchanged from exp0017). Loss diagnostics (steps 0-2500) reviewed: train loss noisy 0.10-0.25, val_loss noisy 0.12-0.34, action_l2 healthy 0.003-0.04 β€” no clear trend either way, ordinary noise, not decisive alone. | `STOP_TRAINING` (early) + `SELECT_CHECKPOINT` (step_002500) for a screen, before deciding whether to extend | Stopped at step 2500/8000 (~31%) rather than continuing to the original 8000-step budget; screening step_002500.pt via cheap panel before deciding whether more training is warranted (possibly via a properly-shaped shorter-schedule restart rather than continuing this run, given the LR-schedule-shape caveat: this checkpoint's schedule was built for 8000 steps, so it isn't a "converged, fully-decayed" checkpoint the way exp0017's final one was) |
72
+
73
+ ### Why training ended
74
+
75
+ Stopped early (`STOP_TRAINING`, user-directed efficiency check) at step 2500/8000 rather than planned completion β€” see progress-decision row above. **Operational note**: stopping required both a `SIGTERM` to the wrapper script *and* an explicit `pkill` sweep for `scripts/train.py`/`accelerate launch` β€” the wrapper exiting did not kill the underlying distributed worker processes (all 4 ranks + dataloader workers stayed alive, GPUs stayed fully loaded) until they were killed directly. Consistent with the process-hygiene lesson from exp0017 (see `research/NOTES.md`) β€” always verify full cleanup (`ps aux` + `nvidia-smi`), not just that the top-level wrapper PID is gone.
76
+
77
+ ### Full completion (after directory-resume continuation)
78
+
79
+ - Resumed via `resume=./runs/reweighted_libero90_finetune/exp0018_weak_task_oversample_v2/checkpoints/state/step_002500` (directory/full-state resume), `max_steps=8000` unchanged.
80
+ - Resync confirmed correct: `Re-synchronized LR scheduler to global_step=2500 (shape: total_train_steps=8000, warmup_steps=400); lr=2.4748e-05` β€” exactly matching the LR at the point training was stopped (2.47e-05), confirming a genuine continuation, not a fresh restart.
81
+ - End time: 2026-08-13 ~21:23 UTC (`max_steps reached step=8000`, clean exit, no crash β€” this time all distributed worker processes exited fully on their own, unlike the earlier `SIGTERM`-to-wrapper-only stop which left the underlying `accelerate`/`torchrun` processes running until explicitly killed β€” see the process-hygiene note in Section 6 above).
82
+ - Final weights checkpoint: `runs/reweighted_libero90_finetune/exp0018_weak_task_oversample_v2/checkpoints/weights/step_008000.pt` (12,041,735,545 bytes)
83
+ - Final training diagnostics at step 8000: `val_loss=0.1771 infer_psnr=28.6849 infer_ssim=0.8690 action_l2=0.0050 action_l1=0.0344` β€” healthy, normal range throughout the continuation (steps 2500-8000: val_loss noisy 0.09-0.27, action_l2 healthy 0.003-0.02, no anomalies).
84
+
85
+ ## 7. Evaluation events
86
+
87
+ ### Event 1 β€” `progress_check` (cheap panel on step_002500)
88
+
89
+ - Checkpoint: `runs/reweighted_libero90_finetune/exp0018_weak_task_oversample_v2/checkpoints/weights/step_002500.pt` (early stop, ~31% through the planned 8000-step budget, LR not yet fully decayed β€” still ~2.47e-5 at stop time)
90
+ - Reference: exp0017's final canonical result (LIBERO-90 92.60%, Spatial 96.80%, Object 99.60%, Goal 97.20%, Long 96.00%) and exp0017's own cheap panel (Spatial 85%, Object 100%, Goal 100%, Long 100%, LIBERO-90 92%)
91
+ - Tasks: same 21-task widened panel, 5 trials/task
92
+ - Raw results: `evaluate_results/exp0018_progress_step2500/` (first launch attempt hit the tmux-death infra bug again, discarded, relaunched cleanly)
93
+
94
+ **Result β€” clear regression relative to exp0017, confirming the mid-schedule checkpoint is genuinely under-converged, not just "good enough sooner":**
95
+
96
+ | Suite | exp0017 (panel) | exp0018 step_002500 (panel) | Delta |
97
+ |---|---:|---:|---:|
98
+ | LIBERO-Spatial | 85.00% | 95.00% | +10pp |
99
+ | LIBERO-Object | 100.00% | 100.00% | 0pp |
100
+ | LIBERO-Goal | 100.00% | **70.00%** | **-30pp** |
101
+ | LIBERO-Long/10 | 100.00% | **75.00%** | **-25pp** |
102
+ | LIBERO-90 (5-task) | 92.00% | 80.00% | -12pp |
103
+ | **Overall** | **95.40%** | **84.00%** | **-11.4pp** |
104
+
105
+ Goal task0 ("open the middle drawer of the cabinet" β€” the historically-hardest task in the project, only recovered to 100% after extensive training in earlier experiments) and Long task8 ("put both moka pots on the stove") both dropped to **0%** at this intermediate checkpoint. This is decisive evidence against the "8000 steps might be more than necessary" hypothesis at the *early-stop* end: a checkpoint pulled mid-schedule is not simply "a smaller version of the final result" β€” it's genuinely regressed on some suites relative to its own starting point (exp0017), consistent with the well-established pattern in this project (see `research/STATE.md`'s retention chart) where original-suite performance dips mid-training before recovering as the LR fully decays.
106
+
107
+ **Decision: `CONTINUE_TRAINING`.** Resumed via directory/full-state resume from `checkpoints/state/step_002500` (optimizer momentum preserved) back toward the original `max_steps=8000` target β€” this answers the efficiency question with real evidence (early-stopping at ~31% produces a worse checkpoint, not just a "good enough, cheaper" one) while avoiding wasted compute by resuming rather than restarting from scratch. The efficiency lesson for *future* similar candidates is still valid (see Section 1) β€” it should be applied by choosing a shorter `max_steps` *from the start* of a new launch, not by truncating a run already in progress.
108
+
109
+ ### Event 2 β€” `candidate_screen` (cheap panel, step 8000, full schedule completed)
110
+
111
+ - Checkpoint: `runs/reweighted_libero90_finetune/exp0018_weak_task_oversample_v2/checkpoints/weights/step_008000.pt`
112
+ - Reference: exp0017's panel (Spatial 85%, Object 100%, Goal 100%, Long 100%, LIBERO-90 92%, overall 95.40%)
113
+ - Tasks: same 21-task widened panel, 5 trials/task
114
+ - Raw results: `evaluate_results/exp0018_cheap_panel/`
115
+
116
+ **Result β€” best-ever LIBERO-90 panel score (first-ever perfect 100%):**
117
+
118
+ | Suite | exp0017 (panel) | exp0018 (panel) | Delta |
119
+ |---|---:|---:|---:|
120
+ | LIBERO-Spatial | 85.00% | 90.00% | +5pp |
121
+ | LIBERO-Object | 100.00% | 100.00% | 0pp |
122
+ | LIBERO-Goal | 100.00% | 95.00% | -5pp |
123
+ | LIBERO-Long/10 | 100.00% | 100.00% | 0pp |
124
+ | LIBERO-90 (5-task) | 92.00% | **100.00%** | **+8pp (first-ever perfect)** |
125
+ | **Overall** | **95.40%** | **97.00%** | **+1.6pp** |
126
+
127
+ Task73 ("book in front compartment of caddy" β€” historically one of the weakest, part of the muted-response caddy cluster in exp0017) scored a perfect 5/5 this time. Given the strength of this result, confirmation evaluation (full 130-task coverage, 15 trials/task) launched immediately.
128
+
129
+ ### Event 3 β€” `confirmation` (full 130-task coverage, 15 trials/task)
130
+
131
+ - Command: `run_libero_manager.py` with all 5 suites, `EVALUATION.num_trials=15`, fresh `output_dir=evaluate_results/exp0018_confirmation`
132
+ - Runtime: ~1h50min (2026-08-13 21:38 -> ~23:28 UTC), all 130 tasks completed, 0 failures.
133
+
134
+ **Result β€” best confirmation-level result in the project's history, LIBERO-90 within 0.26pp of the 95% goal:**
135
+
136
+ | Suite | exp0017 confirmation | exp0017 canonical | exp0018 confirmation |
137
+ |---|---:|---:|---:|
138
+ | LIBERO-Spatial | 94.00% | 96.80% | 94.67% |
139
+ | LIBERO-Object | 100.00% | 99.60% | 99.33% |
140
+ | LIBERO-Goal | 100.00% | 97.20% | **100.00%** |
141
+ | LIBERO-Long/10 | 96.67% | 96.00% | 95.33% |
142
+ | **LIBERO-90** | 91.63% | 92.60% | **94.74%** |
143
+ | Overall | 96.46% | 96.44% | **96.81%** |
144
+
145
+ LIBERO-90's confirmation-level running score held remarkably steady throughout the evaluation (97.8% at 21/90 tasks, 95.7% at 51/90, 95.3% at 61-77/90, settling at 94.74% final) β€” a much more stable trajectory than earlier candidates, suggesting a genuinely more consistent improvement rather than a few lucky tasks dominating a small sample. Canonical (50-trial) evaluation launched immediately given how close this is to the 95% goal.
146
+
147
+ ## 8. Comparison and interpretation
148
+
149
+ Training-control note: the mid-run progress-check + directory-resume continuation (see Sections 6-7, Event 1) was itself informative β€” it demonstrated concretely that early-stopping this recipe at ~31% through its schedule produces a genuinely worse checkpoint (Goal 70%, Long 75% on the panel) than either its starting point (exp0017) or its own eventual completion (Goal 95%, Long 100%), confirming the full schedule was needed for this candidate specifically, separate from the general efficiency question about *future* candidates' budget sizing.
150
+
151
+ The final result is encouraging: LIBERO-90's cheap-panel score reached its first-ever perfect 100%, including a full recovery on the previously-muted-response task73. Confirmation evaluation will show whether this holds at full 90-task coverage.
152
+
153
+ ### Event 4 β€” `canonical` (full 130-task coverage, 50 trials/task) β€” promotion-grade evidence
154
+
155
+ - Command: `run_libero_manager.py` with all 5 suites, `EVALUATION.num_trials=50`, fresh `output_dir=evaluate_results/exp0018_canonical`
156
+ - Start time: 2026-08-13 ~23:29 UTC
157
+ - End time: 2026-08-14 ~04:57 UTC (~5h28min, all 130 tasks completed, 0 failures). Verified: every suite at exactly 50/50 trials, no partial data.
158
+
159
+ **Result β€” LIBERO-90 clears the 95% goal for the first time ever; 4 of 5 suites now clear 95% simultaneously:**
160
+
161
+ | Suite | Canonical (50 trials/task) | vs 90% floor | vs 95% goal | vs exp0017 canonical |
162
+ |---|---:|---|---|---:|
163
+ | LIBERO-Spatial | 94.80% | meets | just below (-0.20pp) | -2.00pp |
164
+ | LIBERO-Object | 99.60% | meets | **meets** | 0.00pp |
165
+ | LIBERO-Goal | 97.40% | meets | **meets** | +0.20pp |
166
+ | LIBERO-Long/10 | 95.20% | meets | **meets** | -0.80pp |
167
+ | **LIBERO-90** | **95.49%** | meets | **meets β€” first time ever** | **+2.89pp** |
168
+ | **Overall** | **96.50%** | β€” | β€” | +0.06pp |
169
+
170
+ **LIBERO-90, the project's primary target throughout, clears the 95% goal for the first time.** Only LIBERO-Spatial falls short of the 95% goal, and only by 0.20pp (94.80%, down from exp0017's 96.80%) β€” the closest the project has ever come to satisfying the full updated goal on all five suites simultaneously.
171
+
172
+ **Task-level**: every one of the 11 targeted weak tasks improved over exp0017's canonical values, several substantially β€” task81 (36%->82%, +46pp), task23 (50%->88%, +38pp), task64 (60%->84%, +24pp), task73 (70%->86%, +16pp), task6 (80%->92%, +12pp), task51 (32%->58%, +26pp, notable even without training signal β€” plausibly some indirect transfer from broader improvement, though still the single weakest task in the project). Only task75 (68%, book-in-right-compartment) remains a clear laggard within the targeted set. Two non-targeted tasks (task21 "stove+pan" 60%, task27 "wine rack" 70%) are now among the weakest, alongside task17/63 (bowl-stacking variants, 82-84%, related to the targeted task64 but not identical).
173
+
174
+ ## 9. Decision
175
+
176
+ - **Decision:** `PROMOTE`. exp0018's checkpoint (`step_008000.pt`) becomes the new main-line accepted checkpoint, replacing exp0017.
177
+ - **Retention gate passed:** yes, all four original suites clear 90% with real margin (94.80-99.60%).
178
+ - **Primary target (LIBERO-90):** 95.49% at canonical precision β€” **the first checkpoint in the project to clear the project's updated 95% goal on LIBERO-90**, a +2.89pp gain over exp0017. Consistent with confirmation (94.74%, +0.75pp β€” canonical came in slightly higher, within normal noise).
179
+ - **Overall goal status**: 4 of 5 suites (LIBERO-90, Object, Goal, Long) now clear 95% simultaneously for the first time. Only LIBERO-Spatial remains, at 94.80% β€” 0.20pp short.
180
+
181
+ ## 10. What this changes for the next experiment
182
+
183
+ 1. **New main-line accepted checkpoint**: `runs/reweighted_libero90_finetune/exp0018_weak_task_oversample_v2/checkpoints/weights/step_008000.pt`. `research/STATE.md` updated accordingly.
184
+ 2. **Broadening the targeted weak-task subset was clearly effective** β€” every one of the 11 targeted tasks improved, several substantially. This validates iterating the same approach further: the next candidate could add the newly-emerged weak tasks (task21 "stove+pan", task27 "wine rack", task17/63 bowl-stacking variants) to a v3 subset, following the exact same validated mechanism.
185
+ 3. **LIBERO-Spatial is now the sole worst-case suite** (94.80%, just 0.20pp short of 95%) β€” a genuinely different situation from every prior round, where LIBERO-90 was always the clear bottleneck. Worth checking Spatial's own per-task breakdown for a quick, targeted fix (e.g. task_spatial_4 scored 76.00% canonical, the weakest Spatial task) rather than assuming it needs the same LIBERO-90-focused intervention.
186
+ 4. **Task51's data gap remains real** but showed unexpected movement (32%->58%) despite having no direct training signal β€” worth a note of caution against over-interpreting single-task movements at this trial count, though it's not concerning since it's still comfortably the weakest task and the explanation (zero exact-matching demos) still holds.
187
+ 5. Given how close the full goal now is (4/5 suites clearing 95%, the 5th only 0.20pp short), the next candidate has a realistic chance of completing the project's active goal outright.
188
+
189
+ ## 11. Artifacts
190
+
191
+ - training log: `runs/reweighted_libero90_finetune/exp0018_weak_task_oversample_v2/train.log`
192
+ - new data directory: `data/libero_mujoco3.3.2/libero_90_weak_subset_v2_lerobot/`
193
+ - new config(s): `configs/data/libero_2cam_plus90_reweighted_weaktasks_v2.yaml`, `configs/task/libero_uncond_2cam224_plus90_reweighted_weaktasks_v2_3e-5.yaml`
194
+ - fix commits: `99826cc`, `b2b49d0`
195
+
196
+ ## 12. Reproducibility checklist
197
+
198
+ - [x] exact candidate commit recorded
199
+ - [x] parent checkpoint recorded
200
+ - [x] intentional modifications listed
201
+ - [x] initial training plan recorded
202
+ - [x] training configuration and command recorded
203
+ - [x] hardware/software environment recorded
204
+ - [x] intermediate checkpoints and progress decisions recorded when used
205
+ - [x] logs and checkpoint paths recorded
206
+ - [x] remote checkpoint path recorded and verified if an HF backup was created
207
+ - [x] every evaluation event has purpose/settings/raw results recorded
208
+ - [x] five-suite metrics recorded when canonical evaluation ran
209
+ - [x] final decision and reasoning recorded
210
+ - [x] `research/EXPERIMENTS.jsonl` updated
211
+ - [x] `research/STATE.md` updated