# PixieSprout — Intelligent Multi-Modal Care Architecture

**Status:** Design (pending Bekky approval)
**Author:** Pixie (PM)
**Date:** 2026-08-13
**Scope:** Replace the regex text-parsing care engine with a context-aware, AI-driven care engine that reflects environment, setup, placement, region, weather, indoor/outdoor, and sun exposure — plus a photo-update loop that refreshes care metrics over time.

---

## 1. Goal & Non-Goals

### Goal
Every plant's care tasks are generated from a **structured care schedule** that the AI produces *given the plant's real context* (environment, setup, placement, region, weather, indoor/outdoor, sun exposure) — and that schedule **adapts over time** as the user submits progress photos and completes care tasks.

### Non-Goals (this phase)
- No hardware sensors (sensorContext stays null for now).
- No community features.
- No seed-packet scan.
- No change to the sick-plant treatment loop (already works — `buildTreatmentCareTasks`).
- No native modules (this is JS-only → OTA-shippable).

---

## 2. What Already Exists (verified on disk 2026-08-13)

| Context | Where it lives | Type |
|---|---|---|
| Environment | `RoomProfile.lightProfile`, `.humidityProfile`, `.windowDirection`, `.lightLevel`, `.temperatureEstimate`, `.environmentPhotos` | structured |
| Setup | `PlantSetupProfile.potType`, `.potSize`, `.drainage`, `.mediumType`, `.wateringMethod`, `.dedicatedArtificialLight*` | structured |
| Placement | `PlantSetupProfile.distanceFromWindow`, `placementPhotos`, AI `placementSummary` | structured |
| Indoor/outdoor | `LocationContext` (`indoor`/`outdoor`/`garden_yard`/...), `RoomProfile.spaceType` (balcony/porch/yard/greenhouse) | structured |
| Sun exposure | `LightProfile` (`bright_indirect`), `OutdoorLightQuality` (`partial_shade`), `windowDirection` | structured |
| Region | `approximateLocationContext` (ZIP/city free-text), GPS coords at scan, `nearbyCityOrZip` | **free-text, not structured** |
| Weather | `CareTaskDetailMetadata.weatherContext` — **always `null`** | **missing entirely** |
| AI memory | `PlantIntelligenceProfile.environmentSummary/placementSummary/setupSummary` + `buildPlantMemoryContext` | structured |
| Sick-plant care | `buildTreatmentCareTasks` → `source:'diagnosis'` + dated tasks | ✅ works |
| Healthy-plant care | `buildCareTasks` → regex-parses `wateringSchedule`/`careSummary` free text, `source:'plant_care_text'` | ⚠️ **the gap** |

**Key finding:** the data model is rich, but `buildCareTasks()` never reads any of it. All context sits structured and unused. `CareTaskSource` already has `'care_schedule'` and `'weather'` enum values — both dead.

---

## 2.5 Care Action Taxonomy (Bekky-refined, 2026-08-13)

Care actions are NOT one kind of thing. They split into **four classes** with different triggers:

| Class | What | Trigger | Examples |
|---|---|---|---|
| **1. Scheduled** | Recur on a cadence | AI schedule + weather adjustments | Water, Fertilize, Inspect |
| **2. Weather-triggered** | Event-based, fire when weather demands | Weather + plant/space context | Frost → cover young outdoor plants; heat wave → water more; storm → bring in |
| **3. Observation** | Only known when you *see* it | Photo/memory update | Prune, Harvest, Mist, Rotate |
| **4. Always-available action** | A capability, not a task | Any time (button) + notification when AI detects need | **Repot, Propagate** |

### Class 2 — Weather-triggered warnings are per-space AND per-plant (intelligent)
The AI does NOT just say "frost coming." It reads each plant's context and decides **who** needs the warning:
- **Whole-garden level:** "Frost Friday night — cover the side garden."
- **Per-plant level:** "Your young tomato seedlings in the side garden are frost-tender — bring them in. Your rosemary is hardy, it's fine."
- Inputs: weather + plant species + plant age/growth stage + location (outdoor) + space. A young frost-tender plant outdoors gets flagged; a hardy established one doesn't.

### Class 4 — Repot & Propagate are dual-nature (action AND notification)
- **Repot** — ALWAYS an available button/feature (a new nursery plant is dripping wet and needs repotting *now* or it dies — the action must be available immediately, not just a reminder). AND a notification when the AI detects the need (roots escaping, pot-bound, nursery-fresh).
- **Propagate** — ALWAYS a choice, never a demand. It can *suggest* "you can propagate this now if you want" but it's optional — a gentle offer, not a task.

**Principle:** some actions are *tasks* (scheduled/weather/observation), some are *capabilities* (repot/propagate) that are always available AND can surface as suggestions.

---

## 3. Architecture Overview

