-- Additive only. Brewing side of the app: roasted/purchased bean management, brew logs, and -- user-generated API tokens. Bean remaining weight is computed (initial - sum of brew doses), -- never stored, so it can't drift. CREATE TABLE roasted_beans ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, name text NOT NULL, roaster text NOT NULL DEFAULT '', origin text NOT NULL DEFAULT '', process text NOT NULL DEFAULT '', variety text NOT NULL DEFAULT '', roast_level text NOT NULL DEFAULT '', roast_date date, initial_weight_g numeric, url text NOT NULL DEFAULT '', tasting_notes text NOT NULL DEFAULT '', notes text NOT NULL DEFAULT '', roast_plan_id uuid REFERENCES roast_plans(id) ON DELETE SET NULL, archived boolean NOT NULL DEFAULT false, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now() ); CREATE TABLE brews ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, bean_id uuid REFERENCES roasted_beans(id) ON DELETE SET NULL, method text NOT NULL, dose_g numeric, water_g numeric, yield_g numeric, grinder text NOT NULL DEFAULT '', grind_setting text NOT NULL DEFAULT '', water_temp_c numeric, brew_time_s integer, bloom_time_s integer, rating numeric, tasting_notes text NOT NULL DEFAULT '', notes text NOT NULL DEFAULT '', brewed_at timestamptz NOT NULL DEFAULT now(), created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now() ); CREATE TABLE api_tokens ( token_hash text PRIMARY KEY, user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, name text NOT NULL DEFAULT '', created_at timestamptz NOT NULL DEFAULT now(), last_used_at timestamptz ); CREATE INDEX roasted_beans_user ON roasted_beans(user_id, archived, created_at DESC); CREATE INDEX brews_user ON brews(user_id, brewed_at DESC); CREATE INDEX brews_bean ON brews(bean_id); CREATE INDEX api_tokens_user ON api_tokens(user_id);