# Garden App — Architecture & Status

## Current State (2026-05-07)

Garden management app at `garden.riverway.ca`. FastAPI + SQLite + Jinja2 templates.

### Philosophy

Two layers:
- **State** (ground truth the user authors): areas (with sketches + plant positions),
  species/varieties (with spacing metadata), plantings, supplies, watering stations,
  and field notes (which subsume what used to be "comments").
- **Artifacts** (AI-generated, regenerable): planting plans, fertilization schedules,
  shopping lists, watering plans. Stored as markdown files in `data/artifacts/`. Created
  by external sessions (terminal Claude) or via `POST /api/artifacts`.

No scheduled-events schema — to-dos and schedules live inside artifacts. Marking things
done happens via field notes on the relevant entity, and next regeneration reads them.

### Data Model

```
Species ──► Variety            (catalog; both have plants_per_unit + space_per_unit_sqft
                                spacing metadata for the area-sketch icon sizing)

Year                           (season with notes)
  └──► Planting                (idea/planned/planted/skipped)
         └──► Plant            (qty + species + variety + area + per-plant `positions` JSON)

Area (nested tree)             (each area has length_ft × width_ft, sketch_rotation
  │                              0/45/90/…/315°, parent_pos_x/y to position itself
  │                              within its parent's sketch, featured_image_path)
  └──► (children render as     (drag-and-drop UI; positions persisted)
        nested rectangles in
        parent's sketch)

Station ──► area_stations      (many-to-many; explicit assignment per area, no longer
                                cascades — checking a parent in the picker auto-checks
                                every descendant on the form side)
Supply                         (inventory + shopping list, has featured_image_path)

field_notes                    (UNIFIED table: comments + raw captures live here)
  ├── (comment-style):           body, kind, comment_date, parent_id (threading)
  ├── (capture-style):           captured_at, audio_path, photo_paths,
  │                              text, transcript, status, processed_at, processing_notes
  └── (any can have media)       audio_path + photo_paths attach to either kind

field_note_targets             (many-to-many: area/plant/species/variety/planting/year/station)
                                renamed from comment_targets in the 2026-05-04 unification

Artifact                       (markdown file + DB row: kind, title, generated_at)

activity_log                   (every user mutation: add/edit/delete/archive/unarchive
                                with old→new diff for edits; sketch repositioning is
                                intentionally not logged)
chat_sessions ──► chat_messages   (Gemini AI assistant conversations; tool_calls JSON
                                   on each assistant message records what tools fired)
tool_suggestions               (meta-tool sink — model proposes new tools it'd want)

(Every entity table has `is_archived BOOLEAN DEFAULT 0` since 2026-05-07.
 Default queries filter to non-archived; `?archived=1` per page reveals them.)
(Species has `type` (plant|animal) and `primary_function` (edible/ornamental/
 support/weed for plants; beneficial/pest for animals) — varieties inherit via JOIN.)
```

### Key relationships
- **Plant** → Planting (required), Species/Variety/Area (optional). `positions` JSON stores
  per-icon (x,y) within the area's natural-orientation rect; `quantity / plants_per_unit`
  = number of icons rendered.
