RohitChandramouli6618 commited on
Commit
026ea26
·
1 Parent(s): 3e4a196

Update easy rollouts 3→2 everywhere: app.py scores, README

Browse files
Files changed (2) hide show
  1. README.md +2 -2
  2. server/app.py +31 -31
README.md CHANGED
@@ -203,7 +203,7 @@ Rollout 2: Memory-augmented prompt
203
  compute advantage = R2 - mean([R1])
204
  reinforce if advantage > -0.5
205
 
206
- ... repeat for N rollouts (easy=3, medium=4, hard=4)
207
  Report best grader score across all rollouts
208
  ```
209
 
@@ -257,7 +257,7 @@ cascade-containment/
257
 
258
  ├── baseline/
259
  │ ├── policy.py # LLM policy with chain-of-thought prompting
260
- │ ├── evaluator.py # GRPO episodic memory loop (easy=3, medium=4, hard=4 rollouts)
261
  │ └── run.py # CLI entry point
262
 
263
  ├── scripts/
 
203
  compute advantage = R2 - mean([R1])
204
  reinforce if advantage > -0.5
205
 
206
+ ... repeat for N rollouts (easy=2, medium=4, hard=4)
207
  Report best grader score across all rollouts
208
  ```
209
 
 
257
 
258
  ├── baseline/
259
  │ ├── policy.py # LLM policy with chain-of-thought prompting
260
+ │ ├── evaluator.py # GRPO episodic memory loop (easy=2, medium=4, hard=4 rollouts)
261
  │ └── run.py # CLI entry point
262
 
263
  ├── scripts/
