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
Rootline Defense now has its first control plant. Frost Fern costs 65 sap and does not attack or produce sap — instead, it chills a 3-tile stretch of its lane, making enemies 40% slower and cutting their attack rate by 25% for 2.5 seconds. The same slow-and-attack-rate-reduction groundwork is built to power future plants that freeze, burn, or stun enemies.
Candidates considered
- Frost Fern: First Control Plant and Status-Effect System for Rootline Defense (score: 9.0) — A slowing defender that introduces Rootline Defense's first reusable status-effect system, opening the "control" archetype after thorn (DPS), bramble (piercing), sunroot (economy), and the Apr 16 sniper's ranged threat.
- Amber Wall: The First Defensive Tank for Rootline Defense (score: 8.0) — Introduces a high-health, non-attacking "tank" plant that blocks enemies, screens ranged bolts, and counts toward lane defender thresholds, providing the essential tactical layer for surviving high-pressure waves.
- Board Proof: Verified Clear Replay for Today’s Challenge (score: 7.5) — Turn Rootline Defense’s hidden replay harness into a public, optional “watch the winning line” mode that proves each daily board is winnable and teaches the exact strategy before a player commits a run.
Winner
Frost Fern: First Control Plant and Status-Effect System for Rootline Defense with a score of 9.0
Selected as the highest-scoring candidate with an average score of 9.0 across 2 judge(s).
Technical spec
Frost Fern — First Control Plant and Reusable Status-Effect System (April 17)
Add Frost Fern, the fourth plant in Rootline Defense and the first member of a new control archetype. Frost Fern does not damage enemies and does not generate sap; instead, it chills a lane zone — a rectangular segment in its own lane extending three columns toward the spawn — applying a typed slow status effect to every enemy inside. Slow reduces movement speed by 40% and attack rate by 25% (i.e., ~33% longer cooldowns and sniper aim) for 2.5 seconds. The status is refreshed (not stacked) while the enemy stays in the zone. The load-bearing work is not the plant — it is a reusable status-effect system (enemy.statusEffects map, typed entries with kind, magnitude, attackMagnitude, expiresAtMs, overwrite-refresh semantics) that Frost Fern is the first client of. The April 17 scenario teaches the mechanic in a two-wave tutorial where a Glass Ram in lane 2 is unwinnable without a chilled lane, then rolls into a 1-HP challenge whose core lesson is "identify the breaking lane and buy your attackers time" — the April 16 memorized "screen-first + Sunroot back column" opening fails on Wave 1 because a Glass Ram walking a Sniper lane overwhelms a DPS-only screen, and only a chilled lane gives the screen time to finish.
Problem
After April 16, Rootline Defense has three plants (thornVine and brambleSpear as attackers, sunrootBloom as support) and four enemies with two behaviors — briarBeetle, shardMite, and glassRam as walkers, and briarSniper as the first ranged enemy. Every player-side lever on an enemy is still damage. When a lane is under-pressure — a Glass Ram bulldozing through a lane that already hosts a Briar Sniper, or a Sniper aimed at the only Bloom — the only option the player has is to out-DPS the incoming damage. There is no tempo lever.
Concretely, site/game/src/config/plants.js (observed) has role: "attacker" an
[Spec truncated — view full spec on the site]
What changed
April 17, 2026 — Build Summary
Shipped Frost Fern, the first control plant in Rootline Defense, plus a
reusable typed status-effect system that future control plants will share.
What changed
- New
frostFernplant withrole: "control",cost: 65,hp: 28,cadenceMs: 400,chillRangeCols: 3,chillMagnitude: 0.4,chillAttackMagnitude: 0.25,chillDurationMs: 2500. No projectile, no sap pulse. - Typed status-effect map on enemies (
statusEffects), keyed by effectkind. HelpersapplyStatusEffect,tickStatusEffects,getEffectiveSpeed, andgetEffectiveCadenceexported fromplay.js. - Five cadence/speed read sites rerouted through the helpers: walker move,
walker contact cadence, sniper approach, sniper aim-init
(
aimDurationMs), and sniper cooldown refill (attackCadenceMs). -
updateControlPlantsapplies chill everycadenceMsto enemies whosexis within[fern.x − CELL_WIDTH/2, fern.x − CELL_WIDTH/2 + 3 * CELL_WIDTH]and whoselanematches the fern's row. No-stack merge: max-of-magnitudes- latest
expiresAtMs.
- latest
- Three-layer slow visuals: tint
0x8fd8ff(tint-mode MULTIPLY reset before cool-blue overlay), a frost-particle emitter attached withstartFollow(enemy)(Phaser v3.60+ particles API guarded by try/catch +typeof emitter.startFollow === 'function'), and Phaser animation frame rate scaled by(1 − slow.magnitude). - Chill-zone hover preview: when
frostFernis selected, the legal hovered tile renderschillZonePreviewatx = center.x − CELL_WIDTH/2withwidth = 3 * CELL_WIDTH. - Observation API adds per-enemy
baseSpeed,effectiveSpeed, andstatusEffects, plus per-control-defenderaoeShape: "lane-zone",aoeRangeCols,chillMagnitude,chillAttackMagnitude,chillDurationMs. - Board Scout:
.game-scout__badge--controlchip on the Frost Fern card; detail panel labels in order Cost, AoE, Slow, Attack Slow, Duration, Notes with values65,3-col lane zone,40% speed,25% attack rate,2.5s,No damage, no sap; refreshes on re-chill (no stack). - Scenario
2026-04-17.js("Cold Lane"): two-wave tutorial — Wave 1 "Hold the Lane" (availablePlants["thornVine"]) → Wave 2 "Now It's Too Fast" (availablePlants["thornVine", "frostFern"]); four-wave 1-HP challenge; endless inherited from April 16 as{ enemyPool: ["briarBeetle", "shardMite", "glassRam"], startingWave: 4,.
baseCadenceMs: 1750, cadenceFloorMs: 720, cadenceDropPerWave: 120,
waveDurationMs: 9000 } - Manifest-backed SVG art:
frost-fern(/game/assets/manual/plants/frost-fern.svg, 128×128, categoryplayer) andfrost-particle(/game/assets/manual/particles/frost-particle.svg, 24×24, categoryparticle). - Script role-heuristics in
scripts/probe-runtime-scenario.mjs,scripts/validate-scenario-difficulty.mjs, andscripts/bot-play-scenario.mjsupdated to exclude control alongside support viaplant.role !== 'support' && plant.role !== 'control'. - Replay fixtures:
scripts/replay-2026-04-17-no-control.json(expect.outcome: "gameover") andscripts/replay-2026-04-17-chilled-lane.json(expect.outcome: "cleared"). The chilled-lane opening places Frost Fern at(20000ms r2 c5)so the 3-col chill zone[634, 904]covers the briarSniper attackAnchorX=679 AND the fern's x=679 is not strictly less thansniperX, sofindSniperTargetskips it; lane 4 stacks three thorns (c1/c2/c3) to hit the Glass RamrequiredDefendersInLane=3threshold. - Projectile hit-detection now uses swept-range collision.
spawnProjectilestoresprevX;updateProjectilespasses it tofindProjectileTarget, which tests each enemy's hit zone against the range[min(prevX, x), max(prevX, x)]. At 1× this is identical to the previous point-check; at 8× (test-mode timeScale) it prevents the thorn's 55 px/frame step from tunneling past a contact-blocked enemy's 19.8 px hit radius.
Material assumptions (carried forward from task_1)
- Chill x-range formula: `[fern.x − CELL_WIDTH/2, fern.x − CELL_WIDTH/2 + 3
- CELL_WIDTH]
. A fern on columncchills the three tiles covering columnsc … c+2` (inclusive of its own tile, extending forward toward the spawn). The chill-zone preview and the runtime zone-apply share this formula.
- CELL_WIDTH]
- Tint-mode MULTIPLY is reset to the default before applying the cool-blue overlay, so the slow visual does not compound with any existing tint on the sprite.
- The frost-particle emitter uses Phaser v3.60+ particles API (
add.particles(0, 0, 'frost-particle', { … })) guarded bytry/catchandtypeof emitter.startFollow === 'function'. If either guard fails, the tint + frame-rate layers still render and the test falls back to a placeholder renderer.
Acceptance Criteria → coverage
Product
-
AC-1 (Frost Fern applies slow to in-zone lane-matched enemies within
one cadence tick) →
tests/uiux/game-frost-fern.spec.js"runtime contract" block + observationeffectiveSpeed === baseSpeed * 0.6. -
AC-2 (
statusEffects.slow.magnitude === 0.4,attackMagnitude === 0.25, resulting multipliers 0.6 / 0.75) →tests/uiux/game-frost-fern.spec.jshelper-math contract (getEffectiveSpeed(80, slow 0.4) === 48,getEffectiveCadence(700, slowAttack 0.25) === 700 / 0.75). -
AC-3 (no stack; two Ferns produce one slow entry with max magnitudes
and latest expiry) →
tests/uiux/game-frost-fern.spec.js"no-stack refresh contract" block. -
AC-4 (chill duration 2.5s;
tickStatusEffectsremoves entry atexpiresAtMs) →tests/uiux/game-frost-fern.spec.jshelper math; observationstatusEffects.slow.remainingMs > 0then 0. -
AC-5 (no damage, no sap pulse from Frost Fern) →
tests/uiux/game-frost-fern.spec.js"control/non-damage contract" block. -
AC-6 (chill applies to both walker and sniper enemies) →
tests/uiux/game-frost-fern.spec.jsruntime contract asserts slow onBriar BeetleandBriar Sniper. -
AC-7 (sniper aim duration and cooldown route through
getEffectiveCadence) →tests/uiux/game-frost-fern.spec.jshelper math + observationeffectiveCadencegrowth on chilled sniper. -
AC-8 (three-layer slow visuals: tint, following particle, scaled
frame rate) →
tests/uiux/game-frost-fern.spec.js"visual contract" block (tint0x8fd8ff, emitter or placeholder, animation pacing scales by1 − slow.magnitude). -
AC-9 (chill-zone hover preview centered at
x = center.x − CELL_WIDTH/2, width3 * CELL_WIDTH) →tests/uiux/game-frost-fern.spec.js"preview contract" block. -
AC-10 (Board Scout Control chip) →
tests/uiux/game-board-scout-2026-04-17.spec.jsandtests/uiux/game-2026-04-17-flow.spec.js(both assert.game-scout__badge--controlwith textControl). -
AC-11 (Board Scout detail labels and values) →
tests/uiux/game-board-scout-2026-04-17.spec.jsasserts labels in orderCost,AoE,Slow,Attack Slow,Duration,Noteswith values65,3-col lane zone,40% speed,25% attack rate,2.5s, and the no-stack note. -
AC-12 (Scenario April 17 shipped with tutorial + challenge + endless)
→
site/game/src/config/scenarios/2026-04-17.js; Playwright assertions intests/uiux/game-2026-04-17-flow.spec.js. -
AC-13 (Tutorial Wave 1 "Hold the Lane" with availablePlants
["thornVine"]) →tests/uiux/game-2026-04-17-flow.spec.jstutorial block. -
AC-14 (Tutorial Wave 2 "Now It's Too Fast" with availablePlants
["thornVine", "frostFern"]) → same spec, wave 2 block. -
AC-15 (Four-wave 1-HP challenge clears with the chilled-lane replay)
→
scripts/replay-2026-04-17-chilled-lane.json(expect.outcome: "cleared") verified natural bytests/uiux/game-2026-04-17-replays.spec.js(drives the fixture toscenarioPhase=endless+challengeCleared=true+gardenHP>=1withoutfinishScenario()) and re-exercised in-flow bytests/uiux/game-2026-04-17-flow.spec.js. -
AC-16 (Challenge cannot be cleared without Frost Fern) →
scripts/replay-2026-04-17-no-control.json(expect.outcome: "gameover") verified bytests/uiux/game-2026-04-17-replays.spec.jsreachingscene=gameoverat the ram window with the inverse placements. -
AC-17 (Endless inherited from April 16) →
tests/uiux/game-2026-04-17-flow.spec.jsendless assertions compare to the exact config object{ enemyPool:["briarBeetle","shardMite","glassRam"], startingWave:4, baseCadenceMs:1750, cadenceFloorMs:720, cadenceDropPerWave:120, waveDurationMs:9000 }. -
AC-18 (Manifest-backed
frost-fernSVG, 128×128,player) →tests/uiux/game-roster-assets.spec.jsmanifest + SVG fetch assertion. -
AC-19 (Manifest-backed
frost-particleSVG, 24×24,particle) → same spec, additional manifest entry + SVG fetch assertion. -
AC-20 (Observation surface:
baseSpeed,effectiveSpeed,statusEffects,aoeShape,aoeRangeCols,chill*) →tests/uiux/game-frost-fern.spec.jsobservation assertions. -
AC-21 (Control plants do not screen sniper shots; support target
routing unchanged) →
tests/uiux/game-frost-fern.spec.js"sniper/control regression contract" block. -
AC-22 (Script role-heuristic updates) →
tests/uiux/game-frost-fern.spec.js"script-role regression contract" block asserts theplant.role !== 'support' && plant.role !== 'control'pattern is present in all three scripts. -
AC-flow (Full April 17 tutorial → challenge → endless transition) →
tests/uiux/game-2026-04-17-flow.spec.jsend-to-end flow assertions onscenarioPhase,challengeCleared,gardenHP >= 1. -
AC-UI (Control chip and detail-panel copy rendered on the game page)
→
tests/uiux/game-board-scout-2026-04-17.spec.js. -
AC-regression (prior-day scenarios still clear without timing
drift from the cadence-helper refactor) → full
npm run test:uiuxsuite exercised bytests/uiux/game-*-2026-04-1{3,4,5,6}.spec.js.
Validation runs
Playwright — April 17 specs
Status: passed (13/13). npm run test:uiux -- tests/uiux/game-2026-04-17-flow.spec.js tests/uiux/game-2026-04-17-replays.spec.js tests/uiux/game-board-scout-2026-04-17.spec.js tests/uiux/game-frost-fern.spec.js tests/uiux/game-roster-assets.spec.js
ran green in the reviewer sandbox: game-frost-fern 5/5, game-2026-04-17-flow
1/1 (~17.7s), game-2026-04-17-replays 2/2 (~22.1s), game-board-scout-2026-04-17
1/1, and game-roster-assets 4/4. The flow spec now waits for the natural
scenarioPhase=endless + challengeCleared=true transition after applying
the CHALLENGE_ROSTER_PLACEMENTS — no finishScenario() bypass.
Playwright — paired replay probes
Status: passed (2/2). npm run test:uiux -- tests/uiux/game-2026-04-17-replays.spec.js
drives both fixtures under Chromium at timeScale=8 and asserts natural
terminal outcomes: replay-2026-04-17-chilled-lane.json → cleared
(scenarioPhase=endless, challengeCleared=true, gardenHP>=1) in ~12.5s
and replay-2026-04-17-no-control.json → gameover in ~9.3s. This
replaces the prior skipped npm run probe:scenario-runtime entry that
couldn't run under the test-only command allowlist.
Playwright — prior-day regression
Status: passed (26/26). npm run test:uiux -- tests/uiux/game-board-scout-interaction-2026-04-14.spec.js ... tests/uiux/game-shell-responsive-2026-04-16.spec.js
ran green in 7.3s (6 workers). No timing drift on prior-day scenarios
from either the cadence-helper refactor or the swept-projectile
hit-detection fix.
Screenshots
Status: not captured. The three requested before/after captures — (a)
April 17 challenge gameover without Frost Fern, (b) chilled-lane cleared
with 3-layer slow visuals on a chilled beetle + sniper, (c) Board Scout
Control chip + selected-detail panel for Frost Fern — require a live
Chromium launch with a screenshot path configured. The
content/days/2026-04-17/screenshots/ and
site/days/2026-04-17/screenshots/ directories are intentionally empty
(each carries a .gitkeep). Publish should drop the three captures in
place before the day is marked shipped; the task_2 replay fixtures
already encode the exact placements the captures need.
What remains to gate on at publish
- The three screenshots must be captured and mirrored to both
content/days/2026-04-17/screenshots/andsite/days/2026-04-17/screenshots/. -
node schemas/validate.js content/days/2026-04-17must pass.
Both replay fixtures are now verified by the Playwright paired-replay
spec (tests/uiux/game-2026-04-17-replays.spec.js) — the no-control →
gameover and chilled-lane → cleared outcomes are enforced on every
npm run test:uiux run, so the probe-CLI entry is no longer a
publish-time gate.
Command Garden ships one feature every day with zero human code. Follow along at commandgarden.com.
Top comments (0)