- **Planting** → Year (optional); status = idea/planned/planted/skipped.
- **Area** has `short_name` + `featured_image_path` + `sketch_rotation` (8-way 45° steps)
  + `parent_pos_x/y` (center within parent's natural-orientation coords).
- **Species** has `plants_per_unit` (default 1) + `space_per_unit_sqft` (default 1.0).
  Seeded for 60+ species via `SPECIES_SPACING_DEFAULTS` in main.py.
- **Variety** has nullable `plants_per_unit` + `space_per_unit_sqft` — NULL means inherit
  from species via `COALESCE(variety, species, default)` in `load_plants_by_area`.
- **Station** coverage is now fully explicit (no ancestor-walk in `load_stations_for_area`).
- **field_note** has primary target via `field_note_targets.is_primary=1` plus any number
  of secondaries. Comments posted from per-entity capture widgets auto-infer secondaries
  via `add_inferred_targets()`.

### Database stats (2026-05-07)
- **128 areas** (nested tree, all dimensioned areas have sketch_rotation=315° default
  for SW–NE property axis)
- **64 species** (63 plant + 1 animal — Mason Bee), **158 varieties** (all species
  have spacing seeded)
- **8 years** (with `featured_image_path`)
- **52 plantings** (2 archived), **216 plant groups** (64 archived) — Spring 2026
  fenced-garden plan committed: 65 groups in planting #26 + comfrey perennial #54
- **15 watering stations, 107 area-station links**
- **163 field_notes**, **1204 field_note_targets** (legacy `comments` + `comment_targets`
  tables dropped 2026-05-07; pre-drop backup at `garden.db.backup-20260507-092119-pre-droplegacy`)
- **8 supplies, 88 overview_versions** (history snapshots)
- **115 activity_log rows** (since the activity-log feature shipped)
- **4 chat_sessions, 12 chat_messages, 1 tool_suggestion** (since the AI chat shipped)
- **4 artifacts**: `2026 Fertilization Schedule` (#3), `2026 Fertilization Plan` (#4),
  `Spring 2026 Fenced Garden Planting Plan` (#5), `2026 Watering Schedule` (#6)

## What's Built

### Core entities (CRUD + detail pages + featured images)
- **Areas** — nested tree, dimensions, sketch rotation, featured image, area sketch SVG
- **Plantings** — idea/planned/planted/skipped, year link, source field, featured image
- **Plants** (groups) — qty + species + variety + area + planting, status, source, notes,
  featured image, per-plant icon positions
- **Species** — name, common name, description, **spacing (plants per N sq.ft)**, featured image
- **Varieties** — under species, **spacing override** (NULL inherits), featured image
- **Watering stations** — explicit-cascade area picker, feeds info, featured image
- **Supplies** — inventory + shopping list, featured image
- **Years** — with featured image
- **Field notes** (unified) — see below

### Area sketch (the "video game" UI)
The signature layer. Available on every area-detail page via a **150×~110 px clickable
thumbnail** that opens a **fullscreen modal dialog** with:
- Rectangle sized to the area's `length_ft × width_ft` (in feet, viewBox-mapped)
- Rotated by `sketch_rotation` (0/45/90/…/315°) with viewBox sized to the rotated bounding box
- 1-foot dotted grid (5-foot major lines)
- Compass mini in bottom-left corner (fixed pixel size, N/E/S/W with red north arrow)
- Sun-intensity gradient bar placeholder (W on left = pm, E on right = am)
- **Plant icons** — emoji or custom SVG glyph per species, scaled to `space_per_unit_sqft`
  footprint, multi-plant species (carrots, radish, clover) get a count-badge in the corner.
  Drag any icon to reposition; saves to `plants.positions` JSON. Hover tooltip shows
  `Nx Species/Variety (qty in this group)`.
- **Child area rectangles** (direct children only) rendered as nested dashed rectangles,
  each rotated to its own absolute compass orientation. Selection-based interaction:
  click rect → highlights + dropdown updates; drag the selected rect to reposition;
  "Go there →" button drills into the child. Uniform child label sizes derived from
  parent's max dimension so labels look the same pixel-size across views.

### Layout mode — mobile-optimized plant positioning (2026-05-07)
Separate page at **`/areas/{id}/layout`** (linked from the area page via an
`Edit layout →` button next to the sketch thumbnail). Designed for thumb-on-phone
editing where the regular drag-in-dialog model fails (accidental scrolls, finicky
small targets, plant-name not visible while a finger covers the icon).
- **Full viewport, no scroll.** Body locked to `overflow: hidden` while mounted;
  `overscroll-behavior: none` kills pull-to-refresh; canvas SVG has `touch-action: none`.
  Layout: top bar (~48px) + canvas (flex 1) + sticky inventory tray (~110-160px).
- **Auto-rotate to fit viewport.** On load and on resize, JS compares the area's
  rotated viewBox aspect to the available canvas aspect and applies a CSS
  `transform: rotate(0|90deg)` to a wrapper around the SVG. The compass mini
  counter-rotates so N stays correct. A `↻ 90°` button cycles through 0/90/180/270
  manually if auto picks wrong.
- **Tap-pick-tap-place** (no drag, no thresholds — touch-friendly):
  - Tap a placed icon → goes 35% opacity (ghost) + yellow ring; top bar shows
    `Selected: 🍅 Tomato / Sungold (tap canvas to move, tap tray to send to inventory…)`.
  - Tap an inventory tile → picks one from pile (count drops by 1); top bar shows
    `Holding: 🍅 Tomato / Sungold (tap canvas to place…)`.
  - Tap empty canvas with a selection → places/moves there.
  - Tap the same icon/tile again → cancels the pick.
  - Tap matching inventory tile while holding a placed plant → returns it to pile.
- **Inventory tray (bottom):** one tile per plant_group with unplaced plants; emoji or
  SVG glyph + count badge + species/variety label. Horizontal scroll on overflow.
  Tools: `⚙ Auto-arrange`, `⤴ All to inventory`.
- **Strict positions semantics.** In layout mode, `plants.positions` JSON is the source
  of truth — anything not in the array sits in the inventory tray. Outside layout mode
  (the regular area-detail dialog), the existing auto-fill behavior still kicks in so
  plants don't disappear from non-editing views.
- **Companion-aware auto-arrange via Gemini.** `POST /api/areas/{id}/auto-arrange` builds
  a JSON request to Gemini 2.5 Flash with area dims + sun + structure_features +
  currently-placed plants (so they're not overlapped) + inventory list. Gemini returns
  `{assignments: [{plant_id, positions: [{x,y}, …]}]}`. The button applies each suggested
  position via the existing place-from-inventory flow (autosaves per placement). Only
  inventory plants are arranged; placed plants stay put.
- **Saves on every action** via `POST /api/plants/{id}/positions` (the existing endpoint —
  layout mode just sends shorter arrays when items go to inventory).
- **Selection display** in the top bar (above the canvas) so the species/variety name is
  always visible regardless of finger position.
- New file: `app/templates/layout_mode.html` (template + embedded JS, ~370 lines).
  CSS appended to `style.css`. New endpoint `POST /api/areas/{id}/auto-arrange`.

### Field notes / capture / comments (unified 2026-05-04)
- **`/capture` page** — audio recording + multi-photo upload + text. Saves to `field_notes`
  with status='new'.
- **`POST /api/notes/{id}/transcribe`** — sends audio to Gemini 2.5 Flash and stores the
  transcript. Stdlib only (urllib + base64 + json) — no `httpx`/`requests` dependency.
- **Capture widget on every entity-detail page** — a reusable `_capture_widget.html` macro
  drives both `/capture` (status='new') and the per-entity comment form (status='processed',
  with primary target = the entity). Audio + photo + drag-drop + clipboard paste +
  browser STT supported. Single `capture.js` auto-discovers all `[data-capture-root]`
  elements and inits each independently.
- **Comments thread on every entity** renders in `_comments.html` macro — per-comment
  inline audio player + photo thumbnails + kind chip + edit/delete + multi-target chips.
- **Comment edit page** supports adding/removing audio + photos in addition to body/date/kind.
- **Global timeline** at `/timeline` shows comment-style rows (excludes raw captures)
  with the same media rendering.

### Per-area sketch additions (recent work)
- Featured image renders at top of area detail (with click-to-replace; placeholder tile
  when none).
- Click thumbnail to expand to fullscreen modal with the rich sketch UI.
- Fullscreen close button (✕). ESC key also closes.
- Drag plant icons to position; per-plant `positions` JSON saved via
  `POST /api/plants/{id}/positions`.
- Drag child-area rectangles to position within parent; saved via
  `POST /api/areas/{id}/parent-position`.
- Inline spacing edit on species/variety detail pages (no need to click "Edit"):
  `Sketch spacing: [N] per [S] sq.ft [Save]` in 0.5-step increments.

### Card thumbnails on list views
- Plantings, stations, years, species, varieties — each list shows a small 56×56 px thumb
  next to the card when the entity has a `featured_image_path`. Layout shifts to flex-row
  when a thumb exists; cards without thumbs stay in their original layout.
- Sub-area tiles in area detail get a 100% × 80px banner thumb when the sub-area has an image.

### Timeline view
- `/timeline` (global) and `/timeline/{entity_type}/{entity_id}` (per-entity)
- Shows comments in chronological order with primary target chip, secondary chips, body,
  audio player, photo thumbs.

### Fertilization plan (artifact #4)
- Generated 2026-04-21, covers 14 categories (strawberries, raspberries, brambles,
  blueberries, grape pergola, currants/gooseberries, haskap/jostaberry/lingonberry/etc.,
  fruit trees, kiwi, garlic, vegetable beds, monthly cheat sheet, decisions to watch).

### Spring 2026 planting plan (artifact #5)
- Generated 2026-05-05, covers all 12 dimensioned fenced-garden areas with placement
  rationale, buy list (~50 transplants, ~$120–160 + companions ~$25 + seeds ~$15–25),
  fertilizer-at-planting protocol, succession schedule.

### Watering schedule (artifact #6)
- Generated 2026-05-05, designed under three constraints: one zone at a time
  (water-pressure limit), 10pm–5am lawn lockout, mixed timer types (day-of-week vs interval).
- Master 5am–10pm grid, per-timer setup instructions, seasonal adjustments.

### Auth, API, infra
- Session-based auth with HMAC-signed cookies
- Public API: export, inline species/variety creation, artifact create/list/serve,
  notes capture/list/transcribe/update/delete, plant positions, area parent-position,
  spacing endpoints (species + variety inline edit)
- All photos/audio served via `/notes/{rel_path}` (auth-required, safe-path-resolved)
- Featured images use the same NOTES_DIR storage and `/notes/{path}` serving

## What's Not Built Yet

### Deferred (active backlog)
- Checkbox interaction on artifacts (tick → post comment, flip state)
- Watering scheduler UI (the artifact provides the plan; in-app management is future)
- AI auto-classification/auto-routing of comments
- In-app AI chat
- **AI chat context architecture overhaul (DEFERRED 2026-05-15).** The chat
  works but uses a "shotgun snapshot" approach: pre-load ~8K tokens of common
  stuff (areas, species, currently-planted, recent care actions / issues / notes
  from last 30-60 days) and hope the AI uses it. Recent fixes patched the
  fertilization-question case by enriching the snapshot with `done`-tagged
  notes from 60 days back, but it's whack-a-mole. The deeper problem:
  - Different question types need wildly different context (timing → past
    planting dates + outcomes; companions → current placements + past
    companion notes; variety selection → harvest/disease history per
    variety; design → empty space + currently planted; etc.).
  - Older history (last year's mistakes, multi-year variety performance)
    can't fit in a static snapshot.
  - Tools exist but the AI rarely calls them — it uses what's pre-loaded.

  **Three options on the table** (decide later):
  1. **Better tools + smarter prompt (incremental).** Trim snapshot to
     evergreen structure (~3-4K tokens). Enrich `search_notes` with
     `target_type/id`, `kind`, `after_date`, `before_date` filters. Add
     `get_history(target_type, target_id)` and `list_recent_activity`.
     Restructure the system prompt as a decision tree: "for care
     questions → search_notes('keyword'); for variety questions →
     get_history(species); for design questions → find_empty_space +
     currently planted; etc." Cost ~1-2 hours. Recommended starting point.
  2. **Semantic search on top of #1.** Embed every note via Gemini's
     `text-embedding-004` + sqlite-vss; add `find_relevant_notes(question)`
     tool. Solves the "I forgot the keyword" failure mode (e.g., "leaf
     yellowing" → finds "chlorosis on the blueberries"). Cost ~3-4 hours
     plus an embedding-on-write hook in field_note CRUD.
  3. **LLM-driven snapshot per turn.** First Gemini call: "what does
     this question need?" → fetches → second Gemini call answers. Doubles
     latency. Cost ~2-3 hours. Try only if #1 and #2 fall short.

  **Pinned-lessons feature (small adjacent idea):** `is_pinned` flag on
  field_notes; pinned notes always go in the snapshot regardless of age.
  Gives the user a deliberate way to surface always-remember wisdom
  ("never plant peas after frost dates", "Romeo cherry: bad fruit set,
  swap variety"). ~30 min on top of any of the above options.

  **Model choice:** currently Gemini 2.5 Flash. Pro is more reliable at
  multi-step tool use but ~3× cost. If after #1 the AI still makes poor
  tool-calling decisions, try switching `GEMINI_CHAT_MODEL=gemini-2.5-pro`
  before committing to #2 or #3.

  **Where things stand right now:** snapshot is the enriched version
  (~8K tokens including done/issue/other notes split by kind), system
  prompt has an explicit 4-step ongoing-care loop, `find_plants` accepts
  no-args + `status` filter. Good enough for "what should I fertilize?"
  type questions; brittle for anything outside the 60-day window or
  needing cross-year context.
- **Processing watermark** via `processing_runs` table — captures `last_processed_at`
  per run so future processing can read "all field_notes since X" instead of relying on
  status='new'. Enables continuous re-processing as comments accumulate.
- **Narrow `add_inferred_targets` scope for area-targeted comments.** Today it adds
  every plant in the area subtree (capped at 10) as secondaries. Result: a sawfly
  comment posted from "Raspberry Patch" got linked to apple, alyssum, and creeper
  plants the comment never references. Better: only infer year + station + parent-area
  chain by default. Add species/variety/plant-group secondaries only when explicitly
  mentioned in body text. See PROCESSING.md Rule 11.
- **Rename `/comments/...` URL paths to `/notes/...`** (with redirects) — purely
  cosmetic; the unification is already functionally complete.
- **Rename UI labels** "Comments" / "Commentary" → "Notes" / "Field notes" universally.
  Mostly done, but a few stragglers may remain.
- **Rename Python function names** (`load_comments` → `load_field_notes_for_target`,
  etc.) — pure naming churn, low priority.

### Done since last revision (2026-04-17 → 2026-05-07)
The list of things that *were* deferred and have now landed:
- ~~Mobile-friendly plant-positioning UX~~ — new `/areas/{id}/layout` route,
  full-viewport tap-pick-tap-place model with inventory tray, auto-rotate to
  fit screen, semi-opaque ghost on selected plants, top-bar selection display
  (so the plant name isn't hidden by a finger), companion-aware auto-arrange
  via Gemini that touches only inventory items. See "Layout mode" subsection
  above for the full description. Lifts the three pain points the user flagged
  on the regular sketch dialog: small targets, scroll/refresh interference,
  no visible name when an icon is touched.
- ~~AI chat assistant (Gemini, function-calling)~~ — the floating modal now
  has a `Note | Chat` tab toggle. Chat mode opens a message stream + input
  + photo attach. New schema: `chat_sessions`, `chat_messages` (with
  `tool_calls` JSON column), `tool_suggestions` (meta-tool sink). New
  endpoints: `POST /api/chat/sessions` (create + activity_log row),
  `POST /api/chat/sessions/{id}/messages` (multipart text+photos, runs
  Gemini with tools), `POST /api/chat/sessions/{id}/end` (Gemini-generated
  one-line title; auto-discards empty sessions), `GET /api/chat/sessions/{id}`
  (replay payload). One activity_log row per session — created on session
  start, summary updated on end with title + message/tool counts. Sketch
  repositioning + transcription flows are unaffected.
  **Architecture: hybrid grounding.** Each turn carries a light starter
  context (`build_starter_context`) — area names + dims, species list,
  plantings, last 14 days of notes (~1-3K tokens). Plus 10 tools the model
  can call when it needs detail: `get_planting_detail`, `get_area_detail`,
  `get_species_detail`, `find_plants`, `find_empty_space`,
  `get_recent_notes`, `search_notes`, `list_artifacts`, `read_artifact`,
  and meta-tool `suggest_new_tool` (writes to `tool_suggestions` for
  later developer review). `gemini_chat_complete` runs the function-call
  loop with a 6-iteration cap. Tool calls persist on the assistant message
  so the replay view shows what the model looked at. Gemini chat model is
  configurable via `GEMINI_CHAT_MODEL` (default `gemini-2.5-flash`).
  **Image upload:** photos attached to chat messages are stored in
  `data/notes/YYYY-MM/` (same path as note photos), passed to Gemini as
  inline_data parts. Verified: model identified sawfly larvae in a test
  photo and connected the observation to a real recent note in the
  starter context. **Note:** Gemini free tier is rate-limited to 5 RPM;
  the `GEMINI_API_KEY` should have Tier 1 / Postpay billing enabled for
  any real use. See PLANNING.md `Deferred / active backlog` for next
  iterations (resumable sessions, tool-suggestion review page).
- ~~Activity log of every user mutation~~ — new `activity_log` table
  (`id, created_at, category, entity_type, entity_id, summary, field,
  old_value, new_value`). Categories: `add | edit | delete | archive |
  unarchive`. Sketch repositioning (`/api/plants/{id}/positions`,
  `/api/areas/{id}/parent-position`) is intentionally NOT logged.
  Helpers (`log_add / log_edit / log_delete / log_archive / log_unarchive`)
  are wired into: `/api/edit/{type}/{id}`, `/api/station/{id}/areas`,
  `archive_entity / unarchive_entity / _maybe_archive_planting` (cascades
  log per affected row), `post_comment`, comment edit + delete, every
  entity create/delete route (areas / plantings / plants / years / species
  / varieties / stations / supplies), planting/plant status, plant move
  (split logs both old + new groups), and `_set_featured_image` (counts
  as edit). Inline-create endpoints (`/api/species`, `/api/varieties`)
  log too. `/timeline` and `/timeline/{type}/{id}` accept `?view=log` to
  flip from comments to a chronological detailed-log feed; per-entity
  log view filters `entity_type+entity_id`. Each log row renders a
  category chip, ISO timestamp, entity link, summary, and (for edits)
  an `old → new` diff line.
- ~~Click-to-edit detail pages, edit screens removed~~ — every editable
  field on `/areas/{id}`, `/plantings/{id}`, `/years/{id}`, `/species/{id}`,
  `/varieties/{id}`, `/stations/{id}` is now wrapped in
  `<span class="editable" data-entity data-id data-field data-type ...>`. Click
  swaps to an `<input>` / `<textarea>` / `<select>`; blur (or Enter) saves;
  Escape cancels. Server-side: a single `POST /api/edit/{entity}/{id}`
  endpoint validates against `EDITABLE_FIELDS` (per-entity field whitelist
  with type/options/min-max/snapshot rules), coerces the value, and applies
  `snapshot_overview` for `notes` / `description` fields. Species type/
  primary_function combo is enforced on the backend (changing `type` clamps
  `primary_function` to a valid option). Sketch rotation lives inside the
  fullscreen-sketch dialog as `↺ / ↻` buttons that step through the eight
  45° steps. Stations have an "Edit areas served" toggle that flips the
  static area list to a checkbox tree; saving hits a separate
  `POST /api/station/{id}/areas` (replaces the set in one shot). Each detail
  page has a Delete button at the bottom.
  Removed: `/areas/{id}/edit`, `/plantings/{id}/edit`, `/years/{id}/edit`,
  `/species/{id}/edit`, `/varieties/{id}/edit`, `/stations/{id}/edit` GET
  routes (they 404 now). The `*_form.html` templates are still reused by
  `/<type>/new` creation flows; only the edit branches were dead-coded.
  New files: `app/static/inline-edit.js`. New endpoints:
  `/api/edit/{entity}/{id}`, `/api/station/{id}/areas`.
- ~~Floating capture (chat-bubble) for field notes + Quick-and-Dirty mode~~ —
  the inline "Add a note" widget at the top of every Field-notes section is
  removed. A fixed-position chat bubble (bottom-left, on every authenticated
  page) opens a modal with the existing capture widget plus a primary-target
  pill. The pill defaults to the current page's entity (resolved via
  `/api/page-context` against `location.pathname`); on a non-detail page it
  defaults to the current year. **Pill body click** opens a native-select
  picker with optgroups for areas/plantings/plants/species/varieties/
  stations/years (loaded from `/api/picker-targets`). **Pill × click** reverts
  to the current year (resolved via `/api/current-year`, creating the year
  row if needed). All field notes thus always have a primary target — there
  is no orphaned-note state. `capture.js` reads `data-target-type` /
  `data-target-id` fresh at submit time so the floating widget can swap
  targets in-place. New files: `_floating_capture.html`,
  `floating-capture.js`. New endpoints: `/api/current-year`,
  `/api/page-context`, `/api/picker-targets`.
- ~~Animals as a separate entity~~ — instead, added two columns to `species`:
  `type` (`plant` | `animal`, default `plant`) and `primary_function`
  (default `edible`). Function values are constrained by type at the app layer
  via `SPECIES_FUNCTION_OPTIONS` (plant → edible/ornamental/support/weed,
  animal → beneficial/pest); `normalize_species_kind` coerces invalid combos.
  Varieties inherit type/function from their species via JOIN — no extra
  columns. Species form has a Type select that filters the Function options
  via JS. Species list/detail show colored chips; `/species?type=animal`
  filters to just animals. Backfill (2026-05-07): Mason Bee → animal/beneficial;
  Alyssum, Borage, Calendula, Marigold, Nasturtium, White Clover, Buckwheat,
  Comfrey → plant/support; Creeper → plant/weed.
- ~~Archive (soft-delete) flag on every entity~~ — `is_archived` column added to
  areas, plantings, plants, species, varieties, watering_stations, supplies,
  years, field_notes (2026-05-07). Hidden from default list/tree views; per-page
  "Show archived" toggle (`?archived=1`) reveals them with muted styling.
  **Down cascade** (structural children only): archive planting → its plants;
  archive area → all descendant areas; archive species → its varieties.
  Variety/plant/station/supply/year/comment archive does not cascade.
  **Up cascade**: when every plant in a planting is archived, the planting
  auto-archives. Archive routes are `POST /archive/{type}/{id}` and
  `POST /unarchive/{type}/{id}`. Visiting the detail page of an archived item
  without `?archived=1` redirects so the page loads with the chip + Unarchive
  button. Inference (`inferred_targets_for`) skips archived entities so
  posting a note never auto-links archived secondaries.
- ~~Voice capture~~ — `/capture` page + audio + photo + Gemini transcription.
- ~~Photo uploads~~ — featured images on 8 entity types; photos on field notes/comments.
- ~~Sun gradient bar (placeholder)~~ — rendered on area sketches; user fills the stops later.
- ~~Field-note processing flow + reusable rules~~ — see PROCESSING.md (16 rules accumulated).
- ~~Comments + field_notes unified~~ — single `field_notes` table; legacy `comments`
  and `comment_targets` dropped 2026-05-07 (pre-drop backup at
  `data/garden.db.backup-20260507-092119-pre-droplegacy`).
- ~~Per-species + per-variety spacing~~ — seeded 60+ species; varieties inherit via COALESCE.
- ~~Area sketch with rotation, plant icons, child-area positioning~~.
- ~~Brocolli species typo~~ — turned out to be a duplicate; merged into canonical Broccoli.
- ~~Northwest/Southeast Back Patch name/notes inconsistency~~ — fixed; #33 also renamed
  to just "Back Patch" since two patches in different parents were confusing.
- ~~"Sourtheast Back Patch" typo in area #57~~ — gone (notes rewritten during back-patch fix).

### Known issues
- Area picker popover on area edit form opens automatically and blocks the form
  on mobile (pre-existing issue with area-picker.js)
- "Secton 1" typo in area id 3 (Northwest Side of Grape Pergola) — still present.
- Cantaloupe / `Beet → 🍅` are awkward emoji choices; Beet now has a custom SVG icon,
  Cantaloupe still uses the generic 🍈.
- Apple/Pear/Plum/Peach trees set to 100 sqft per icon — may render larger than narrow
  strip areas (e.g., Orchard Row's 4 ft width). Centers are clamped to area bounds, so
  no save corruption; just visual overflow.

## File Structure

```
/srv/apps/garden/
├── app/
│   ├── main.py                       # All routes, schema, helpers (~5300 lines)
│   ├── static/
│   │   ├── style.css                 # All styles
│   │   ├── capture.js                # Reusable capture widget (auto-init on
│   │   │                               every [data-capture-root] on the page;
│   │   │                               site-wide via base.html)
│   │   ├── floating-capture.js       # Bottom-left chat-bubble capture/chat modal
│   │   ├── inline-edit.js            # Click-to-edit fields on detail pages
│   │   ├── notes-actions.js          # /capture queue actions
│   │   ├── area-drilldown.js         # Hierarchical area picker
│   │   ├── area-picker.js            # Searchable area autocomplete
│   │   ├── plant-cards-inline.js     # Inline plant editing in tables
│   │   ├── planting-form.js          # Card-based planting form
│   │   ├── tree.js                   # Area tree drag-and-drop
│   │   ├── tree-search.js            # Area tree search
│   │   └── garden-map.svg            # (decorative)
│   └── templates/                    # 35+ Jinja2 templates including:
│       ├── _archive.html             # archive_button + archived_chip macros
│       ├── _capture_widget.html      # Shared capture UI macro
│       ├── _comments.html            # render_comments macro (renders unified field_notes)
│       ├── _featured_image.html      # featured_image_block + featured_image_thumb macros
│       ├── _floating_capture.html    # Site-wide chat bubble (capture + chat)
│       ├── _picker.html              # picker + area_drilldown_picker macros
│       ├── _species.html             # species pill macro
│       ├── _species_icons.html       # SVG symbol library (~25 custom species icons)
│       └── layout_mode.html          # Mobile-friendly plant-positioning editor
│                                       (tap-pick-tap-place + Gemini auto-arrange)
├── data/
│   ├── garden.db                     # SQLite database
│   ├── garden.db.backup-*            # snapshots (pre-merge, etc.)
│   ├── notes/YYYY-MM/<uuid>.<ext>    # all uploaded media (audio, photos, featured images)
│   └── artifacts/                    # AI-generated markdown
├── docker-compose.yml
├── Dockerfile
├── PLANNING.md                       # this file
├── PROCESSING.md                     # field-note processing rules (16 rules)
├── CLAUDE.md                         # project info + external services
└── .env                              # GARDEN_PASSWORD, API_TOKEN, SECRET_KEY, GEMINI_API_KEY
```
