DEV Community

dtannen
dtannen

Posted on

Fully Automated Website Day 23: Lane Forecast: see the next swarm before it lands

Command Garden is a website that builds itself — one feature per day, fully autonomously. No human writes the code. An AI pipeline proposes candidates, judges score them, and the winner gets implemented, tested, and shipped.

What shipped

May 3 ships Lane Forecast — an in-canvas telegraph that draws each scripted enemy spawn directly on the lane it will arrive in, before it arrives. A faint amber marker with the enemy silhouette, swarm count, and a depleting countdown ring appears at the right edge of each lane up to six seconds early, then dissolves cleanly into the actual spawn. The forecast reads the existing encounter timeline and changes no spawn, AI, or balance logic; it only surfaces information the engine had already committed to.

Candidates considered

  • Lane Forecast — Live Threat Telegraphs (score: 8.5) — Show upcoming scripted enemy spawns directly on each lane before they arrive, making Rootline Defense easier to read without making it easier to win.
  • Watch Today's Garden — Title-Screen Attract Reel for Rootline Defense (score: 7.5) — The /game/ title screen idles into a 20-second highlight of a curated clear of today's challenge, so every visitor sees Rootline Defense in motion before deciding to play.
  • *Garden Snapshot — Shareable Challenge Summaries* (score: 7.3) — A post-game "social card" generator that captures your unique daily challenge clear into a shareable visual summary, salvaging the social growth mission after the Garden Replay failure.

Winner

Lane Forecast — Live Threat Telegraphs with a score of 8.5

Selected as the highest-scoring candidate with an average score of 8.5 across 2 judge(s).

Technical spec

May 3, 2026 — Lane Forecast: Live Threat Telegraphs

May 3 ships Lane Forecast: an in-canvas telegraph that draws each scripted enemy spawn directly on the lane it will arrive in, before it arrives. The forecast reads the next ~6 seconds of the live encounter timeline (EncounterSystem.events, already exposed read-only via getObservation().upcomingEvents), and renders a per-lane edge marker — enemy silhouette, swarm count, and a shrinking countdown ring — at the right edge of the playable board. When the spawn fires, the marker resolves cleanly into the actual enemy. The product surface is on-board game intelligence; the engineering surface is a thin overlay system that consumes existing scenario data without changing any spawn, AI, or balance logic.

This is the smallest credible "make Rootline Defense more legible without making it easier by stat tuning" cycle. The forecast does not advance fights, does not reduce enemy HP, does not unlock new plants, does not let the player place during a freeze. It only surfaces information the engine has already committed to. The teach is one specific thing: new visitors stop wondering "where did that swarm come from?" — they see Spore Tick × 5 building on the third lane for two-plus seconds, watch it resolve, and learn the rhythm of the board.

Honest fairness framing. Lane Forecast does make the game easier to read, and any feature that helps a player read the board makes that player likelier to win. The defensible claim is narrower: this feature does not change enemy stats, spawn timing, or scoring; it surfaces information the encounter system has already committed to and that is in principle visible in the open-source scenario file. Hard boards remain hard because the load-bearing question is what to do (Pod the armor, splash the swarm), not what is coming.

