The Bee Team . Product Canon
What is live today - Pollinator Oasis
Version
v1.0.10
Status
Live . canon
Brand
Crown Bees
btt-server v61
client EAS build 24
main 69ed4b5
Contents
The current-state map of Pollinator Oasis - the shipped screens, the auth reality, the routes, and the design system - reconciled against the running code, never speculative.
Durable document (designated Session 310, 2026-07-05). One of the three canonical project documents per the Hub methodology [[10-Three-Document-Product-Canon]] (~/Documents/Claude/ Vaults/The Hub/25 Methodology/10-Three-Document-Product-Canon.md). This is the As-Built - it answers "what is live right now?" and is the first document a session reads to establish product state, before the git log. Version: v1.0.10 (v1.0 MVP + BT-094 security response headers + the S322 GDD-chart cache-invalidation fix + the BT-062 in-app "Ask" / AI BeeBot feature + BT-106 native guest sign-in + v1.0.5 login/nav polish + v1.0.6 forecast-day pollen & GDD/Ask UI + v1.0.7 evening-pollen fix + v1.0.8 server-hardening fixes + v1.0.9 cache-log coordinate redaction + v1.0.10 weather setup-form disabled-button visual; current production). Production coordinates: btt-server v61, client EAS build 24, productmain69ed4b5. Living copy: this file. Dated, version-stamped snapshots underdocuments/snapshots/. Companions (the canon is four documents): UX Flow Atlas (ux-flow-atlas.md- the desired experience, persona-by-flow), To Build Spec (next-build-spec.md- what we build next + the plan: roadmap, LOE, prioritization), and Design System (design-system.md- the look-and-feel basis, referenced from Section 4). Fidelity: the built product = production truth, reconciled against the running code - never speculative.
Fidelity note. This v1.0 captures the current-state map (what ships, the auth reality, the routes, the design system) at a trustworthy summary fidelity, reconciled against the code. The per-screen pixel-grade As-Built detail (every shipped screen's exact layout, tokens, and components) is deepened from the design-system reference and per-screen recon in a later pass; the summary below is authoritative for "what is live" today. Production screen captures (the visual As-Built) now live in Section 3b.
v1.0 / MVP gate met (2026-07-05, Session 314). v0.9 + the in-flight auth work shipped to production at btt-server v50: the email-OTP flow is now wired into the app UI (welcome-hero- first, email-step-first) and the login captcha carries the canonical circle-less bee icon. The deploy is machine-verified - the v50 bundle served from app.mybeecloud.com has the prod Supabase host, the real Turnstile site key (not the test-key), and the same-origin /trpc base all baked; the active machine's health check passes; root serves HTTP 200. The interactive on-device e2e (Turnstile renders + Guest anonymous sign-in + email-OTP all complete on the real host) is Scott's confirmation step, pending at the time of this fold - not asserted here.
Section 1
| Fact | Value |
|---|---|
| Version | v1.0.10 (v1.0 MVP + BT-094 security response headers + S322 GDD-chart cache-invalidation fix + BT-062 in-app "Ask" / AI BeeBot + BT-106 native guest sign-in + v1.0.5 login/nav polish + v1.0.6 forecast-day pollen & GDD/Ask UI + v1.0.7 evening-pollen fix + v1.0.8 server-hardening fixes + v1.0.9 cache-log coordinate redaction + v1.0.10 weather setup-form disabled-button visual) |
| Production server | Fly.io app btt-server, v61 (deployed 2026-07-12, Session 364 - BT-023/E2 cache-log coordinate redaction; v60 = v1.0.8 server-hardening fixes from the S359 full-build review, 2026-07-12, Session 363; v59 = BT-115 evening-pollen fix, 2026-07-11, Session 357; v58 = BT-115 forecast-day pollen, 2026-07-11, Session 355; v57 = BT-106 native guest sign-in, 2026-07-10, Session 349; v56 = BT-113 fail-closed rate limiters, 2026-07-10, Session 346; v55 = BT-101 native email-OTP server-mediated send, 2026-07-09; v54 = BT-062 in-app "Ask" / AI BeeBot, Session 325; v53 = the S322 GDD-chart fix; v52 = BT-094 headers, Session 317; v50 = the v1.0 MVP, Session 314) |
| Product repo | ~/projects/the-bee-team/, main @ 69ed4b5 |
| Production host | app.mybeecloud.com |
| v1.0 (MVP) gate | MET - the in-flight auth work (email-OTP wired into the app UI) shipped at v50 |
| v1.0.1 delta | BT-094 security response headers at the Fly origin - enforcing CSP (@fastify/helmet, config-derived source lists) + HSTS 1yr/includeSubDomains (no preload) + X-Frame-Options DENY + nosniff + Referrer-Policy strict-origin-when-cross-origin + Permissions-Policy (geolocation=self, rest disabled). Shipped Report-Only (v51) -> walked to zero live prod violations -> flipped enforcing (v52). Server suite 370/370. |
| v1.0.2 delta | GDD-chart cache-invalidation fix (btt-server v53, Session 322): addPlant/removePlant now invalidate garden.yardAwareness so the My Yard chart refreshes without a remount. |
| v1.0.3 delta | BT-062 in-app "Ask" / AI BeeBot (btt-server v54, Session 325): the center orange "Ask" FAB opens /(app)/ask. Native (iOS/Android) embeds the Crown Bees / Rep "AI BeeBot" assistant in a WebView (device-smoked green, S322). Web renders a graceful ask.web.tsx state (the canonical BeeIcon + "AI BeeBot", framed by the app chrome) - an app-owned .web.tsx split that keeps native-only react-native-webview (which has no web build) out of the web bundle, so /ask on web shows the badge, not rnw's "does not support this platform" error. Merge integrated cleanly with v53; QA green (typecheck 0 / eslint 0 / client jest 143 / server 370 / build:web ok); the deployed /ask web render machine-verified (BeeIcon "AI BeeBot" state, no red error, 0 page errors). Rep boundary: BT-037 (no hosted AI - Rep is a partner-provided webview assistant), BT-056 (single-click-out), BT-061 (revenue separability). |
| v1.0.4 delta | BT-106 native guest sign-in (btt-server v57, Session 349): the native "Guest" button now works. Previously supabase.auth.signInAnonymously() hit Supabase's project-wide captcha gate (Turnstile is web-only), so native Guest always returned "Could not start a guest session". Fixed the BT-101 way - a server-mediated auth.guestSignIn tRPC mutation authenticated as service-role (bypasses captcha to GoTrue source), guarded by a new fail-closed per-IP guest limiter (btt:rl:guest-ip, 5/hr, via the BT-113 withFallback composition); native calls it then supabase.auth.setSession(...). Web unchanged (Turnstile still gates it). A wire-transport defect caught at delivery (the z.void() mutation over httpLink sent an empty body Fastify 400'd before the resolver ran) was fixed by moving the native auth client to httpBatchLink + a new SP-7 HTTP wire test (auth-guest.http.test.ts). QA green (server 424/424, client 159/159, typecheck 0 / eslint 0 / ASCII clean); on-device verified on iOS TestFlight build 17 (Guest AND email OTP both work). |
| v1.0.5 delta | Login send-feedback + first-tap fix + bottom-nav sizing (client EAS build 20; server unchanged at v57). The login send button shows a "Sending . . ." in-flight label and keyboardShouldPersistTaps="handled" fixes the swallowed first tap (b38666e, Session 351); bottom-nav icons + labels enlarged 24->28 / 10.5->12 with the center AI FAB frozen, RED-first test guarding the sizes (7c20794, Session 353). Client-only, no server deploy. |
| v1.0.6 delta | BT-115 forecast-day pollen + GDD off the Weather daily card + Ask label spacing (btt-server v58; client EAS build 22, superseding 21). Weather forecast pollen is now per-date Google Pollen (days=5) aligned to the selected forecast day by ISO date (79fe2c3, Session 355, server v58); GDD removed from the Weather daily card (GDD is the Yard's readout; plants/detail untouched) and all five nav labels aligned to 0.1 letterSpacing (f61c7a0, Session 355, build 22). RED-first tests both. |
| v1.0.7 delta | BT-115 evening-pollen fix (btt-server v59; build 22 unchanged). pollen-service.ts rebuilt as a rolling per-location day-union cache that survives Google's 00:00-UTC forecast rollover, plus a Redis persistence tier (pollen:days:<coord>, 48h TTL, best-effort). RED-first 9 new tests; server 439/439, client 168/168, tsc + eslint clean (ab829c7, Session 357, server v59). SHIPPED - awaiting Scott's evening-window verification (2026-07-12). |
| v1.0.8 delta | Server-hardening fixes from the S359 full-build review (btt-server v60, Session 363; client build 22 unchanged). Nine server-side fixes, each RED-first: coordinate-free weather error surface (svc-2, a live BT-023 fix - errors no longer leak raw lat/long), graceful SIGTERM/SIGINT shutdown so deploys stop cutting in-flight work (dat-2), postgres prepare:false for the Supabase transaction pooler (dat-3), an AbortSignal timeout on the LocationIQ geocoder (svc-1), a fire-and-forget best-effort pollen Redis write (svc-4), SUPABASE_SERVICE_ROLE_KEY shape validation at config load (BT-103), and a UNIQUE (user_id, plant_key) constraint on garden_plant with an idempotent onConflictDoNothing addPlant so a double-tap no longer creates duplicate cards (rtr-1). Migration 0023 (dedupe-before-constrain) applied to prod: 0 rows deleted (no existing dupes), constraint validated/enforcing. Gates: server 467/467, typecheck + lint clean. Pre-deploy key gate passed (the live prod service-role key passes the new shape guard). Fast-forward merge fa95cce..7019363; folds the non-deployed dat-1 dev-seed guard (fa95cce). |
| v1.0.9 delta | Cache-log coordinate redaction (btt-server v61, Session 364; client build 22 unchanged). The weather (service.ts, DEBUG) and pollen (pollen-service.ts, WARN) best-effort Redis cache helpers logged the raw cache key - which embeds a 2dp (~1.1 km) coordinate (wx:forecast:<lat>,<lon> / pollen:days:<lat>,<lon>) - and the cache-op timeout embedded that key in its Error message, so the coordinate also rode out inside the logged err. A shared redactCoordKey (in coords.ts) now replaces the coordinate tail with <coord> across all 6 log/error vectors (both services: timeout message + get log + set log), keeping the useful key namespace. Defense-in-depth extending BT-023 (whose letter is user surfaces) to operator logs (E2). Zero end-user impact - the real cache key is unchanged, so cache behaviour, forecast data, and API responses are byte-for-byte identical; only what is logged is redacted. No migration, no client change. RED-first (10 tests failed for the right reason -> GREEN); gates server 475/475, typecheck + lint clean. Commit bd8e102 (pushed, deployed); reversible to v60 (deployment-01KXC6PH...). |
| v1.0.10 delta | Weather setup-form disabled-button visual (client EAS build 24, Session 368; btt-server v61 unchanged). Both Weather location-setup action buttons (set-address-button + use-device-location-button in apps/client/app/(app)/weather.tsx) now apply an inline style={{ opacity: 0.5 }} when disabled, matching the shared LocationSetup component's styles.btnDisabled - previously they rendered solid bg-cb-green while inert, so an empty-address / mid-mutation button looked tappable (the S366 A2 Phase 2 native-verify defect, BT-117/wx-1). Inline style, not a NativeWind opacity-50 class (which does not resolve to an inspectable style on raw RN components). RED-first (3 tests); client 177/177, typecheck + eslint clean. Commit 69ed4b5, client EAS build 24. Client-only, no server deploy. Shipped to TestFlight via the altool direct-upload bypass - the EAS submission queue stalled undispatched ~16 min and was cancelled (CANCELED, no double-upload), then the binary uploaded to App Store Connect in 5.8 s (Delivery UUID e12e8959...), processingState VALID. |
Section 2
Shipped auth = email one-time-code (OTP) / magic-link via Supabase, delivered via Resend.
Sessions 299-301. Resend is the transactional email service (decision D3, S161) and the likely SMTP delivery path for the auth emails (delivery detail to confirm).
history that production auth is Shopify is wrong - it is email-OTP. Consult this document, not the git log, for the auth reality.
works and the banner is gone"). Production is now v50.
welcome-hero-first, then email-step-first (37e4caa); the login captcha carries the canonical circle-less bee icon (f293004 + 69e7b18). This wiring + deploy is exactly the v1.0 / MVP work that closed the gate. The v50 bundle is machine-verified (prod Supabase host + real Turnstile key + same-origin /trpc all baked; health passing). The interactive on-device e2e - Turnstile renders, Guest completes the anonymous sign-in, email-OTP completes end-to-end on the real host - is Scott's confirmation step, pending at fold time (the plain preview cannot exercise it: test-key Turnstile tokens are server-rejected by Supabase and the static preview has no backend - an environmental artifact proven in S312/S313, not a product defect).
visual branding - independent branding is BT-095, post-MVP).
Section 3
The shipped app is the post-login product the onboarding funnel (Atlas) will feed into.
Pre-auth: a login / Welcome screen (login.tsx) with the wired email-OTP entry (welcome-hero- first -> email-step-first) and a web captcha (Turnstile) captioned with the canonical circle-less bee icon. Guest (anonymous sign-in) and email-OTP are the two entry paths.
Authenticated app (routes):
| Screen | Route / note |
|---|---|
| Home | /(app)/welcome - weather box, a GO/WAIT/HOLD release-decision banner, top alert, this-week's-advice, yard-awareness, Shop. This is the post-login Home, not the pre-auth welcome (that lives in login.tsx) - keep the two straight. |
| Weather | forecast + pollen + location setup |
| Ask | /(app)/ask - the center orange "Ask" FAB. Native: embeds the Crown Bees / Rep "AI BeeBot" assistant in a WebView (device-smoked green, BT-062). Web: a graceful ask.web.tsx state (canonical BeeIcon + "AI BeeBot"), the .web.tsx split keeping native-only react-native-webview out of the web bundle. Partner-assistant boundary: BT-037 / BT-056 / BT-061. |
| My Yard | release decision, seasonal GDD / bloom chart, plant cards, add/remove |
| Advice | species to-dos |
| Plant detail | per-plant detail |
| Diary | seasonal diary - orphaned on native (only the desktop-web header nav routes here, SiteHeader.tsx:54; hidden below 980px, not in the bottom tab bar) |
| Alerts | alert feed |
| Notifications | currently orphaned (no in-app way to reach it) |
| Profile | account / profile |
| Paywall | present but inert / unreachable in-app |
Facts that shape the next builds:
69ed4b5,Session 376): Notifications is orphaned (BT-117; the header bell routes to Advice, not here), Paywall is inert / unreachable (BT-024), and Diary is orphaned on native (its only navigation is the desktop-web header menu SiteHeader.tsx:54, hidden below 980px; not in the bottom tab bar). The funnel must wire these entry points. v1.0 / MVP shipped auth-only; wiring Notifications + Paywall is tracked (BT-117 notifications entry; BT-024 paywall / subscription).
server-mediated captcha bypass instead (BT-113 owns the abuse-control question).
pnpm dev, press i / a`).
Section 3a
main 69ed4b5)Per BT-102 Section 1a #2 / the Hub SOP: the route file, key @btt/ui components, and backing server procedures per screen, at the shipped commit. tRPC namespaces map to apps/server/src/routers/<namespace>.ts (barrel: apps/server/src/router.ts); @btt/ui components live in packages/ui/src/components/ + charts/ (see the Design System doc, Section 5).
| Screen | Route file (apps/client/) | Key @btt/ui | Backing procedures |
|---|---|---|---|
| Home | app/(app)/welcome.tsx -> components/WelcomeScreen.tsx | DecisionBanner, AlertCard, YardAwarenessCard, WeeklyTipSheet, Card, Button, Typography | garden.getReleaseDecision, garden.yardAwareness, weather.forecast, weather.myLocation, alerts.list, tip.current / markSeen / maybeLater, subscription.status |
| Weather | app/(app)/weather.tsx | Card, PollenGauge, Typography | weather.forecast, weather.myLocation, weather.pollen, weather.setLocation |
| Ask | app/(app)/ask.tsx (+ ask.web.tsx) | colors (WebView-based; Ionicons) | none - embeds the Rep "AI BeeBot" WebView (BT-062); web = ask.web.tsx graceful state |
| My Yard | app/(app)/plants.tsx | PlantCard, AddPlantCard, BloomConfirmRow, DecisionBanner, GddYearChart, Pill, Card, Button | garden.listGardenPlants / addPlant / removePlant / getProfile / upsertProfile / getReleaseDecision / yardAwareness / confirmBloom / deferBloom, weather.gddYear / myLocation |
| Advice | app/(app)/advice.tsx | SpeciesAccordion, ActionDetailSheet, Typography | garden.adviceFeed, garden.markAdviceDone, weather.myLocation |
| Plant detail | app/plant/[key].tsx | DetailHero, GddBar, KvList, Card, Button, Typography | garden.getProfile, garden.getReleaseDecision, weather.myLocation |
| Diary | app/(app)/diary.tsx | Card, Pill, Button, Typography | diary.list, diary.create, diary.remove |
| Alerts | app/(app)/alerts.tsx | AlertFeedRow, Card, Typography | alerts.list, weather.myLocation |
| Notifications (orphaned) | app/(app)/notifications.tsx | Card, Button, Typography | notification.list / markRead / markAllRead |
| Profile | app/(app)/profile.tsx | DetailHero, KvList, Card, Button, Typography | user.me, garden.listGardenPlants, notification.getPreferences / setPreference, weather.myLocation |
| Paywall (inert) | app/(app)/paywall.tsx -> components/PaywallScreen.tsx | Card, Pill, Button, Typography | none wired |
| Login (pre-auth) | app/login.tsx | Hero, StepCard, Card, Button, Typography | session.authStatus; auth itself = Supabase email-OTP + auth.guestSignIn (native guest, server-mediated; Section 2 + routers/auth.ts) |
| Entry | app/index.tsx | (Redirect) | - |
Chrome (every signed-in screen): SiteHeader + BottomTabBar in apps/client/components/, wrapped by app/(app)/_layout.tsx. See the Design System doc Section 5.
Note - captures done. The pixel-perfect production screen captures (BT-102 Section 1a #1) are now in as-built-screens/ and shown in Section 3b - real device captures from TestFlight build 24 (69ed4b5, v1.0.10), 2026-07-14 (Session 376). This closes the pixel-perfect half of BT-102 Unit 2c (9 of 12 real screens; the 3 native-orphaned screens are documented, not fabricated).
Section 3b
Real captures from the shipped native iOS app - TestFlight build 24 = 69ed4b5 = v1.0.10, on a real device (1290 x 2796), 2026-07-14 (Session 376). Auth and data are real (Supabase email-OTP + guest). True production pixels - not schematics or mockups. The full set (28 captures, including every state below) plus a per-file manifest lives in as-built-screens/ (as-built-screens/README.md). This closes the pixel-perfect half of BT-102 Unit 2c.
9 of the 12 real screens are captured. Three ship with no native entry point and are documented, not fabricated: Diary (desktop-web only), Notifications (orphaned), Paywall (inert) - see Section 3 and the manifest. (index.tsx Entry is a bare redirect, not a real screen.)
Home - welcome (post-login: weather box, GO / WAIT / HOLD banner, top alert, this-week's advice, yard awareness)
Weather - weather (forecast day tabs + pollen gauge)
Ask - ask (the center orange FAB; the Rep "AI BeeBot" assistant in a WebView)
My Yard - plants (release decision, seasonal GDD / bloom chart, plant cards)
Advice - advice (species to-dos, grouped by species)
Plant detail - plant/[key]
Profile - profile (signed-in Crown Bees account)
Alerts - alerts (weather + pollinator alert feed; reached via the Home alert card, not the tab bar)
The value-first welcome, the email-OTP sign-in, and location setup - the entry sequence into the app.
Welcome, then "How it works":
Email code, then 6-digit verify:
Location permission, confirm yard, into Home:
myyard-empty.png), add-a-plant picker (myyard-addplant.png), yard-size selector (myyard-yardsize.png).advice-nolocation.png), tip / action detail (advice-tip.png).home-guest.png, profile-guest.png, profile-guest-notifications.png, profile-guest-delete.png).Diary, Notifications, and Paywall are present in the build but cannot be reached by tapping on a phone (Section 3). No fabricated captures were made; a deep-link / dev-route capture is a possible future pass.
Section 4
The app's live skin is the shipped Crown Bees Design System - Inter throughout, the exact green / orange tokens, @btt/ui components. Building new screens is compose-from-the-kit, not a reskin.
#004322 = frame / heading / structure; orange#e69100 = the ONLY call-to-action color, used sparingly. Status (GO / WAIT / HOLD) #28a745 / #eb9247 / #e95050. Warm neutrals only (never cold grey).
SiteHeader (green app bar, orange-bee CROWNBEES wordmark, notifications bell,avatar, orange TopProgressBar underline) + BottomTabBar (Home / Weather / center orange "Ask" FAB / Yard / Advice; the FAB is the Rep-AI entry point - live at v54, BT-062).
SiteHeader) and the 5-itemBottom Tab Bar above are the settled navigation canon - confirmed by Scott with Dave and Kellie (discussed 2026-07-13). Alternatives proposed in the UX Flow Atlas (e.g. Dave's Home / Bloom / Bees / Shop / Ask bottom bar) are tracked deltas against this baseline, not open questions. The three native-orphaned screens (Diary, Notifications, Paywall) sit outside this settled nav and remain known entry-point gaps against it.
documents/design-system.md (v1.0) - the reference-hub over the brand authority (~/.claude/skills/crown-bees-design/colors_and_type.css) and the implemented tokens (packages/ui/src/tokens/*.ts), with the full narrative skin write-up at outputs/s305/THE-BUILD-SPEC.md Appendix A. Read the DS doc for the token/component vocabulary.
Section 5
Reconciled against the code (apps/server/src/lib/email.ts, the expo-router tree) and the Session 305 build spec (outputs/s305/THE-BUILD-SPEC.md Part 2), not asserted from memory or the git history. This document supersedes any product-state inference from git.
*As-Built Spec v1.0.10 - The Bee Team. v1.0 (MVP): Session 314 - the in-flight auth work (email-OTP wired into the app UI + the canonical bee captcha icon) deployed to production at btt-server v50 on 2026-07-05, closing the MVP gate (v0.9 -> v1.0). v1.0.1: Session 317 (2026-07-05) - the BT-094 security response headers at the Fly origin (v51 Report-Only, walked to zero live prod violations, then v52 enforcing). v1.0.2: Session 322 (2026-07-06) - the GDD-chart cache-invalidation fix (btt-server v53). v1.0.3: Session 325 (2026-07-07) - the BT-062 in-app "Ask" / AI BeeBot feature (btt-server v54). v1.0.4: Session 349 (2026-07-10) - BT-106 native guest sign-in (btt-server v57). v1.0.5: Sessions 351/353 (2026-07-10) - login send-feedback + first-tap fix + bottom-nav sizing (client EAS build 20). v1.0.6: Session 355 (2026-07-11) - BT-115 forecast-day pollen (btt-server v58) + GDD off the Weather daily card + Ask label spacing (build 22). v1.0.7: Session 357 (2026-07-11) - BT-115 evening-pollen fix (btt-server v59). v1.0.8: Session 363 (2026-07-12) - nine server-hardening fixes from the S359 full-build review, migration 0023 applied to prod (0 rows deleted) (btt-server v60). v1.0.9: Session 364 (2026-07-12) - BT-023/E2 cache-log coordinate redaction across the weather + pollen Redis cache helpers (btt-server v61). v1.0.10: Session 368 (2026-07-13) - the weather setup-form disabled-button visual (BT-117/wx-1; client EAS build 24, shipped to TestFlight via the altool bypass); product main @ 69ed4b5. Session 376 (2026-07-14) added Section 3b, the pixel-perfect production screen gallery (28 real TestFlight build 24 captures) + the Diary native-orphan reconciliation - a docs enrichment, no version change (production unchanged). Production truth for "what is live"; machine-verified at deploy. Change history lives in git + the LMA records + the dated snapshots, never this footer.*