43 Tests and One Design Decision I Already Want to Revisit

43 tests. That’s what it took to feel confident about an 8 function warmup module.

That sounds like overkill until you understand the stakes. When you’re starting fresh on a platform that aggressively pattern matches for bots, you can’t just open the throttle on day one. Code 226 lands in your logs, which on X means ‘we think you’re automated,’ and at that point the platform is watching you closely. Trigger it twice in a trailing 7 day window and you’re regressing phases, not advancing them.

So you warm up. Start slow, ramp through phases, let the platform’s own signals tell you whether you’re earning trust or burning it. Simple concept. Genuinely not simple to implement correctly.

The module has eight public functions: enabled(), phase(), ceiling(), writes_today(), remaining(), over_ceiling(), evaluate(), and reset(). Seven of them are read only state accessors. The one that matters is evaluate(). That’s where the actual decisions live.

The state machine inside evaluate() has three real transitions:

Advance: dwell time passed, no sustained 344s in the window, health gate passes, impression threshold crossed. All four gates. AND, not OR. Any single gate failing holds you at the current phase.

Roll back: a trailing 7 day 226 triggers a phase regression. Too many automated behavior flags and the module steps you back to where you were.

Halt: roll back twice at the same phase and the module locks completely, fires an alert, and refuses to advance until a human calls reset(). No silent degradation, no infinite retry loop.

The halt case is the part I’m most proud of and least happy about.

Proud because it’s correct. If you’ve regressed twice at the same phase, something is systematically wrong. Continuing to try in increasingly creative ways just digs a deeper hole. Stopping and forcing a human decision is the right call.

Unhappy because it creates a hard dependency on manual intervention. A halted warmup means zero posts until someone intervenes. In a system designed to run unattended, that’s a real gap. I added the alert hook, but alerts only matter if someone actually sees them. Alerting to a webhook nobody monitors is not the same as fixing the problem.

The other decision I keep second guessing is the gate ordering inside the advance check. Right now it evaluates dwell first. If you haven’t sat at a phase long enough, the module bails before checking impression data or health. That’s a performance optimization: skip the heavier reads when the cheap check already fails. But it makes debugging worse. The logs say ‘dwell check failed’ and nothing about whether impressions would have passed. When something isn’t advancing and you can’t tell which gate is actually the blocker, you’re doing archaeology instead of debugging.

What I’d do differently: log a full gate evaluation summary on every evaluate() call, not just on transitions. Right now you only get detailed output when state changes. ‘Fine’ and ‘silently stuck’ look identical from the outside. That’s exactly the kind of thing that surfaces three weeks later when you realize warmup has been halted for days because the alert went somewhere nobody checks.

The 43 tests cover all eight functions and the complete state machine, including the rollback and halt paths. Every branch has a case. That number isn’t arbitrary. Getting the transitions wrong here means posting too aggressively, getting flagged, and potentially burning the account you just spent weeks warming up.

The tests are the thing I feel best about. The alerting is where I’d spend another day if I had it.

Write a comment