R
Roamze · Agent Swarm

Dashboard

Every feature run through the loop, with all artifacts and verdicts inline. Regenerated from docs/agent-swarm-log.json.

Last generated: 2026-04-25 12:48 UTC

Features shipped
3
In progress
0
Total est. cost
$8.00
Bugs caught by trail test
4
shipped backend ai gear-rec-polish

Gear rec polish — conditioned on season, elevation, experience

Polished the existing generateGearList AI prompt to condition recommendations on season (with alpine override for May/Oct at 12k+ ft), elevation_ft + trailhead_elevation_ft (enables gain-aware reasoning), experience_level, is_day_trip, and group_notes. Output shape changed from string[] to GearRecommendation[] with criticality, reason, confidence, and conditioned_on.

5db27f91
Started: 2026-04-21 Shipped: 2026-04-21 Branch: agent-swarm-setup
Trip scenario / user scenario
Late June, Sherman/Sheridan double 14er, two-day shakedown with Kid — his first 14er. Kyle wanted Roamze's gear list to tell Kid he needs real traction above 13k, not nag the experienced hiker about beginner items, and factor in car-camping at the TH.
Verdicts
Infrastructure reviewer
HOLD_FOR_TRAIL_TEST
Design reviewer
NO_UI_CHANGES
Trail test
PASS (after fixing 2 bugs)
Metrics
Files new
3
Files modified
3
Files deleted
1
Tests added
4
Tests total
585
Est. cost (USD)
$3.50
Artifacts
PRD docs/features/gear-rec-polish-prd.md
Research (what the researcher pulled) docs/agent-swarm/research/2026-04-21-gear-rec-polish.md
Low-level design (LLD) docs/agent-swarm/lld/2026-04-21-gear-rec-polish.md
Design critique
not yet generated
Bugs caught in trail test
  • maxTokens: 2048 in generateGearListHandler truncated real responses mid-array — real Haiku output is ~8k chars. Bumped to 4096.
  • trailhead_elevation_ft silently dropped from inline request bodies — field existed on TripContext but not on GearListRequest. Added to the request type and wired into buildTripContext.
Lessons for next loop
Canned test mocks blinded us to response-size truncation and request↔context field drift. Neither bug was reachable without a live API call. Next loop: add a throwaway live-call trail-test script to the builder pass so these surface before review.
shipped infrastructure agent-swarm agent-swarm-designer-setup

Designer subagent + 3 design skills

Added a fourth subagent (designer, Haiku) and three Roamze-specific design skills: design-critique (Tailwind/shadcn token discipline, hierarchy, state coverage), ux-copy (Roamze voice, CTA/error/empty state tone), outdoor-ux (units respect user settings, offline states, trust signals). Designer runs in parallel with the infrastructure reviewer on UI features and short-circuits with NO_UI_CHANGES for backend-only diffs.

5ac9389e
Started: 2026-04-21 Shipped: 2026-04-21 Branch: agent-swarm-designer
Trip scenario / user scenario
N/A — this was a swarm-infrastructure change, not a product feature.
Verdicts
Infrastructure reviewer
MANUAL_REVIEW
Design reviewer
N/A
Trail test
N/A
Metrics
Files new
4
Files modified
2
Files deleted
0
Tests added
0
Est. cost (USD)
$0.30
Artifacts
PRD
not yet generated
Research (what the researcher pulled)
not yet generated
Low-level design (LLD)
not yet generated
Design critique
not yet generated
Bugs caught in trail test
None — or trail test not yet run.
Lessons for next loop
Model-choice inconsistency between agent frontmatter and plan doc caught by the follow-up verification step. Keep agent frontmatter authoritative; reconcile docs to match.
shipped web-next explore-search-bar

Explore page — WHERE/WHEN/ACTIVITY search pill in top nav

Ship the existing SearchPill on /explore (previously hidden until scrolled) and add quick-date chips above the date picker — 'This weekend', 'Next weekend', and a conditional 'Memorial Day weekend' chip inside a 28-day window. Search fields integrate into the existing Navigation component inline between logo and Messages/Alerts on desktop; collapsed pill opens a full-screen sheet on mobile. First UI-heavy feature to exercise the designer subagent — three design iterations to get the nav integration right.

de32d6be
Started: 2026-04-21 Shipped: 2026-04-21 Branch: feat/search-pill-quick-date-chips
Trip scenario / user scenario
Wednesday night, planning Memorial Day weekend. Kyle opens /explore, wants to filter to 'Colorado, May 23-25, day hike' without typing a natural-language query. Taps the pill, taps the Memorial Day quick-date chip (visible 28 days out), picks Colorado, sees filtered results in-page.
Verdicts
Infrastructure reviewer
PASS
Design reviewer
APPROVE (iter 3)
Trail test
PASS
Metrics
Files new
7
Files modified
3
Files deleted
0
Tests added
15
Tests total
308
Est. cost (USD)
$4.20
Artifacts
PRD docs/features/explore-search-bar.md
Research (what the researcher pulled) docs/agent-swarm/research/2026-04-21-explore-search-bar.md
Low-level design (LLD) docs/agent-swarm/lld/2026-04-21-explore-search-bar.md
Design critique docs/agent-swarm/design/2026-04-21-explore-search-bar-iter3.md
Bugs caught in trail test
  • Initial build added the search bar as a separate sticky row below the nav instead of integrating inline into the existing Navigation component — caught by side-by-side screenshot comparison with the home page, which already has the correct pattern. Required a second iteration to refactor into the existing Navigation.tsx instead of scaffolding a parallel component tree.
  • Memorial Day chip hidden on Apr 21 because the 28-day window put the holiday 32 days out. Kept the 28-day window as-is; chip will render starting Apr 25. Manual trail test used explicit date picker instead.
