# Substance-Aware Default Serving Units

## Context

GitHub issue #65: When a user logs a bare count like `1 whiteclaw`, the annotation currently shows `whiteclaw 1 (355ml 5%)`. The feature request is to infer the default serving type from the substance's category, producing `whiteclaw 1can (355ml 5%)` instead. Additionally, `whiteclaw` is not yet in `alcohol.json`.

## Changes

### 1. Add whiteclaw and truly to alcohol.json

**File:** `josie-health-lut/data/alcohol.json`

Add under `brands`:
```json
"whiteclaw": {
  "abv": 5.0,
  "category": "seltzer",
  "style": "hard seltzer",
  "default_ml": 355
},
"truly": {
  "abv": 5.0,
  "category": "seltzer",
  "style": "hard seltzer",
  "default_ml": 355
}
```

Add `"seltzer"` to `CATEGORY_DEFAULTS` in the Crystal code as well.

### 2. Add category default serving constants to AlcoholLUT

**File:** `josie-health-lut/src/alcohol_lut.cr`

Add constant:
```crystal
CATEGORY_DEFAULT_SERVING = {
  "beer"     => "can",
  "seltzer"  => "can",
  "cider"    => "can",
  "wine"     => "glass",
  "spirits"  => "shot",
  "liqueur"  => "shot",
  "cocktail" => nil,
}
```

Add method:
```crystal
def self.default_serving_for(name : String) : String?
  brand = lookup_brand(name)
  return nil unless brand
  CATEGORY_DEFAULT_SERVING[brand[:category]]?
end
```

Expose via `josie-health-lut.cr`:
```crystal
def self.alcohol_default_serving(name : String) : String?
  AlcoholLUT.default_serving_for(name)
end
```

Update `CATEGORY_DEFAULTS` to include seltzer:
```crystal
"seltzer"  => {abv: 5.0, default_ml: 355},
```

### 3. Add category default serving constants to CaffeineLUT

**File:** `josie-health-lut/src/caffeine_lut.cr`

Add constant:
```crystal
CATEGORY_DEFAULT_SERVING = {
  "energy_drink" => "can",
  "energy_shot"  => "shot",
  "coffee"       => "cup",
  "tea"          => "cup",
  "soda"         => "can",
}
```

Add method:
```crystal
def self.default_serving_for(name : String) : String?
  brand = lookup_brand(name)
  return nil unless brand
  CATEGORY_DEFAULT_SERVING[brand[:category]]?
end
```

Expose via `josie-health-lut.cr`:
```crystal
def self.caffeine_default_serving(name : String) : String?
  CaffeineLUT.default_serving_for(name)
end
```

### 4. Use default serving in log_handler annotation strings

**File:** `services/core/src/handlers/log_handler.cr`

In `process_alcohol_dose` (line ~1106), change:
```crystal
# Before:
serving_str = parsed[:serving] ? "#{parsed[:count]}#{parsed[:serving]}" : "#{parsed[:count]}"

# After:
serving_str = if parsed[:serving]
                "#{parsed[:count]}#{parsed[:serving]}"
              else
                default = JosieHealth::LUT.alcohol_default_serving(substance_name)
                default ? "#{parsed[:count]}#{default}" : "#{parsed[:count]}"
              end
```

In `process_caffeine_dose` (line ~1159), same pattern:
```crystal
# Before:
serving_str = parsed[:serving] ? "#{parsed[:count]}#{parsed[:serving]}" : "#{parsed[:count]}"

# After:
serving_str = if parsed[:serving]
                "#{parsed[:count]}#{parsed[:serving]}"
              else
                default = JosieHealth::LUT.caffeine_default_serving(substance_name)
                default ? "#{parsed[:count]}#{default}" : "#{parsed[:count]}"
              end
```

### 5. Update LUT calculate_dose methods for consistency

**File:** `josie-health-lut/src/alcohol_lut.cr` (line 152)

Same pattern in `AlcoholLUT.calculate_dose`:
```crystal
serving_str = if parsed[:serving]
                "#{parsed[:count]}#{parsed[:serving]}"
              else
                default = CATEGORY_DEFAULT_SERVING[brand_info.try(&.[:category])]?
                default ? "#{parsed[:count]}#{default}" : parsed[:count].to_s
              end
```

**File:** `josie-health-lut/src/caffeine_lut.cr` (line 180)

Same for `CaffeineLUT.calculate_dose`.

## Files Modified

1. `josie-health-lut/data/alcohol.json` -- add whiteclaw, truly brands + seltzer category
2. `josie-health-lut/src/alcohol_lut.cr` -- CATEGORY_DEFAULT_SERVING, default_serving_for, update CATEGORY_DEFAULTS
3. `josie-health-lut/src/caffeine_lut.cr` -- CATEGORY_DEFAULT_SERVING, default_serving_for
4. `josie-health-lut/src/josie-health-lut.cr` -- expose alcohol_default_serving, caffeine_default_serving
5. `services/core/src/handlers/log_handler.cr` -- use default serving in process_alcohol_dose, process_caffeine_dose

## Verification

1. `cd services && make check` -- type check passes
2. `cd services && make test` -- all tests pass
3. Manual API test:
   ```bash
   curl -u admin:zEx7eNN4LfvpJuXdb1QPbLiDfya0ur https://api.josie.health/v1/metrics/substances
   ```
4. Verify annotation output by logging a test dose via the API and checking the annotation contains "1can" for whiteclaw, "1shot" for vodka, "1cup" for coffee, etc.