server/app.py CHANGED
@@ -525,7 +525,7 @@ body{font-family:var(--mono);background:var(--bg);color:var(--text);min-height:1
525
  <div class="brand-icon">🦠</div>
526
  <div>
527
  <div class="brand-name">Cascade Containment</div>
528
- <div class="brand-sub">OpenEnv Benchmark &middot; Meta PyTorch Hackathon × SST 2026 &middot; LLM avg 76.2%</div>
529
  </div>
530
  </div>
531
  <div class="header-right">
@@ -568,8 +568,8 @@ body{font-family:var(--mono);background:var(--bg);color:var(--text);min-height:1
568
  </div>
569
  <div class="hero-stats">
570
  <div class="hs-item"><div class="hs-val">3</div><div class="hs-label">Task levels</div></div>
571
- <div class="hs-item"><div class="hs-val">76.2%</div><div class="hs-label">LLM+GRPO avg</div></div>
572
- <div class="hs-item"><div class="hs-val">+37pp</div><div class="hs-label">vs greedy</div></div>
573
  </div>
574
  </div>
575
 
@@ -763,25 +763,25 @@ body{font-family:var(--mono);background:var(--bg);color:var(--text);min-height:1
763
  <div style="font-size:0.82rem;font-weight:700;color:#fff;">LLM + GRPO Agent (Llama 3.3 70B)</div>
764
  </div>
765
  <div style="font-size:0.72rem;color:var(--muted);margin-bottom:1rem;line-height:1.7;">
766
- Llama 3.3 70B via Groq, with GRPO-style episodic memory across 3&ndash;4 rollouts per task.
767
  Each rollout injects advantage-gated memory from prior rollouts into the prompt.
768
  Reproduced by running <code style="color:var(--text);">python baseline/run.py</code>.
769
  Total runtime: <strong style="color:var(--text);">19.8 minutes</strong>.
770
  </div>
771
  <div class="grid-3" style="margin-bottom:1rem;">
772
  <div class="card-sm" style="border-top:2px solid var(--green);">
773
- <div style="font-size:0.62rem;letter-spacing:0.08em;text-transform:uppercase;color:var(--green);margin-bottom:0.5rem;">Easy &middot; 3 rollouts</div>
774
- <div style="font-family:var(--serif);font-size:2rem;font-weight:700;color:#fff;line-height:1;">90.8%</div>
775
- <div style="font-size:0.68rem;color:var(--muted);margin-top:0.25rem;">Best of 3 &middot; no breach</div>
776
  <div style="margin-top:0.75rem;display:flex;flex-direction:column;gap:0.3rem;">
777
  <div style="display:flex;justify-content:space-between;font-size:0.68rem;"><span style="color:var(--muted);">Containment</span><span>100%</span></div>
778
  <div style="display:flex;justify-content:space-between;font-size:0.68rem;"><span style="color:var(--muted);">Hospital</span><span>100%</span></div>
779
- <div style="display:flex;justify-content:space-between;font-size:0.68rem;"><span style="color:var(--muted);">Efficiency</span><span>86%</span></div>
780
  </div>
781
  </div>
782
  <div class="card-sm" style="border-top:2px solid var(--amber);">
783
  <div style="font-size:0.62rem;letter-spacing:0.08em;text-transform:uppercase;color:var(--amber);margin-bottom:0.5rem;">Medium &middot; 4 rollouts</div>
784
- <div style="font-family:var(--serif);font-size:2rem;font-weight:700;color:#fff;line-height:1;">71.6%</div>
785
  <div style="font-size:0.68rem;color:var(--muted);margin-top:0.25rem;">Best of 4 &middot; no breach</div>
786
  <div style="margin-top:0.75rem;display:flex;flex-direction:column;gap:0.3rem;">
787
  <div style="display:flex;justify-content:space-between;font-size:0.68rem;"><span style="color:var(--muted);">Containment</span><span>42%</span></div>
@@ -791,7 +791,7 @@ body{font-family:var(--mono);background:var(--bg);color:var(--text);min-height:1
791
  </div>
792
  <div class="card-sm" style="border-top:2px solid var(--red);">
793
  <div style="font-size:0.62rem;letter-spacing:0.08em;text-transform:uppercase;color:var(--red);margin-bottom:0.5rem;">Hard &middot; 4 rollouts</div>
794
- <div style="font-family:var(--serif);font-size:2rem;font-weight:700;color:#fff;line-height:1;">66.1%</div>
795
  <div style="font-size:0.68rem;color:var(--muted);margin-top:0.25rem;">Best of 4 &middot; no breach</div>
796
  <div style="margin-top:0.75rem;display:flex;flex-direction:column;gap:0.3rem;">
797
  <div style="display:flex;justify-content:space-between;font-size:0.68rem;"><span style="color:var(--muted);">Containment</span><span>51%</span></div>
@@ -806,18 +806,18 @@ body{font-family:var(--mono);background:var(--bg);color:var(--text);min-height:1
806
  <tr><th>Task</th><th>Rollout 1</th><th>Rollout 2</th><th>Rollout 3</th><th>Rollout 4</th><th>Best</th></tr>
807
  <tr>
808
  <td><span class="badge badge-green">Easy</span></td>
809
- <td>90.8%</td><td>85.5%</td><td>90.0%</td><td>&mdash;</td>
810
- <td><strong style="color:var(--green);">90.8%</strong></td>
811
  </tr>
812
  <tr>
813
  <td><span class="badge badge-amber">Medium</span></td>
814
- <td>58.0%</td><td>65.4%</td><td>71.6%</td><td>65.5%</td>
815
- <td><strong style="color:var(--amber);">71.6%</strong></td>
816
  </tr>
817
  <tr>
818
  <td><span class="badge badge-red">Hard</span></td>
819
- <td>59.0%</td><td>58.4%</td><td>66.1%</td><td>64.0%</td>
820
- <td><strong style="color:var(--red);">66.1%</strong></td>
821
  </tr>
822
  </table>
823
  </div>
@@ -841,29 +841,29 @@ body{font-family:var(--mono);background:var(--bg);color:var(--text);min-height:1
841
  <tr><th>Task</th><th>Greedy (D0)</th><th>LLM+GRPO</th><th>Lift (&Delta;)</th><th>Signal</th><th>Exploit Risk</th></tr>
842
  <tr>
843
  <td><span class="badge badge-green">Easy</span></td>
844
- <td style="color:var(--muted);">42.8%</td><td><strong>90.8%</strong></td>
845
- <td class="pos">+48pp</td>
846
  <td><span class="badge badge-green">Strong</span></td>
847
  <td><span class="badge badge-green">None &mdash; 0% eff, 60% breach</span></td>
848
  </tr>
849
  <tr>
850
  <td><span class="badge badge-amber">Medium</span></td>
851
- <td style="color:var(--muted);">42.7%</td><td><strong>71.6%</strong></td>
852
- <td class="pos">+29pp</td>
853
  <td><span class="badge badge-green">Strong</span></td>
854
  <td><span class="badge badge-green">None &mdash; 8% eff, 80% breach</span></td>
855
  </tr>
856
  <tr>
857
  <td><span class="badge badge-red">Hard</span></td>
858
- <td style="color:var(--muted);">33.0%</td><td><strong>66.1%</strong></td>
859
  <td class="pos">+33pp</td>
860
  <td><span class="badge badge-green">Strong</span></td>
861
  <td><span class="badge badge-green">None &mdash; 0% eff, 100% breach</span></td>
862
  </tr>
863
  <tr style="border-top:1px solid var(--border2);">
864
  <td><strong>Average</strong></td>
865
- <td style="color:var(--muted);">39.5%</td><td><strong>76.2%</strong></td>
866
- <td class="pos"><strong>+37pp</strong></td>
867
  <td><span class="badge badge-green">Strong</span></td>
868
  <td><span class="badge badge-green">No exploits found</span></td>
869
  </tr>
@@ -874,29 +874,29 @@ body{font-family:var(--mono);background:var(--bg);color:var(--text);min-height:1
874
  <div style="font-size:0.62rem;letter-spacing:0.08em;text-transform:uppercase;color:var(--muted);margin-bottom:0.75rem;">Easy</div>
875
  <div style="display:flex;flex-direction:column;gap:0.45rem;">
876
  <div><div style="display:flex;justify-content:space-between;font-size:0.68rem;margin-bottom:0.2rem;"><span style="color:var(--muted);">Greedy (D0)</span><span>43%</span></div><div class="bar-track" style="height:8px;"><div class="bar-fill" style="width:43%;background:var(--dim);"></div></div></div>
877
- <div><div style="display:flex;justify-content:space-between;font-size:0.68rem;margin-bottom:0.2rem;"><span style="color:var(--green);">LLM+GRPO</span><span>91%</span></div><div class="bar-track" style="height:8px;"><div class="bar-fill" style="width:91%;background:var(--green);"></div></div></div>
878
- <div style="font-size:0.68rem;color:var(--muted);">Lift: <span style="color:var(--green);font-weight:700;">+48pp</span></div>
879
  </div>
880
  </div>
881
  <div class="card-sm">
882
  <div style="font-size:0.62rem;letter-spacing:0.08em;text-transform:uppercase;color:var(--muted);margin-bottom:0.75rem;">Medium</div>
883
  <div style="display:flex;flex-direction:column;gap:0.45rem;">
884
  <div><div style="display:flex;justify-content:space-between;font-size:0.68rem;margin-bottom:0.2rem;"><span style="color:var(--muted);">Greedy (D0)</span><span>43%</span></div><div class="bar-track" style="height:8px;"><div class="bar-fill" style="width:43%;background:var(--dim);"></div></div></div>
885
- <div><div style="display:flex;justify-content:space-between;font-size:0.68rem;margin-bottom:0.2rem;"><span style="color:var(--amber);">LLM+GRPO</span><span>72%</span></div><div class="bar-track" style="height:8px;"><div class="bar-fill" style="width:72%;background:var(--amber);"></div></div></div>
886
- <div style="font-size:0.68rem;color:var(--muted);">Lift: <span style="color:var(--amber);font-weight:700;">+29pp</span></div>
887
  </div>
888
  </div>
889
  <div class="card-sm">
890
  <div style="font-size:0.62rem;letter-spacing:0.08em;text-transform:uppercase;color:var(--muted);margin-bottom:0.75rem;">Hard</div>
891
  <div style="display:flex;flex-direction:column;gap:0.45rem;">
892
  <div><div style="display:flex;justify-content:space-between;font-size:0.68rem;margin-bottom:0.2rem;"><span style="color:var(--muted);">Greedy (D0)</span><span>33%</span></div><div class="bar-track" style="height:8px;"><div class="bar-fill" style="width:33%;background:var(--dim);"></div></div></div>
893
- <div><div style="display:flex;justify-content:space-between;font-size:0.68rem;margin-bottom:0.2rem;"><span style="color:var(--red);">LLM+GRPO</span><span>66%</span></div><div class="bar-track" style="height:8px;"><div class="bar-fill" style="width:66%;background:var(--red);"></div></div></div>
894
- <div style="font-size:0.68rem;color:var(--muted);">Lift: <span style="color:var(--red);font-weight:700;">+33pp</span></div>
895
  </div>
896
  </div>
897
  </div>
898
  <div style="padding:0.85rem 1.1rem;background:var(--green-dim);border:1px solid rgba(61,214,140,0.2);border-radius:8px;font-size:0.72rem;color:var(--text);line-height:1.8;">
899
- &#10003; <strong>Variance check passed.</strong> Mean lift of <strong>+37pp</strong> across all tasks.
900
  Greedy (D0) scores 33&ndash;43% with 60&ndash;100% hospital breach rates &mdash; confirming no trivial exploit path.
901
  LLM+GRPO reaches 66&ndash;91% with zero breaches &mdash; genuine triage reasoning is required and rewarded.
902
  </div>
@@ -1086,7 +1086,7 @@ body{font-family:var(--mono);background:var(--bg);color:var(--text);min-height:1
1086
  <div class="arch-card"><div class="arch-icon">📊</div><div class="arch-name">grader.py</div><div class="arch-desc">Deterministic trajectory scorer. Reads hidden CityState. Four components weighted into final_score ∈ [0.0, 1.0]. Zero LLM calls.</div></div>
1087
  <div class="arch-card"><div class="arch-icon">🌊</div><div class="arch-name">utils.py</div><div class="arch-desc">SIR-inspired spread model with linear spillover (no wrap-around). Observation builder enforcing partial observability by task.</div></div>
1088
  <div class="arch-card"><div class="arch-icon">🧠</div><div class="arch-name">core/trajectory.py</div><div class="arch-desc">EpisodicMemory. Stores (obs, action, reward) tuples. Retrieves top-k by L1 distance on infection profiles, phase-weighted.</div></div>
1089
- <div class="arch-card"><div class="arch-icon">🎯</div><div class="arch-name">baseline/evaluator.py</div><div class="arch-desc">GRPO-style loop. Per-task rollouts: easy=3, medium=4, hard=4. Advantage = Rᵢ − mean(R). Memory threshold -0.3. Best score reported.</div></div>
1090
  </div>
1091
 
1092
  <div class="card">
 
525
  <div class="brand-icon">🦠</div>
526
  <div>
527
  <div class="brand-name">Cascade Containment</div>
528
+ <div class="brand-sub">OpenEnv Benchmark &middot; Meta PyTorch Hackathon &times; SST 2026 &middot; LLM avg 75.7%</div>
529
  </div>
530
  </div>
531
  <div class="header-right">
 
568
  </div>
569
  <div class="hero-stats">
570
  <div class="hs-item"><div class="hs-val">3</div><div class="hs-label">Task levels</div></div>
571
+ <div class="hs-item"><div class="hs-val">75.7%</div><div class="hs-label">LLM+GRPO avg</div></div>
572
+ <div class="hs-item"><div class="hs-val">+36pp</div><div class="hs-label">vs greedy</div></div>
573
  </div>
574
  </div>
575
 
 
763
  <div style="font-size:0.82rem;font-weight:700;color:#fff;">LLM + GRPO Agent (Llama 3.3 70B)</div>
764
  </div>
765
  <div style="font-size:0.72rem;color:var(--muted);margin-bottom:1rem;line-height:1.7;">
766
+ Llama 3.3 70B via Groq, with GRPO-style episodic memory across 2&ndash;4 rollouts per task.
767
  Each rollout injects advantage-gated memory from prior rollouts into the prompt.
768
  Reproduced by running <code style="color:var(--text);">python baseline/run.py</code>.
769
  Total runtime: <strong style="color:var(--text);">19.8 minutes</strong>.
770
  </div>
771
  <div class="grid-3" style="margin-bottom:1rem;">
772
  <div class="card-sm" style="border-top:2px solid var(--green);">
773
+ <div style="font-size:0.62rem;letter-spacing:0.08em;text-transform:uppercase;color:var(--green);margin-bottom:0.5rem;">Easy &middot; 2 rollouts</div>
774
+ <div style="font-family:var(--serif);font-size:2rem;font-weight:700;color:#fff;line-height:1;">88.5%</div>
775
+ <div style="font-size:0.68rem;color:var(--muted);margin-top:0.25rem;">Best of 2 &middot; no breach</div>
776
  <div style="margin-top:0.75rem;display:flex;flex-direction:column;gap:0.3rem;">
777
  <div style="display:flex;justify-content:space-between;font-size:0.68rem;"><span style="color:var(--muted);">Containment</span><span>100%</span></div>
778
  <div style="display:flex;justify-content:space-between;font-size:0.68rem;"><span style="color:var(--muted);">Hospital</span><span>100%</span></div>
779
+ <div style="display:flex;justify-content:space-between;font-size:0.68rem;"><span style="color:var(--muted);">Efficiency</span><span>90%</span></div>
780
  </div>
781
  </div>
782
  <div class="card-sm" style="border-top:2px solid var(--amber);">
783
  <div style="font-size:0.62rem;letter-spacing:0.08em;text-transform:uppercase;color:var(--amber);margin-bottom:0.5rem;">Medium &middot; 4 rollouts</div>
784
+ <div style="font-family:var(--serif);font-size:2rem;font-weight:700;color:#fff;line-height:1;">75.4%</div>
785
  <div style="font-size:0.68rem;color:var(--muted);margin-top:0.25rem;">Best of 4 &middot; no breach</div>
786
  <div style="margin-top:0.75rem;display:flex;flex-direction:column;gap:0.3rem;">
787
  <div style="display:flex;justify-content:space-between;font-size:0.68rem;"><span style="color:var(--muted);">Containment</span><span>42%</span></div>
 
791
  </div>
792
  <div class="card-sm" style="border-top:2px solid var(--red);">
793
  <div style="font-size:0.62rem;letter-spacing:0.08em;text-transform:uppercase;color:var(--red);margin-bottom:0.5rem;">Hard &middot; 4 rollouts</div>
794
+ <div style="font-family:var(--serif);font-size:2rem;font-weight:700;color:#fff;line-height:1;">63.1%</div>
795
  <div style="font-size:0.68rem;color:var(--muted);margin-top:0.25rem;">Best of 4 &middot; no breach</div>
796
  <div style="margin-top:0.75rem;display:flex;flex-direction:column;gap:0.3rem;">
797
  <div style="display:flex;justify-content:space-between;font-size:0.68rem;"><span style="color:var(--muted);">Containment</span><span>51%</span></div>
 
806
  <tr><th>Task</th><th>Rollout 1</th><th>Rollout 2</th><th>Rollout 3</th><th>Rollout 4</th><th>Best</th></tr>
807
  <tr>
808
  <td><span class="badge badge-green">Easy</span></td>
809
+ <td>88.5%</td><td>83.2%</td><td>&mdash;</td><td>&mdash;</td>
810
+ <td><strong style="color:var(--green);">88.5%</strong></td>
811
  </tr>
812
  <tr>
813
  <td><span class="badge badge-amber">Medium</span></td>
814
+ <td>59.6%</td><td>66.2%</td><td>75.4%</td><td>53.7%</td>
815
+ <td><strong style="color:var(--amber);">75.4%</strong></td>
816
  </tr>
817
  <tr>
818
  <td><span class="badge badge-red">Hard</span></td>
819
+ <td>63.1%</td><td>53.9%</td><td>59.0%</td><td>63.0%</td>
820
+ <td><strong style="color:var(--red);">63.1%</strong></td>
821
  </tr>
822
  </table>
823
  </div>
 
841
  <tr><th>Task</th><th>Greedy (D0)</th><th>LLM+GRPO</th><th>Lift (&Delta;)</th><th>Signal</th><th>Exploit Risk</th></tr>
842
  <tr>
843
  <td><span class="badge badge-green">Easy</span></td>
844
+ <td style="color:var(--muted);">42.8%</td><td><strong>88.5%</strong></td>
845
+ <td class="pos">+46pp</td>
846
  <td><span class="badge badge-green">Strong</span></td>
847
  <td><span class="badge badge-green">None &mdash; 0% eff, 60% breach</span></td>
848
  </tr>
849
  <tr>
850
  <td><span class="badge badge-amber">Medium</span></td>
851
+ <td style="color:var(--muted);">42.7%</td><td><strong>75.4%</strong></td>
852
+ <td class="pos">+33pp</td>
853
  <td><span class="badge badge-green">Strong</span></td>
854
  <td><span class="badge badge-green">None &mdash; 8% eff, 80% breach</span></td>
855
  </tr>
856
  <tr>
857
  <td><span class="badge badge-red">Hard</span></td>
858
+ <td style="color:var(--muted);">33.0%</td><td><strong>63.1%</strong></td>
859
  <td class="pos">+33pp</td>
860
  <td><span class="badge badge-green">Strong</span></td>
861
  <td><span class="badge badge-green">None &mdash; 0% eff, 100% breach</span></td>
862
  </tr>
863
  <tr style="border-top:1px solid var(--border2);">
864
  <td><strong>Average</strong></td>
865
+ <td style="color:var(--muted);">39.5%</td><td><strong>75.7%</strong></td>
866
+ <td class="pos"><strong>+36pp</strong></td>
867
  <td><span class="badge badge-green">Strong</span></td>
868
  <td><span class="badge badge-green">No exploits found</span></td>
869
  </tr>
 
874
  <div style="font-size:0.62rem;letter-spacing:0.08em;text-transform:uppercase;color:var(--muted);margin-bottom:0.75rem;">Easy</div>
875
  <div style="display:flex;flex-direction:column;gap:0.45rem;">
876
  <div><div style="display:flex;justify-content:space-between;font-size:0.68rem;margin-bottom:0.2rem;"><span style="color:var(--muted);">Greedy (D0)</span><span>43%</span></div><div class="bar-track" style="height:8px;"><div class="bar-fill" style="width:43%;background:var(--dim);"></div></div></div>
877
+ <div><div style="display:flex;justify-content:space-between;font-size:0.68rem;margin-bottom:0.2rem;"><span style="color:var(--green);">LLM+GRPO</span><span>89%</span></div><div class="bar-track" style="height:8px;"><div class="bar-fill" style="width:89%;background:var(--green);"></div></div></div>
878
+ <div style="font-size:0.68rem;color:var(--muted);">Lift: <span style="color:var(--green);font-weight:700;">+46pp</span></div>
879
  </div>
880
  </div>
881
  <div class="card-sm">
882
  <div style="font-size:0.62rem;letter-spacing:0.08em;text-transform:uppercase;color:var(--muted);margin-bottom:0.75rem;">Medium</div>
883
  <div style="display:flex;flex-direction:column;gap:0.45rem;">
884
  <div><div style="display:flex;justify-content:space-between;font-size:0.68rem;margin-bottom:0.2rem;"><span style="color:var(--muted);">Greedy (D0)</span><span>43%</span></div><div class="bar-track" style="height:8px;"><div class="bar-fill" style="width:43%;background:var(--dim);"></div></div></div>
885
+ <div><div style="display:flex;justify-content:space-between;font-size:0.68rem;margin-bottom:0.2rem;"><span style="color:var(--amber);">LLM+GRPO</span><span>75%</span></div><div class="bar-track" style="height:8px;"><div class="bar-fill" style="width:75%;background:var(--amber);"></div></div></div>
886
+ <div style="font-size:0.68rem;color:var(--muted);">Lift: <span style="color:var(--amber);font-weight:700;">+33pp</span></div>
887
  </div>
888
  </div>
889
  <div class="card-sm">
890
  <div style="font-size:0.62rem;letter-spacing:0.08em;text-transform:uppercase;color:var(--muted);margin-bottom:0.75rem;">Hard</div>
891
  <div style="display:flex;flex-direction:column;gap:0.45rem;">
892
  <div><div style="display:flex;justify-content:space-between;font-size:0.68rem;margin-bottom:0.2rem;"><span style="color:var(--muted);">Greedy (D0)</span><span>33%</span></div><div class="bar-track" style="height:8px;"><div class="bar-fill" style="width:33%;background:var(--dim);"></div></div></div>
893
+ <div><div style="display:flex;justify-content:space-between;font-size:0.68rem;margin-bottom:0.2rem;"><span style="color:var(--red);">LLM+GRPO</span><span>63%</span></div><div class="bar-track" style="height:8px;"><div class="bar-fill" style="width:63%;background:var(--red);"></div></div></div>
894
+ <div style="font-size:0.68rem;color:var(--muted);">Lift: <span style="color:var(--red);font-weight:700;">+30pp</span></div>
895
  </div>
896
  </div>
897
  </div>
898
  <div style="padding:0.85rem 1.1rem;background:var(--green-dim);border:1px solid rgba(61,214,140,0.2);border-radius:8px;font-size:0.72rem;color:var(--text);line-height:1.8;">
899
+ &#10003; <strong>Variance check passed.</strong> Mean lift of <strong>+36pp</strong> across all tasks.
900
  Greedy (D0) scores 33&ndash;43% with 60&ndash;100% hospital breach rates &mdash; confirming no trivial exploit path.
901
  LLM+GRPO reaches 66&ndash;91% with zero breaches &mdash; genuine triage reasoning is required and rewarded.
902
  </div>
 
1086
  <div class="arch-card"><div class="arch-icon">📊</div><div class="arch-name">grader.py</div><div class="arch-desc">Deterministic trajectory scorer. Reads hidden CityState. Four components weighted into final_score ∈ [0.0, 1.0]. Zero LLM calls.</div></div>
1087
  <div class="arch-card"><div class="arch-icon">🌊</div><div class="arch-name">utils.py</div><div class="arch-desc">SIR-inspired spread model with linear spillover (no wrap-around). Observation builder enforcing partial observability by task.</div></div>
1088
  <div class="arch-card"><div class="arch-icon">🧠</div><div class="arch-name">core/trajectory.py</div><div class="arch-desc">EpisodicMemory. Stores (obs, action, reward) tuples. Retrieves top-k by L1 distance on infection profiles, phase-weighted.</div></div>
1089
+ <div class="arch-card"><div class="arch-icon">🎯</div><div class="arch-name">baseline/evaluator.py</div><div class="arch-desc">GRPO-style loop. Per-task rollouts: easy=2, medium=4, hard=4. Advantage = Rᵢ − mean(R). Memory threshold -0.3. Best score reported.</div></div>
1090
  </div>
1091
 
1092
  <div class="card">