# Conventions

Shared assumptions the whole system relies on. Keep these consistent.

## Customer tags (membership)

Active club members are identified entirely by customer tags — no separate list.

| Tag | Meaning |
|-----|---------|
| `wineclub` | Active club member (included in reminders + fulfillment) |
| `wc-tier:<key>` | Their tier — one of `classic-3`, `classic-6`, `reserve-3`, `reserve-6`, `magnum` |

These must be set when a subscription starts/changes. Options:
- **Appstle → Shopify Flow:** on subscription created/updated, add the tags. Recommended.
- Set manually for pilot members while wiring Flow.

The storefront section reads `wc-tier:*` to resolve the customer's tier; the Worker's
`listMembers()` queries `tag:wineclub` and reads the same tag for the tier.

## Handles

- Cycle metaobject handle: `q2-2026`, `q3-2026`, …
- Tier pool handle: `<cycle>-<tier>`, e.g. `q2-2026-classic-3`. The Worker looks pools up
  by this exact pattern (`fetchTierPool`), so the handle format is load-bearing.

## Tier keys

`classic-3`, `classic-6`, `reserve-3`, `reserve-6`, `magnum`. Used in tags, handles,
metaobject `tier` field, and order tags. Don't rename without updating all four.

## Order tags (packing / shipping)

Generated fulfillment orders carry: `wineclub`, `tier:<key>`, `cycle:<handle>`,
`pack:<key>`, and `customized` | `default`. Extras draft orders carry `wineclub`,
`cycle:<handle>`, `extras`. Use these to filter for packing runs and 3PL export.

## Cycle status lifecycle

`draft` → `open` (window live, portal editable) → `fulfilled` (orders generated).
The portal only renders for a cycle with `status = open`; the Worker refuses saves
outside that and flips to `fulfilled` after generating orders.

## Dates

`open_at` / `close_at` are dates (no time). The scheduler compares against UTC midnight,
firing reminders at `close_at − 10 days` and fulfillment on `close_at`. Cron runs 08:00
UTC daily; adjust in `wrangler.toml` for the winery's timezone if needed.