Lineage note. May 1 attempted Garden Replay (a per-user replay-share system) and did not ship. May 2 specced Featured Clear (a watch-mode for

[Spec truncated — view full spec on the site]

What changed

Build Summary

May 3 ships Lane Forecast — an in-canvas telegraph that previews each scripted enemy spawn directly on the lane it will arrive in, before it arrives.

Product changes

  • A new amber per-lane marker appears at the right edge of the playable board for every scripted spawn within a six-second horizon. The marker contains an enemy silhouette (existing texture at reduced alpha), a label (e.g. Spore Tick × 5 for swarms, Husk Walker for singletons), a one-decimal countdown text, and a depleting countdown ring/arc.
  • When the spawn fires, the marker plays a 200 ms dissolve (alpha 1 → 0, scale 1 → 0.85) and is destroyed. The spawn itself is unaffected — it fires on its scheduled frame regardless.
  • Forecast is on by default in tutorial and challenge modes per IR8 / PO1, so the same legibility lesson teaches across both surfaces.
  • Forecast hides automatically on game-over, challenge clear, endless mode, and during scene transitions; the same IR9 gate (PlayScene.getForecastSnapshot()) is the sole source of truth used by the observation snapshot, the test-mode hook, and the marker reconciler.

Engineering surface

  • New module: site/game/src/systems/lane-forecast.js. LaneForecastSystem.getEntries(elapsedMs) returns one entry per swarm group (IR4 dedupe — only swarmIndex === 0 is emitted) over the live EncounterSystem.events array. Pure read; never mutates encounter or scene state; never touches Math.random.
  • PlayScene integration adds this.laneForecast, this.forecastLayer (depth 8, above enemies, below HUD), and this.forecastMarkers. runGameStep calls updateForecastMarkers() after the existing publish phase. New helpers: getForecastSnapshot(), updateForecastMarkers(), createForecastMarker(), updateForecastMarker(), dissolveForecastMarker().
  • Observation extension: forecast: this.getForecastSnapshot() is added to the getObservation() snapshot. forecast is an additive field on the existing schemaVersion: 1 observation; existing consumers ignore it.
  • Test hooks: __gameTestHooks.getForecast() returns each entry plus per-marker render geometry (x, y, visible, alpha, labelText). __gameTestHooks.setDisableForecast(value) toggles a runtime flag mid-run. Both hooks are testMode-only.
  • Determinism harness: bootstrap.testDisableForecast is wired from ?testDisableForecast=1 (testMode-only) so the AC-5 spec can drive the prior-roster replay twice — once with the forecast on, once with it off — and assert the final observation is identical excluding the forecast field.

Test coverage

Three new Playwright specs land in tests/uiux/:

  • lane-forecast-entries-2026-05-03.spec.js — AC-2 (data layer): at elapsedMs ≈ 0 of the 2026-04-28 challenge, observation.forecast has exactly one Spore Tick × 5 entry on row 2 at atMs=4500.
  • lane-forecast-marker-resolution-2026-05-03.spec.js — AC-4 (resolution): with setTimeScale(8) and a poll until survivedMs >= 4800, the wave-1 Spore Tick swarm entry is gone from getForecast().
  • lane-forecast-determinism-2026-05-03.spec.js — AC-5 (determinism): the prior-roster replay drives the same actions twice with testDisableForecast toggled and the final observation (excluding forecast) is identical.

Public artifacts

This bundle publishes the public decision trail for May 3: decision data, specification, build summary, review notes, test results, and feedback digest.


Command Garden ships one feature every day with zero human code. Follow along at commandgarden.com.

Top comments (1)

Collapse
 
peacebinflow profile image
PEACEBINFLOW

The "honest fairness framing" paragraph is the part that got my attention — the one where you explicitly acknowledge that making the board easier to read does make the game easier, even though the feature doesn't touch stats or spawn logic. That kind of self-interrogation is rare in feature write-ups, human or AI-generated. Most product descriptions would just lead with the defensible claim and leave the tension unstated.

What it surfaces is a design question that goes beyond this specific feature: when does surfacing information that was technically already there change the nature of a system? The encounter data was public — you could go read the scenario file. But nobody was going to do that mid-wave. So the forecast isn't just revealing hidden state; it's changing the player's relationship to time. Six seconds of anticipation is a different cognitive experience than reacting in the moment, even if the numbers underneath are identical.

I think that's where the "easier to read vs. easier to win" distinction gets genuinely blurry, and it's interesting that the spec doesn't try to resolve it cleanly — it just names the tension and moves forward. That feels like the right call for a daily-ship project where you can't spend a week philosophizing about game feel. But I wonder if the judging model that scored this candidate recognized that nuance, or if it just evaluated the surface claim. Does the scoring system have any mechanism for rewarding a candidate that names its own tradeoffs honestly, or is that the kind of thing only a human reader would notice?