atagen

jelq

public

an http based roguelike rpg about jelqing

Star0
Watch0

Commit bf5f8cb6f60f

tarball

atagen <boss@atagen.co>2026-10-13 12:14 UTC

14 files changed+48-71
game: define cold as EQ under 30

Cold was two things. A towel flag that only the towel could set, so a run cold after its refractory period paid the cold tax on every jelq for the rest of the floor, and a 404 below 30 EQ that refused jelqs and edges outright. Now cold is just EQ under 30. Cold jelqs pay the tax but still roll, and edging climbs out untaxed.
Modified file: src/game/body.rs−2 deletions
@@ -17,7 +17,6 @@ pub struct Body {
1717 pub(crate) grip: Grip,
1818 /// The grip of the last stroke, which decides whether this one gets the switching discount.
1919 pub(crate) jelq_grip: Option<Grip>,
20- pub(crate) warmed_up: bool,
2120 pub(crate) exhausted: bool,
2221 pub(crate) put_applied: bool,
2322 pub(crate) conditions: Conditions,
@@ -36,7 +35,6 @@ impl Body {
3635 friction: 0,
3736 grip: Grip::Overhand,
3837 jelq_grip: None,
39- warmed_up: false,
4038 exhausted: false,
4139 put_applied: false,
4240 conditions: Conditions::default(),
Modified file: src/game/dispatch.rs+7 additions−5 deletions
@@ -84,9 +84,10 @@ impl Run {
8484 self.lube_available(lube)?;
8585 self.floor_enemy()?;
8686 self.take_lube(lube)?;
87- self.chafe(lube, intensity);
87+ let cold = self.cold();
88+ self.chafe(lube, intensity, cold);
8889 self.commit(intensity, lube, 1);
89- if let ControlFlow::Break(interrupted) = self.mishaps(training, intensity)? {
90+ if let ControlFlow::Break(interrupted) = self.mishaps(training, intensity, cold)? {
9091 return Ok(interrupted);
9192 }
9293 if let ControlFlow::Break(interrupted) = self.hazards(intensity)? {
@@ -116,11 +117,12 @@ impl Run {
116117 self.lube_available(lube)?;
117118 self.floor_enemy()?;
118119 self.take_lube(lube)?;
119- self.chafe(lube, intensity);
120- self.chafe(lube, intensity);
120+ let cold = self.cold();
121+ self.chafe(lube, intensity, cold);
122+ self.chafe(lube, intensity, cold);
121123 self.commit(intensity, lube, 2);
122124 for training in [a, b] {
123- if let ControlFlow::Break(interrupted) = self.mishaps(training, intensity)? {
125+ if let ControlFlow::Break(interrupted) = self.mishaps(training, intensity, cold)? {
124126 return Ok(interrupted);
125127 }
126128 }
Modified file: src/game/hazards.rs+11 additions−9 deletions
@@ -41,8 +41,17 @@ impl Run {
4141 }
4242 }
4343
44- pub(super) fn mishaps(&mut self, training: Training, intensity: i32) -> Interruption {
45- if !self.body.warmed_up {
44+ pub(super) fn cold(&self) -> bool {
45+ self.body.eq < COLD_EQ
46+ }
47+
48+ pub(super) fn mishaps(
49+ &mut self,
50+ training: Training,
51+ intensity: i32,
52+ cold: bool,
53+ ) -> Interruption {
54+ if cold {
4655 if !self.notices.contains(&Notice::ColdStart) {
4756 self.notices.push(Notice::ColdStart);
4857 }
@@ -155,13 +164,6 @@ impl Run {
155164 "The floor has ended early. Everyone involved has agreed not to discuss it.",
156165 ));
157166 }
158- if self.body.eq < COLD_EQ {
159- return Err(Problem::new(
160- StatusCode::NOT_FOUND,
161- "cold",
162- "The resource exists. It is cold.",
163- ));
164- }
165167 Ok(ControlFlow::Continue(()))
166168 }
167169
Modified file: src/game/notice.rs+1 additions−1 deletions
@@ -29,7 +29,7 @@ impl Notice {
2929 Self::ThreadHasReplies => "Somewhere, a forum thread from 2009 still has replies.",
3030 Self::Reflection => "the count reached ten, and then the reflection started",
3131 Self::MumSatisfied => "Mum seems satisfied. She takes the washing with her.",
32- Self::ColdStart => "Warm-up was skipped. The server has noted this.",
32+ Self::ColdStart => "Cold start. The server has noted this.",
3333 }
3434 }
3535 }
Modified file: src/game/progress.rs+2 additions−2 deletions
@@ -10,7 +10,7 @@ use super::{
1010 };
1111
1212 impl Run {
13- pub(super) fn chafe(&mut self, lube: Option<Lube>, intensity: i32) {
13+ pub(super) fn chafe(&mut self, lube: Option<Lube>, intensity: i32, cold: bool) {
1414 let (base, slip) = match lube {
1515 None => (FRICTION_DRY, 0),
1616 Some(Lube::Spit) => (FRICTION_SPIT, 0),
@@ -31,7 +31,7 @@ impl Run {
3131 {
3232 friction -= FRICTION_GRIP;
3333 }
34- if !self.body.warmed_up {
34+ if cold {
3535 friction += FRICTION_COLD;
3636 }
3737 self.body.jelq_grip = Some(self.body.grip);
Modified file: src/game/run.rs+1 additions−4 deletions
@@ -219,10 +219,9 @@ impl Run {
219219 }
220220 }
221221 s.push_str(&format!(
222- "suspicion: {}\nshame: {}\nwarmed up: {}\ngrip: {}\n",
222+ "suspicion: {}\nshame: {}\ngrip: {}\n",
223223 self.standing.suspicion,
224224 self.standing.shame,
225- if self.body.warmed_up { "yes" } else { "no" },
226225 self.body.grip.name(),
227226 ));
228227 let held: Vec<&str> = self.items.owned().iter().map(|item| item.slug()).collect();
@@ -314,7 +313,6 @@ impl Run {
314313 "turn": self.turn,
315314 "run_shame": self.standing.shame,
316315 "grip": self.body.grip.name(),
317- "warmed_up": self.body.warmed_up,
318316 "ranked": !self.ident.seeded,
319317 "conditions": conditions,
320318 })
@@ -333,7 +331,6 @@ impl Run {
333331 self.body.exhausted = false;
334332 self.body.stamina = self.max_stamina();
335333 self.reset_eq(STARTING_EQ);
336- self.body.warmed_up = false;
337334 }
338335
339336 pub(super) fn check_exhausted(&self) -> Result<(), Problem> {
Modified file: src/game/travel.rs−1 deletions
@@ -28,7 +28,6 @@ impl Run {
2828 warm_arrival: warm,
2929 ..FloorState::default()
3030 };
31- self.body.warmed_up = false;
3231 self.reset_eq(STARTING_EQ);
3332 self.standing.suspicion = 0;
3433 self.shed(FRICTION_FLOOR);
Modified file: src/game/utility.rs+1 additions−2 deletions
@@ -61,7 +61,6 @@ impl Run {
6161 self.here.kitchen.kettle_boiled = false;
6262 }
6363 self.here.towel_warmed = true;
64- self.body.warmed_up = true;
6564 self.body.eq = (self.body.eq + boost).min(100);
6665 Ok(Outcome::json(StatusCode::OK, json!({ "detail": detail })))
6766 }
@@ -147,7 +146,7 @@ impl Run {
147146 }
148147
149148 pub(super) fn edge(&mut self) -> Result<Outcome, Problem> {
150- self.chafe(None, 5);
149+ self.chafe(None, 5, false);
151150 self.commit(5, None, 1);
152151 if let ControlFlow::Break(interrupted) = self.hazards(5)? {
153152 return Ok(interrupted);
Modified file: src/tests/body.rs+4 additions−15 deletions
@@ -100,8 +100,8 @@ async fn the_displayed_length_is_accepted_as_honest() {
100100
101101 #[tokio::test]
102102 async fn dry_jelqs_build_friction_and_real_lube_does_not() {
103- let app = seeded_with(Floor::Bedroom, |run| {
104- run.body.warmed_up = true;
103+ let app = seeded_with(Floor::ChangingRoom, |run| {
104+ run.body.eq = 60;
105105 run.items.lubes.baby_oil = 1;
106106 run.body.foreskin = Some(0.9);
107107 });
@@ -131,8 +131,8 @@ async fn dry_jelqs_build_friction_and_real_lube_does_not() {
131131
132132 #[tokio::test]
133133 async fn switching_grips_makes_the_friction_cheaper() {
134- let app = seeded_with(Floor::Bedroom, |run| {
135- run.body.warmed_up = true;
134+ let app = seeded_with(Floor::ChangingRoom, |run| {
135+ run.body.eq = 60;
136136 run.body.foreskin = Some(0.9);
137137 });
138138 let dry = send(&app, "JELQ", "/runs/cafebabe?technique=dry", &[], "").await;
@@ -185,12 +185,10 @@ async fn a_friction_burn_halves_the_gains() {
185185 let path = "/runs/cafebabe?technique=wet&intensity=5";
186186 for seed in 0..60u64 {
187187 let steady = app_with(Floor::Bedroom, seed, |run| {
188- run.body.warmed_up = true;
189188 run.body.eq = 60;
190189 run.items.lubes.baby_oil = 9;
191190 });
192191 let hurt_app = app_with(Floor::Bedroom, seed, |run| {
193- run.body.warmed_up = true;
194192 run.body.eq = 60;
195193 run.items.lubes.baby_oil = 9;
196194 run.body.conditions.friction_burn = true;
@@ -214,7 +212,6 @@ async fn a_friction_burn_halves_the_gains() {
214212 #[tokio::test]
215213 async fn the_burn_warns_and_the_ice_treats_it() {
216214 let app = seeded_with(Floor::Bedroom, |run| {
217- run.body.warmed_up = true;
218215 run.body.eq = 60;
219216 run.items.lubes.baby_oil = 9;
220217 run.body.friction = 7;
@@ -253,7 +250,6 @@ async fn friction_rises_with_vigour_and_edging_chafes() {
253250 for (intensity, friction) in [(5, "1/10"), (7, "2/10"), (10, "3/10")] {
254251 let app = seeded_with(Floor::Hotel, |run| {
255252 run.here.wifi_accepted = true;
256- run.body.warmed_up = true;
257253 run.body.eq = 55;
258254 run.body.foreskin = Some(0.5);
259255 });
@@ -267,7 +263,6 @@ async fn friction_rises_with_vigour_and_edging_chafes() {
267263 }
268264 let app = seeded_with(Floor::Hotel, |run| {
269265 run.here.wifi_accepted = true;
270- run.body.warmed_up = true;
271266 run.body.eq = 55;
272267 run.body.foreskin = Some(0.5);
273268 });
@@ -282,7 +277,6 @@ async fn the_body_changes_how_the_skin_takes_it() {
282277 {
283278 let app = seeded_with(Floor::Hotel, |run| {
284279 run.here.wifi_accepted = true;
285- run.body.warmed_up = true;
286280 run.body.eq = 55;
287281 run.body.foreskin = Some(foreskin);
288282 run.body.girth = girth;
@@ -309,7 +303,6 @@ async fn a_thick_girth_outgrows_the_underhand_grip() {
309303 #[tokio::test]
310304 async fn a_long_run_fills_the_jeans_before_a_thick_one() {
311305 let app = seeded_with(Floor::Bedroom, |run| {
312- run.body.warmed_up = true;
313306 run.items.kit.extender = true;
314307 run.body.girth = 4.8;
315308 run.body.length = 7.5;
@@ -322,7 +315,6 @@ async fn a_long_run_fills_the_jeans_before_a_thick_one() {
322315 #[tokio::test]
323316 async fn a_refused_jelq_leaves_the_lube_alone() {
324317 let app = seeded_with(Floor::Bedroom, |run| {
325- run.body.warmed_up = true;
326318 run.items.kit.extender = true;
327319 run.body.girth = 6.0;
328320 run.body.length = 6.0;
@@ -346,7 +338,6 @@ async fn a_refused_jelq_leaves_the_lube_alone() {
346338 #[tokio::test]
347339 async fn lubes_run_out_but_spit_never_does() {
348340 let app = seeded_with(Floor::Bedroom, |run| {
349- run.body.warmed_up = true;
350341 run.items.lubes.baby_oil = 1;
351342 });
352343 let lube = [("content-type", "application/baby-oil")];
@@ -384,7 +375,6 @@ async fn patching_costs_a_little_eq() {
384375 async fn a_combo_chafes_twice_and_costs_two_stamina() {
385376 let app = seeded_with(Floor::Hotel, |run| {
386377 run.here.wifi_accepted = true;
387- run.body.warmed_up = true;
388378 run.body.eq = 55;
389379 run.body.foreskin = Some(0.5);
390380 });
@@ -421,7 +411,6 @@ async fn stretching_reports_the_foreskin_it_grew() {
421411 let mut landed = 0;
422412 for seed in 0..40u64 {
423413 let app = app_with(Floor::Bedroom, seed, |run| {
424- run.body.warmed_up = true;
425414 run.body.eq = 60;
426415 run.body.foreskin = Some(1.0);
427416 });
Modified file: src/tests/floors.rs+1 additions−7 deletions
@@ -115,7 +115,6 @@ async fn caged_drawers_are_noisy_and_eventually_give_up_the_key() {
115115 async fn an_open_tab_is_linked_and_jelq_still_answers_with_choices() {
116116 let app = seeded_with(Floor::Bathroom, |run| {
117117 run.online.story = Story::Pending;
118- run.body.warmed_up = true;
119118 });
120119 let response = send(&app, "GET", "/runs/cafebabe", &[], "").await;
121120 let links = header(&response, "link");
@@ -131,7 +130,6 @@ async fn either_story_choice_is_reachable_during_cooldown() {
131130 for choice in ["forum", "close-tab"] {
132131 let app = seeded_with(Floor::Bathroom, |run| {
133132 run.turn = 9;
134- run.body.warmed_up = true;
135133 run.body.eq = 45;
136134 run.body.stamina = 1;
137135 run.items.stores.door_locks = 1;
@@ -157,7 +155,6 @@ async fn either_story_choice_is_reachable_during_cooldown() {
157155 async fn the_thread_announces_itself_at_the_branch() {
158156 let app = seeded_with(Floor::Bathroom, |run| {
159157 run.turn = 8;
160- run.body.warmed_up = true;
161158 run.items.lubes.baby_oil = 9;
162159 });
163160 send(
@@ -196,7 +193,6 @@ async fn reading_the_thread_teaches_uli() {
196193 for seed in 0..100u64 {
197194 let app = app_with(Floor::Bedroom, seed, |run| {
198195 run.turn = BRANCH_TURN;
199- run.body.warmed_up = true;
200196 run.online.story = Story::ClosedTab;
201197 run.online.meditation_taught = true;
202198 });
@@ -235,7 +231,7 @@ async fn reading_the_thread_teaches_uli() {
235231
236232 #[tokio::test]
237233 async fn the_thread_cannot_be_read_before_anyone_mentions_it() {
238- let app = seeded_with(Floor::Bedroom, |run| run.body.warmed_up = true);
234+ let app = seeded_with(Floor::Bedroom, |_| {});
239235 let read = send(&app, "GET", "/runs/cafebabe/thread", &[], "").await;
240236 assert_eq!(read.status(), StatusCode::NOT_FOUND);
241237 let after = send(&app, "GET", "/runs/cafebabe", &[], "").await;
@@ -439,7 +435,6 @@ async fn the_foreskin_window_has_hard_edges() {
439435 ] {
440436 let app = seeded_with(Floor::Hotel, |run| {
441437 run.here.wifi_accepted = true;
442- run.body.warmed_up = true;
443438 run.body.eq = 55;
444439 run.body.foreskin = Some(foreskin);
445440 });
@@ -461,7 +456,6 @@ async fn the_forum_teaches_breathing_before_uli() {
461456 for seed in 0..100u64 {
462457 let app = app_with(Floor::Bedroom, seed, |run| {
463458 run.turn = BRANCH_TURN;
464- run.body.warmed_up = true;
465459 run.online.story = Story::Pending;
466460 });
467461 let listed = json(send(&app, "GET", "/runs/cafebabe/techniques", &[], "").await).await;
Modified file: src/tests/household.rs−3 deletions
@@ -154,7 +154,6 @@ async fn mom_speaks_american() {
154154 #[tokio::test]
155155 async fn an_empty_bottle_is_declined_before_the_enemy_stirs() {
156156 let control = seeded_with(Floor::ChangingRoom, |run| {
157- run.body.warmed_up = true;
158157 run.standing.suspicion = SUSPICION_LIMIT;
159158 });
160159 let lube = [("content-type", "application/baby-oil")];
@@ -169,7 +168,6 @@ async fn an_empty_bottle_is_declined_before_the_enemy_stirs() {
169168 assert_eq!(caught.status(), StatusCode::UNAVAILABLE_FOR_LEGAL_REASONS);
170169
171170 let app = seeded_with(Floor::ChangingRoom, |run| {
172- run.body.warmed_up = true;
173171 run.standing.suspicion = SUSPICION_LIMIT;
174172 run.items.lubes.baby_oil = 0;
175173 });
@@ -191,7 +189,6 @@ async fn an_empty_bottle_is_declined_before_the_enemy_stirs() {
191189 async fn respectable_girth_waves_off_the_gym_staff() {
192190 let staff = |girth: f64| {
193191 seeded_with(Floor::ChangingRoom, move |run| {
194- run.body.warmed_up = true;
195192 run.body.eq = 45;
196193 run.body.girth = girth;
197194 run.standing.suspicion = SUSPICION_LIMIT;
Modified file: src/tests/methods.rs+2 additions−4 deletions
@@ -31,13 +31,11 @@ async fn bodies_do_not_repeat_the_headers() {
3131 async fn jelqing_cold_goes_through_at_a_price() {
3232 let friction = async |warm: bool| {
3333 let app = seeded_with(Floor::Bedroom, |run| {
34- run.body.warmed_up = warm;
34+ run.body.eq = if warm { 40 } else { 20 };
3535 run.body.foreskin = Some(1.0);
3636 });
3737 let response = send(&app, "JELQ", "/runs/cafebabe?technique=stretch", &[], "").await;
38- let noted = warnings(&response)
39- .iter()
40- .any(|w| w.contains("Warm-up was skipped"));
38+ let noted = warnings(&response).iter().any(|w| w.contains("Cold start"));
4139 assert_eq!(noted, !warm);
4240 header(&response, "x-friction").to_owned()
4341 };
Modified file: src/tests/rolls.rs+1 additions−5 deletions
@@ -30,7 +30,6 @@ async fn the_burn_shifts_the_rupture_tiers() {
3030 let mut gone = 0;
3131 for seed in 0..400u64 {
3232 let app = app_with(Floor::Bedroom, seed, |run| {
33- run.body.warmed_up = true;
3433 run.body.eq = 60;
3534 run.items.lubes.baby_oil = 9;
3635 run.body.conditions.friction_burn = burned;
@@ -64,7 +63,6 @@ async fn the_top_tier_rolls_a_single_third() {
6463 let mut early = 0;
6564 for seed in 0..400u64 {
6665 let app = app_with(Floor::Bedroom, seed, |run| {
67- run.body.warmed_up = true;
6866 run.body.eq = 96;
6967 run.items.lubes.baby_oil = 9;
7068 run.items.kit.blinds = true;
@@ -100,7 +98,6 @@ async fn holding_rolls_a_flat_rupture_chance() {
10098 let mut gone = 0;
10199 for seed in 0..600u64 {
102100 let app = app_with(Floor::Bedroom, seed, |run| {
103- run.body.warmed_up = true;
104101 run.body.eq = eq;
105102 run.body.stamina = stamina;
106103 });
@@ -178,7 +175,6 @@ async fn degloving_takes_one_hard_dry_stroke_in_twenty() {
178175 for seed in 0..1200u64 {
179176 let app = app_with(Floor::Hotel, seed, |run| {
180177 run.here.wifi_accepted = true;
181- run.body.warmed_up = true;
182178 run.body.eq = 55;
183179 run.body.foreskin = Some(1.0);
184180 });
@@ -211,7 +207,7 @@ async fn degloving_takes_one_cold_stroke_in_twenty() {
211207 for seed in 0..1200u64 {
212208 let app = app_with(Floor::Hotel, seed, |run| {
213209 run.here.wifi_accepted = true;
214- run.body.eq = 55;
210+ run.body.eq = 20;
215211 run.body.foreskin = Some(1.0);
216212 });
217213 let cold = send(&app, "JELQ", "/runs/cafebabe?technique=stretch", &[], "").await;
Modified file: src/tests/turns.rs+17 additions−11 deletions
@@ -27,7 +27,6 @@ async fn meditation_respects_the_priapism_floor() {
2727 #[tokio::test]
2828 async fn priapism_holds_the_floor_through_every_reset() {
2929 let exit = seeded_with(Floor::Bedroom, |run| {
30- run.body.warmed_up = true;
3130 run.body.eq = 90;
3231 run.here.turns = 99;
3332 run.body.conditions.priapism_turns = 3;
@@ -70,7 +69,6 @@ async fn the_towel_warms_once_until_the_kettle_boils() {
7069 async fn the_refractory_period_is_the_next_hold() {
7170 let app = seeded_with(Floor::Hotel, |run| {
7271 run.here.wifi_accepted = true;
73- run.body.warmed_up = true;
7472 run.here.towel_warmed = true;
7573 run.body.eq = 60;
7674 run.body.stamina = 0;
@@ -88,7 +86,6 @@ async fn the_refractory_period_is_the_next_hold() {
8886 #[tokio::test]
8987 async fn a_refused_jelq_does_not_break_the_edging_streak() {
9088 let app = seeded_with(Floor::ChangingRoom, |run| {
91- run.body.warmed_up = true;
9289 run.body.eq = 45;
9390 run.here.edges = 2;
9491 });
@@ -107,14 +104,14 @@ async fn a_refused_jelq_does_not_break_the_edging_streak() {
107104
108105 #[tokio::test]
109106 async fn an_interrupted_edge_keeps_no_gains() {
110- let app = seeded_with(Floor::ChangingRoom, |run| {
111- run.body.warmed_up = true;
112- run.body.eq = 10;
107+ let app = seeded_with(Floor::Hotel, |run| {
108+ run.here.wifi_accepted = true;
109+ run.body.eq = 100;
113110 });
114111 let before = send(&app, "GET", "/runs/cafebabe", &[], "").await;
115112 let girth = header(&before, "x-girth").to_owned();
116113 let edge = send(&app, "JELQ", "/runs/cafebabe?technique=edge", &[], "").await;
117- assert_eq!(edge.status(), StatusCode::NOT_FOUND);
114+ assert!(!edge.status().is_success());
118115 assert_eq!(header(&edge, "x-girth"), girth);
119116 }
120117
@@ -126,12 +123,10 @@ async fn greed_multiplies_gains_above_the_band() {
126123 for seed in 0..120u64 {
127124 let app = app_with(Floor::Bedroom, seed, |run| {
128125 run.body.eq = 60;
129- run.body.warmed_up = true;
130126 });
131127 let in_band = send(&app, "JELQ", "/runs/cafebabe?technique=wet", &lube, "").await;
132128 let app = app_with(Floor::Bedroom, seed, |run| {
133129 run.body.eq = eq;
134- run.body.warmed_up = true;
135130 });
136131 let greedy = send(&app, "JELQ", "/runs/cafebabe?technique=wet", &lube, "").await;
137132 if in_band.status() != StatusCode::OK || greedy.status() != StatusCode::OK {
@@ -187,7 +182,6 @@ async fn hold_keeps_the_shop_and_the_melon_open() {
187182 async fn leaving_the_band_warms_the_next_floor() {
188183 let app = seeded_with(Floor::Bedroom, |run| {
189184 run.body.eq = 60;
190- run.body.warmed_up = true;
191185 run.here.turns = 6;
192186 run.items.lubes.baby_oil = 9;
193187 });
@@ -255,7 +249,6 @@ async fn queued_gains_announce_their_arrival() {
255249 #[tokio::test]
256250 async fn an_energy_drink_ends_exhaustion_without_the_refractory_period() {
257251 let app = seeded_with(Floor::Bedroom, |run| {
258- run.body.warmed_up = true;
259252 run.body.eq = 60;
260253 run.body.stamina = 0;
261254 run.body.exhausted = true;
@@ -274,3 +267,16 @@ async fn an_energy_drink_ends_exhaustion_without_the_refractory_period() {
274267 let jelq = send(&app, "JELQ", "/runs/cafebabe?technique=edge", &[], "").await;
275268 assert_ne!(jelq.status(), StatusCode::TOO_MANY_REQUESTS);
276269 }
270+
271+#[tokio::test]
272+async fn edging_climbs_out_of_the_cold() {
273+ let app = seeded_with(Floor::ChangingRoom, |run| run.body.eq = 20);
274+ let edge = send(&app, "JELQ", "/runs/cafebabe?technique=edge", &[], "").await;
275+ assert_eq!(edge.status(), StatusCode::OK);
276+ assert!(warnings(&edge).iter().all(|w| !w.contains("Cold start")));
277+ assert_eq!(
278+ header(&edge, "x-friction"),
279+ "2/10",
280+ "edging pays no cold tax"
281+ );
282+}