Lessons for next loop
Listing component filenames in the LLD ('create WhereField.tsx, WhenField.tsx, ActivityField.tsx') misled the builder into scaffolding five net-new components instead of extending the existing SearchPill + Navigation. Next loop: LLD should describe behavior + integration point ('extend existing X, do not create parallel components') and leave filenames to the builder. Also: side-by-side screenshot comparison with an analogous existing page (home vs. /explore) surfaced the layout miss in one glance — worth making screenshot diff a step in trail test when a visual analog exists elsewhere in the app.
shipped-staging backend web-next ai email jobs ai-lifecycle-marketing

AI Lifecycle Marketing — Dormant Email Campaign

Daily cron scans for users dormant 30+ days and sends an AI-personalized re-engagement email. Haiku 4.5 generator + reviewer pair with a $20/day in-process cost cap, lifetime cap of 2 sends per user, 21-day spacing, 10% deterministic holdout via hash(user_id) mod 10, US-only via timezone allowlist (D11), HMAC-SHA256 signed one-click unsubscribe (90-day expiry), CAN-SPAM footer, Resend webhook with manual Svix signature verification, and an immersive indigo photo-led email template inspired by onX Backcountry. Currently live on staging; production rollout gated on running migration 129 against prod Supabase and setting the LIFECYCLE_* env vars on the prod Cloud Run service.

37d81315
Started: 2026-04-21 Shipped: 2026-04-22 Branch: feat/ai-lifecycle-marketing
Trip scenario / user scenario
Imagine a user who saved a Smith Rock overnight back in February, logged one completed experience, and hasn't opened Roamze in 35+ days. On the daily 9am local run, the candidate query finds them, the generator writes a short note that references Smith Rock and Leadville by name, the reviewer scores it 23/25 and greenlights, Resend delivers it to Gmail. If the reviewer fails on safety (invented closure) or specificity, we don't send. Second follow-up ~3 weeks later explicitly signals it's the final check-in.
Verdicts
Infrastructure reviewer
PASS
Design reviewer
APPROVE (immersive indigo iter)
Trail test
PASS (after Option 3 seasonal fix + logo fix + migration 129)
Metrics
Tests added
100
Est. cost (USD)
$12.00
Artifacts
PRD docs/features/ai-lifecycle-marketing.md
Research (what the researcher pulled)
not yet generated
Low-level design (LLD) docs/agent-swarm/lld/2026-04-21-ai-lifecycle-marketing.md
Design critique
not yet generated
Bugs caught in trail test
  • contextPacket.ts selected non-existent scalar columns (location_name, activity_type) when the experiences schema stores location as JSONB and uses experience_type enum (migration 005). The 400s were silently absorbed because the code destructured `{ data }` without `{ error }`, so every user hit the NO_SEND sentinel. Fixed with correct column names + explicit error logging on every Supabase call.
  • signUnsubscribeToken test was flaky (~6.5% rate) because it flipped the LAST base64 char of the sig — the last char encodes only 4 real bits, so some flips decoded to the identical buffer. Fixed by flipping the first char instead.
  • Logo invisible in Gmail — used a colored SVG + CSS `filter: brightness(0) invert(1)` to whiten it at render time, but Gmail strips CSS filters on <img>, producing indigo-on-indigo. Fixed with a pre-whitened PNG hosted on Supabase Storage behind LIFECYCLE_LOGO_URL env var.
  • Seasonal hallucinations — 2 of 3 early drafts failed reviewer on Safety axis with phrases like 'Leadville's high country is waking up' and 'snowmelt's running.' The season field in the context packet was derived from month (not observed) and the generator treated it as ground truth. Option 3 fix: dropped the season field entirely so the generator can only reference actual input data.
  • Migration 129 (campaign_sends + email_events tables) was not applied to staging at first. Silent-error-absorption in the job's Supabase writes masked this — sent:1 was reported even though no row was being written. Kyle applied the SQL manually to staging; prod still pending. Root-cause of the silent absorption is tracked in GH #227.