```
┌────────────────────────────────────────────────────────────────────┐
│                    CARE ENGINE (per plant)                          │
│                                                                    │
│  CONTEXT PACKET  ──►  AI SCHEDULE  ──►  DATED CARE TASKS           │
│  (assembled from  (structured       (source:'care_schedule',       │
│   real data)       cadence)         weather-adjusted)              │
│                                                                    │
│  ┌──────────────┐   ┌────────────┐   ┌─────────────────────────┐   │
│  │ RoomProfile  │   │ watering   │   │ Water due today         │   │
│  │ SetupProfile │   │ fertilize  │   │ Fertilize in 5 days     │   │
│  │ Placement    │──►│ prune      │──►│ Prune in 30 days         │   │
│  │ Region/climate│  │ repot      │   │ Repot in 2 years         │   │
│  │ Weather      │   │ mist       │   │ Mist every 2 days        │   │
│  │ Indoor/outdoor│  │ harvest    │   │ (heat wave → water now)  │   │
│  │ Sun exposure │   │ inspect    │   └─────────────────────────┘   │
│  └──────────────┘   └────────────┘                                │
│                                                                    │
│  PHOTO-UPDATE LOOP (adapts the schedule over time)                 │
│  care-task done / schedule / notification ──► progress photo       │
│  ──► AI reads photo + context ──► updates care metrics/schedule    │
└────────────────────────────────────────────────────────────────────┘
```

---

## 4. New Services

### 4.1 `services/weather/weatherService.ts` — Weather (NEW)
- **Provider:** Open-Meteo (free, no API key, 10k calls/day non-commercial, CC BY 4.0 attribution).
- `getWeather(lat, lon)` → current + 7-day forecast: temperature, precipitation, humidity, wind, weather code, UV, sunrise/sunset.
- `getWeatherForRegion(approximateLocationContext)` → geocode ZIP/city → lat/lon → weather (fallback when no GPS).
- **Privacy:** respect `gpsPrecision` — 'rough' rounds coords (~1 mile), 'precise' uses full, 'skip' uses ZIP/city only.
- **Caching:** cache per (region, day) in expo-file-system; don't re-fetch within 6h. Prevents API hammering.
- **Attribution:** Open-Meteo requires attribution under CC BY 4.0 — add a small credit line in Settings/About.

### 4.2 `services/weather/climateService.ts` — Region → Climate (NEW)
- Derive **hardiness zone** + **climate type** from lat/lon or ZIP.
- Use Open-Meteo climate API (30-year normals) or a hardiness-zone lookup keyed by annual min temp.
- Output: `{ hardinessZone, climateType, seasonalContext }` stored on the plant.
- This is what makes "what region it's living in" actionable (e.g. "zone 9b — don't leave frost-tender plants out in winter").

### 4.3 `services/care/contextPacket.ts` — Context Packet Builder (NEW)
- `buildCareContextPacket(plant, room, setup, region, weather)` → a structured `CareContextPacket`.
- Renders into a compact prompt block (like `renderMemoryContextBlock`).
- Pulls from: RoomProfile, PlantSetupProfile, placement, LocationContext, region/climate, weather, indoor/outdoor, sun exposure.
- **Non-fatal:** if any piece is missing, the packet still builds with what's there (same pattern as memory read).

### 4.4 `services/care/careScheduleService.ts` — AI Schedule Generator (NEW)
- `generateCareSchedule(plant, contextPacket)` → AI returns structured cadence:
  ```ts
  type CareSchedule = {
    scheduled: Array<{ action: 'water'|'fertilize'|'inspect'; everyDays: number }>;
    observation: Array<'prune'|'harvest'|'mist'|'rotate'>;  // surfaced on photo/memory update
    weatherSensitive: boolean;   // does this plant need weather-triggered warnings?
    frostTender?: boolean;        // young/outdoor plants → frost warning
    notes?: string;
    generatedAt: string;
  };
  ```
- **Extends the enrichment prompt** (one AI call) OR a separate call. Recommend extending enrichment so a plant gets its schedule at the same time as its bio/care profile — no extra round-trip.
- The AI sees the context packet and species, and decides cadence *given the environment* (e.g. a Monstera in a bright humid bathroom waters more often than one in a dry office).
- Stores as `SavedPlantProfile.careSchedule`.

### 4.4b `services/care/weatherWarningService.ts` — Weather-Triggered Warnings (NEW)
- `evaluateWeatherWarnings(plants, spaces, weather)` → per-space AND per-plant warnings.
- **Whole-garden level:** "Frost Friday night — cover the side garden."
- **Per-plant level:** "Your young tomato seedlings in the side garden are frost-tender — bring them in. Your rosemary is hardy, it's fine."
- Inputs: weather + plant species + growth stage + location (outdoor) + space. A young frost-tender plant outdoors gets flagged; a hardy established one doesn't.
- Emits `source:'weather'` care tasks + in-app notifications (bell). Android push is a later arm (Bekky noted — current notifications are in-app only).