Lessons for next loop
Three separate bugs traced back to the same anti-pattern: `const { data } = await supabase.…` without destructuring `error`. The job ran green for days while silently hitting 400s, a missing table, and a schema mismatch. Next loop: builder skill should flag any Supabase call that destructures without `error`. Second lesson: AI-generated marketing copy will invent seasonal / weather / trail conditions if any derived-but-not-observed signal is in the prompt — safer to remove the signal from the context packet entirely than to try to prompt the model into not using it. Third lesson: trail-test must include a real inbox render (Gmail + Apple Mail at minimum), not just a localhost preview — the CSS-filter-strip on logos only surfaced in Gmail.
shipped-staging backend web-next email jobs webhooks migrations welcome-email

Welcome Email — Post-Confirmation Founder Note

Webhook-driven welcome email sent ~5 minutes after a user confirms their email. Replaces the legacy synchronous signup-time send (which fired before bot-signup verification + had SaaS-speak copy + no engagement tracking). Architecture: Supabase auth.users UPDATE webhook → POST /api/webhooks/supabase-auth-confirmed → enqueue pending_welcome_emails (5-min delay) → Cloud Scheduler fires every minute → /api/jobs/welcome-email-drain claims due rows via FOR UPDATE SKIP LOCKED → renders WelcomeEmail.tsx → sends via Resend → stamps campaign_sends. Triple idempotency: UNIQUE on pending_welcome_emails(user_id), partial UNIQUE on campaign_sends(user_id) WHERE slug='welcome', and a transition guard in the webhook handler. onX-style editorial composition on indigo: tight 60px logo bar, full-bleed Patagonia hero with massive 'YOU'RE IN.' overlay, narrow 440px body column, white-fill pill CTA. Currently live on staging; production deferred to a separate session (Supabase webhook config + flag flip pending).

5a82fd79
Started: 2026-04-23 Shipped: 2026-04-25 Branch: feat/welcome-email
Trip scenario / user scenario
New user signs up via the Roamze frontend at 8pm on a Sunday. Frontend calls /signup → public.users row created. Supabase sends a confirm-your-email link. User clicks the link from their phone at 8:02pm. Behind the scenes: auth.users.email_confirmed_at gets set, Supabase DB webhook hits the backend, handler enqueues a pending_welcome_emails row with fire_at=8:07pm. Cloud Scheduler fires the drain at 8:08:00pm, picks up the row, renders the 'YOU'RE IN.' email, sends via Resend. User gets the email at 8:08:30pm — 6 minutes 30 seconds after clicking confirm. They open it, see the founder note from Kyle, click 'Find your first trail', and land on /discover with utm_source=welcome attribution intact.
Verdicts
Infrastructure reviewer
PASS (27 new tests, all green)
Design reviewer
APPROVE (v5 onX composition on indigo)
Trail test
PASS (5m36s end-to-end, magic-link path, sent + delivered)
Metrics
Tests added
27
Est. cost (USD)
$8.00
Artifacts
PRD docs/agent-swarm/prd/2026-04-23-welcome-email.md
Research (what the researcher pulled)
not yet generated
Low-level design (LLD) docs/agent-swarm/lld/2026-04-23-welcome-email.md
Design critique
not yet generated
Bugs caught in trail test
  • v1 hero showed a generic orange-circle SVG placeholder — fallback URL was `${APP_URL}/og-image.png` (the brand OG illustration), wrong tone for welcome. Fixed by uploading a real outdoor photograph (Patagonia hikers) to Supabase Storage at experience-photos/brand/welcome-hero.jpg and hardcoding that as the WELCOME_HERO_IMAGE_URL fallback so preview renders correctly without env config.
  • Three design iterations before composition lock: v2 was full-indigo immersive with 'You're in.' at 42px (too small, looked like lifecycle), v3 dropped the outer-darker bg per Kyle's feedback but still felt undifferentiated, v4 swung too far to a cream 'letter' variant that abandoned the brand palette, v5 landed on indigo + onX-style editorial composition — tight header, full-bleed hero, 96px overlay, 440px column, pill CTA, 3-line minimal footer. Lesson lives in the `lessons` block.
  • Schema-verification pass during LLD assumed an auth.users → public.users sync trigger on signup. Wrong — public.users is created by the backend's /signup route, not a DB trigger. Production reality is fine (frontend always calls /signup before user clicks confirm) but the trail-test admin-API path bypassed /signup, so I had to manually INSERT public.users to simulate it. LLD failure-modes table updated to reflect that the 'no_public_user' edge case is more theoretical than first thought.
  • On the first end-to-end run, Kyle created a test signup before flipping WELCOME_EMAIL_ENABLED=true → webhook fired but handler short-circuited with 'kill_switch' (correct behavior). Not a bug, just a sequencing reminder: configure webhook + verify acceptance with flag off, then flip flag, then test.
Lessons for next loop
Pattern-match the reference reference, not the surrounding palette. The lifecycle email's editorial composition principles (full-bleed hero, massive display type, narrow body column, pill CTA, short content) work in any color — including indigo. Color is brand; composition is craft. The cream 'letter' variant in v4 was an over-correction that conflated the two — Kyle's actual ask had always been better composition, not a different palette. Three design iterations could have collapsed to one if the reviewer pass had asked 'would this composition still work in the original palette' before committing to a palette swap. Add that question to the design-critique skill.