### 4.5 `services/care/careTaskBuilder.ts` — Dated Task Builder (REWRITE of `buildCareTasks`)
- Generate dated tasks from `careSchedule` + weather adjustments.
- `source:'care_schedule'` (finally uses the dead enum).
- **Weather-aware:** heat wave → pull watering forward; heavy rain → skip outdoor watering; frost warning → add a "bring indoors" task.
- Merge with existing `buildTreatmentCareTasks` (sick plants) — both feed `appCareTasks`.
- Keep the existing `CareTaskItem` shape so the Care tab UI is untouched.

### 4.6 `services/care/careMetricsUpdater.ts` — Photo-Update Loop (NEW)
- `updateCareMetricsFromPhoto(plant, photoUri, contextPacket)` → AI reads the progress photo + context, returns updated metrics/schedule deltas.
- Reuses the `photoIntelligenceExtractor` pattern (progress photo analysis).
- **Triggers (Bekky's ask):**
  1. **On care-task completion** — after completing a task, offer a progress photo ("📸 Snap a quick photo so Pixie can see how it's doing?"). Optional, non-blocking.
  2. **On a schedule** — a recurring "check-in" cadence (e.g. weekly) → notification → prompt for a photo.
  3. **Via notification** — the bell surfaces the check-in prompt.
- Output updates `careSchedule` (e.g. "leaves yellowing → reduce watering cadence") and records a memory episode.

---

## 5. Data Model Additions

### `types/plantScan.ts` — `SavedPlantProfile`
```ts
careSchedule?: CareSchedule;          // structured cadence (from AI)
regionContext?: RegionContext;        // hardiness zone, climate
```

### `types/garden.ts`
- `CareTaskSource` — already has `'care_schedule'` and `'weather'`; now actually used.
- `CareTaskDetailMetadata.weatherContext` — now populated (was always null).
- New `CareSchedule` + `RegionContext` types (or in a new `types/care.ts`).

---

## 6. Photo-Update Loop Detail (Bekky's ask)

The loop closes the gap the OU roadmap flagged: *care currently advances on documentation frequency, not plant recovery.* The photo-update loop makes care **adaptive**:

```
① Care task completed  OR  scheduled check-in  OR  notification
② Prompt: "📸 Snap a quick photo so Pixie can see how it's doing?"
   (optional, non-blocking — user can skip)
③ AI reads photo + context packet + prior memory
④ AI returns: health assessment + care-schedule deltas
   (e.g. "leaves yellowing → water less often" / "new growth → fertilize")
⑤ Update careSchedule + record a memory episode
⑥ Next care tasks reflect the new schedule
```

**Key rules:**
- **Never blocking** — the photo prompt is always skippable; care tasks complete regardless.
- **Reuses the existing progress-photo analysis** — no new native modules, JS-only → OTA.
- **Cost-conscious** — cap how often the AI re-evaluates (e.g. once per care-task completion, not every photo). Bekky's cost-consciousness rule applies.

---

## 7. Rollback / Reversibility

- **Backup first:** snapshot every edited file (`*.bak-<date>`) before changing — per project rule.
- **Additive data model:** `careSchedule`/`regionContext` are optional fields; old plants without them fall back to the current regex path until re-enriched. Revert = stop populating the new fields; the old `buildCareTasks` path still works.
- **Feature-flag the AI schedule:** ship the context packet + weather first (safe, additive), then flip the AI-schedule generation on. Revert = unset the flag.
- **Weather is non-fatal:** if Open-Meteo is unreachable, care still generates from the context packet without weather. Never block care on weather.
- **Verify:** `npx tsc --noEmit --pretty false | grep -c "error TS"` == 0 before any build.

---

## 8. Build Order (proposed)

1. **Weather service** (`weatherService.ts` + `climateService.ts`) — safe, additive, no UI change.
2. **Context packet builder** (`contextPacket.ts`) — assembles what already exists.
3. **Extend enrichment prompt** to return `careSchedule` + `regionContext`.
4. **Rewrite `buildCareTasks`** → `careTaskBuilder.ts` (source:'care_schedule', weather-aware).
5. **Photo-update loop** (`careMetricsUpdater.ts`) + completion/schedule/notification triggers.
6. **Verify + ship as ONE OTA** when Bekky says "compile".

---

## 9. Open Questions for Bekky

1. **Which care actions** should healthy plants get? (Water, Fertilize, Prune, Repot, Mist, Harvest, Inspect — or a subset?)
2. **Backfill** existing plants — re-run enrichment to fetch schedules, or start with defaults from `careDifficulty`?
3. **Photo-update cadence** — how often should the scheduled check-in fire? (weekly? every N care completions?)
4. **Weather attribution** — OK to add a small "Weather by Open-Meteo" credit in Settings/About? (CC BY 4.0 requires it.)
