How CP Liquor works
CP Liquor is a three-layer system: it captures what's on your shelf (manual, photo, video, or scale), keeps a live inventory the bar can run from, and surfaces variance and anomaly signals so loss is visible the same shift it happens. This page explains every feature, every flag, and every workflow in plain English. Help icons throughout the app deep-link here.
Table of Contents
01The 3-Layer Architecture
Every feature in CP Liquor sits in one of three layers. Understanding the layers makes the rest of the document click into place.
Layer 1 — Capture
How a count enters the system. Eight methods, ranging from a hand slider to thermal imaging. Each method writes to the same liquor_counts table with a confidence score and a method tag, so downstream math doesn't care how the number got there.
- Manual entry · single-photo · shelf scan · video walk-by · multi-photo · Bluetooth scale · FLIR thermal · photo QA gate
Layer 2 — Inventory
The operational truth your bar runs from. Counts become baselines, POS pours subtract from those baselines in real time, par status flips, and bottles-on-hand updates. This is the layer the bar manager opens at 9pm to know "do I need to drop another bottle of Patrón."
- Live levels · par status · bottles-on-hand · calibration · quick audit
Layer 3 — Loss Detection
The reason CP Liquor exists. Variance flags catch over-pours, anomaly z-scores surface statistical outliers against a 30-day baseline, the bartender heatmap attributes loss pro-rata, and annualized projections turn one bad shift into a real dollar number management can act on.
- Variance flags · anomaly flags · bartender heatmap · competing-brand insights · annualized loss
02Capture Methods
Eight ways to turn the bar into numbers. Use whichever the staff actually completes — accuracy is moot if the count never happens.
Manual Entry
A slider per bottle from 0% to 100%. No AI, no camera. Always works, even on a 5-year-old phone in a back-room with bad light. This is the fallback when every other method fails. Confidence is hard-coded at 1.00 because a human eyeballed it.
Single Photo
Snap one bottle. Vision model returns a fill percentage. Best for bottles you wanted to recount or that the shelf-scan flagged as low-confidence. Typical accuracy: ±3-5% on clear glass under decent light. Drops to ±10% on dark glass or backlit shots — that's why we have the QA gate (below).
Shelf Scan
One wide-angle photo of the whole shelf. The vision model identifies every bottle, locates each one in the frame, and reads its level. Fastest method for a 12-bottle wall — under 4 seconds end-to-end. Trade-off: small bottles tucked behind larger ones can be missed; the system flags those as not_seen and asks for a single-photo recount.
Video Walk-by
Record a 10-15 second pan along the shelf. The system extracts 6 frames at even intervals, runs each frame through the vision model in parallel, then merges the results.
How the merge works
For each bottle the system collects every reading across all 6 frames. A bottle "seen in 5 of 6 frames" is high-confidence and the median fill percentage wins. A bottle "seen in 1 of 6 frames" is flagged as low-confidence and the operator is shown a single-photo recount prompt.
Concrete example from a recent test pan: 12 bottles total, 11 seen in 5+ frames (auto-accepted), 1 bottle seen in 2 frames (flagged for recount). Total wall-clock: 8 seconds for the video, 4 seconds for the merge.
Multi-Photo
Take 1-6 deliberate stills with a re-shoot button between each. Same merge logic as video — readings are pooled across frames and median wins. Useful when video pan quality is poor (motion blur, autofocus hunting) or when you want HDR / intentional composition on a tricky bottle.
When multi-photo beats video
Multi-photo wins anywhere photo resolution matters more than speed: dim back-bar with foil-wrapped tequila, a busy shelf with reflections, or when the operator wants to deliberately frame each shot to avoid backlight. Each still gets full sensor resolution and HDR; video frames are compressed and rolling-shutter-blurred.
Bluetooth Scale
Connect a Web Bluetooth scale, place a bottle, and the live grams reading converts to ounces using the bottle's empty_weight_g and full_weight_g calibration values. Most accurate method available — typical ±0.1 oz. There's also a manual grams field for shops with a non-Bluetooth scale: type the number, the math is the same.
Recommended on Pro tier for high-cost bottles ($60+ retail). At those prices, a 0.5 oz misread on a single bottle erases a third of the savings the system delivers — scale closes that gap.
FLIR Thermal (Optional)
An optional Pro-tier hardware add-on. A FLIR phone-clip camera reads the temperature differential between liquid and air inside the bottle — works through opaque foil, frosted glass, and dark amber where vision models struggle. Honest framing: 90% of bars never need this. It exists for the operator with a wall of mezcal in colored glass who otherwise can't get reliable counts.
Photo QA Gate
Before any photo enters the model, a client-side check measures average brightness and contrast. Photos that fail the gate are rejected with a "too dark / re-shoot" or "too low contrast" message and never charged against the AI cost budget. Saves ~12% of vision calls and stops garbage-in-garbage-out variance.
03Inventory Operating Layer
The bar runs from this layer. Counts establish baselines, POS pours subtract in real time, par status flips, alerts fire.
Live Levels
The single most important formula in the app:
// what's actually in the bottle right now
live_oz = last_count.total_oz − SUM(pos_pours since last_count)
Every count writes a row to liquor_counts with the total ounces measured. Every POS pour writes a row to liquor_pos_pours. Live oz is the difference, computed on demand. No nightly cron, no stale snapshot — open the app at 11:42 pm and the number reflects the pour that hit the rail at 11:41.
Par Status
Each bottle has a par_level_oz. Live oz is colored against it:
| Status | Threshold | What it means |
|---|---|---|
| OK | live_oz ≥ par | Stocked. No action. |
| LOW | 50% ≤ live_oz < 100% of par | Plan an order on the next supplier cycle. |
| CRITICAL | live_oz < 50% of par | Auto-fires an in-app alert. Manager dashboard widget pings. |
Bottles on Hand
Operators think in bottles, not ounces. The display computes:
full_bottles = floor(live_oz / size_oz)
partial_oz = live_oz − (full_bottles × size_oz)
display = "{full_bottles} full + {partial_oz}oz" // e.g. "2 full + 14oz"
Calibration ("Digital Twin")
Each bottle in the library carries a calibration profile that the vision model uses as a fingerprint:
- Reference photo — a clean front shot at full pour, used as the visual anchor for fill estimation.
- Empty weight (g) + full weight (g) — used by the Bluetooth scale path to convert grams to ounces.
- Position memory — the bottle's typical (x,y) on the shelf scan, so the model finds it even when partially occluded.
- Trained vs Training — bottles need 3+ confirmed counts before they're marked
trained; until then they'retrainingand the system biases toward higher confidence thresholds and more frequent recount prompts.
This per-bottle profile is what the team internally calls the "digital twin" — a stored representation of each bottle that persists across counts.
Quick Audit
Adjust a single bottle without opening or closing a session. Useful for: a delivery dropped 4 fresh bottles of vodka mid-shift, a bartender broke a bottle and wrote it off, or you spotted a count that was clearly wrong on the morning report. Quick audit writes a liquor_counts row tagged method='audit' and is excluded from variance attribution.
04Sessions & Counts
A session is the unit of measurement loss is computed against — bracket the shift, capture both ends, the math becomes possible.
Open Count
Start of shift. The session is created, baseline ounces are recorded for every bottle, and the timer starts. POS pours from this moment forward are attributed to this session. The session card on the home screen shows the open dot lit and the elapsed time.
Close Count
End of shift. The system asks for a fresh capture using any of the eight methods. The moment counts are saved, three things happen automatically:
- Variance is computed for every bottle:
expected_oz = open_oz − pos_pours; variance_oz = expected_oz − close_oz - Anomaly z-scores are computed against the rolling 30-day baseline.
- Shift-close report auto-fires (in-app + Slack if configured).
Audit (No Session)
Spot-check a bottle without bracketing a shift. Writes to counts but not to a session. Doesn't trigger variance compute. Useful when you walk in mid-day and want a number for one specific bottle without disturbing the open session.
Auto-advance & Auto-fire
Sessions advance automatically: open at 4pm, close at 2am, system marks the open count with auto_started if no one tapped Open. The same is true on close — if 30 minutes pass after the POS terminal goes idle, the system prompts to close. Alerts fire automatically on critical par, anomaly detection, and order-sent events.
05Loss Detection
Two flag families, one heatmap, and two dollar projections. Together they answer "is something wrong tonight?" and "is something wrong systemically?"
Variance Flag
A static threshold flag. A bottle is flagged if either:
- Dollar variance > $20 (lost retail value), or
- Volume variance > 4 oz (about 3 standard pours)
Variance is the gap between what the POS said you poured and what the bottle actually shows. It is not an accusation. Five things can cause it, in roughly this order of frequency:
- Over-pour — bartender free-pours and hits 1.7 oz when the recipe says 1.5
- Comps & spillage — drinks given away or knocked over that were never rung in
- Recipe drift — a margarita is now made with 2.0 oz tequila by house custom, but the recipe in the POS still says 1.5
- Mismeasurement — a bad count, low-confidence photo, scale calibration off
- Theft — last on the list, but real, especially over a 30-day pattern
The tonight-only variance number on the live demo is $164.46. Annualize that — see below — and it becomes a number management can react to.
Anomaly Flag
A statistical flag. For every bottle, the system keeps a 30-day rolling distribution of nightly variance ($). When tonight's variance arrives, it's converted to a z-score:
z = (tonight_variance − mean_30d) / stddev_30d
A z-score of 2 means tonight is two standard deviations from the bottle's normal — about a 5% probability under typical statistics. Bands:
| Severity | Z-score | Trigger |
|---|---|---|
| Warning | 2.0 ≤ z < 3.0 | Logged in report. No alert. |
| Critical | z ≥ 3.0 | In-app + Slack alert. |
| Over-credited | z ≤ −2.0 | POS rang in more than the bottle shows poured. Often a recipe error, sometimes a comp adjustment. |
Plain-English version: a z-score above 2 means "this bottle is way outside its normal." On the live demo, Patrón Silver is currently flagged at z = 40.6. That's not a typo. That bottle's variance tonight is 40 standard deviations beyond its 30-day mean, which is the system saying "stop reading the rest of the report and look at this bottle first."
Bartender Heatmap
For any flagged bottle, the system attributes variance to bartenders pro-rata by share-of-pours. If a bottle has $40 of variance and bartender A poured 60% of the night's pours from it while B poured 40%, A is attributed $24 and B is attributed $16.
Honest about the approximation: this assumes loss is proportional to volume. It isn't always — one bartender may pour heavier and another lighter. The heatmap is a starting point, not a verdict. Use it to know who to watch on the next shift, not who to fire.
Competing-Brand Insights
Within a category (vodka, tequila, gin), the system shows variance spreads across competing brands. When Patrón Silver shows $150 of variance and four other tequilas show $5-$15 each, that's a brand-specific signal — likely an over-pour problem on that one bottle, not a category-wide drift.
Annualized Loss
The headline number for any conversation with ownership: annualized_loss = nightly_variance × 365. With $164.46 of variance on the live demo night, that projects to $60,028/year. Even with conservative assumptions (operate 300 nights, account for half being recipe drift not loss), the residual is meaningful real dollars.
This is a projection, not a guarantee. One bad night doesn't mean every night is bad. The 30-day baseline tells the truer story — and after 30 days of data, the annualized number is precise rather than projected.
06Recipes & Cocktails
30 cocktails seeded out of the box. 8 featured signatures. Every recipe ingredient links back to the bottle library — that's what makes recipe-level variance possible.
Categories & Counts
- Cocktail — classic and craft mixed drinks (most of the 30)
- Shot — single-pour and layered shots
- Highball — spirit + mixer over ice
- Texas signature — the regional house style; 8 of the 30 are featured signatures
Recipe-to-Bottle Linking
Every ingredient on a recipe carries a bottle_id reference. When a cocktail is rung in at the POS, the system knows which exact bottles should have been touched and in what amounts. That's how the loss-detection layer can distinguish "lots of margaritas were sold tonight" (expected pour pattern) from "someone is over-pouring tequila on margaritas" (variance against recipe spec).
Recipe Fields
| Field | Purpose |
|---|---|
| Glass | Coupe, rocks, highball, etc. — guidance for the bartender; not enforced. |
| Ice | Cubed, crushed, neat, single rock. |
| Garnish | String list. Surfaced on the prep card. |
| Technique | Stir, shake, build, blend. |
| Instructions | Step-by-step prep, free-text. |
Pour Cost & Margin
cost = SUM(ingredient.oz × bottle.cost_per_oz) margin = (retail_price − cost) / retail_price × 100
Live example: a margarita with 1.5 oz of $0.85/oz tequila ($1.28), 1 oz of $0.40/oz triple sec ($0.40), and 0.75 oz of $0.18/oz lime ($0.14) costs $1.82 to make. Sold at $12 retail, that's a $10.18 margin and 84.8% margin percentage. Drift the tequila pour to 2.0 oz and cost jumps to $2.24 — margin drops to 81.3%. Across 200 margaritas that's $84 of unbooked cost.
07Suppliers & Ordering
A supplier directory plus a single ordering edge function that picks the right dispatch method per supplier — EDI, email, webhook, or portal link.
Supplier Directory
Each supplier carries a default_dispatch field with one of four values. The directory also stores contact name, email, phone, EDI partner ID (if applicable), webhook URL (if applicable), and portal URL (if applicable).
EDI 850 Purchase Order
X12 850 is the legacy electronic format big distributors (Southern Glazer's, RNDC, Breakthru) accept. It's a flat ASCII file made of segments, each segment ends with ~, fields are split with *, sub-fields with :. Every distributor's EDI inbox accepts it without setup work — that's why we generate it.
What each segment means (plain English)
ISA — interchange envelope. Tells the distributor "here's a transmission from sender X to receiver Y, version Z."
GS — functional group header. Wraps one or more transactions of the same type.
ST — transaction set start. ST*850 means "this is a purchase order."
BEG — beginning of PO. PO number, PO date, purpose code.
N1 — name segments. Buyer and seller, with addresses.
PO1 — line item. Quantity, unit, price, item ID.
PID — product description. Free-text bottle name + size.
CTT — total line count.
SE / GE / IEA — closing envelopes for ST / GS / ISA respectively.
Real generated example (one PO from a recent demo run):
ISA*00* *00* *ZZ*UNION28 *ZZ*SOUTHERN *250428*1142*U*00401*000000001*0*P*>~ GS*PO*UNION28*SOUTHERN*20260428*1142*1*X*004010~ ST*850*0001~ BEG*00*NE*PO-2026-0428-001**20260428~ N1*BY*UNION 28 TAPROOM~ N1*SE*SOUTHERN GLAZER'S*92*SG-DAL~ PO1*1*6*EA*32.50**BP*PATRON-SLV-750~ PID*F****PATRON SILVER TEQUILA 750ML~ PO1*2*4*EA*45.00**BP*WOODFRD-RES-750~ PID*F****WOODFORD RESERVE BOURBON 750ML~ CTT*2~ SE*8*0001~ GE*1*1~ IEA*1*000000001~
Email Dispatch
For mid-size distributors that don't accept EDI but expect formatted orders. The system builds a pre-filled HTML email draft with PO number, line items, totals, and a polite cover paragraph. If the user's mail client is wired up, one click sends it. If not, the system falls back to a mailto: with the body URL-encoded.
Webhook Dispatch
For modern suppliers running their own POS-integration API. The system POSTs a JSON payload to the supplier's webhook URL with HMAC-signed headers and waits for a 2xx. Any non-2xx response surfaces a manual-fallback dialog.
POST {supplier.webhook_url}
Content-Type: application/json
X-CP-Signature: sha256=...
{
"po_number": "PO-2026-0428-001",
"buyer": "Union 28 Taproom",
"ordered_at": "2026-04-28T11:42:00Z",
"lines": [
{ "sku": "PATRON-SLV-750", "qty": 6, "unit_price": 32.50 },
{ "sku": "WOODFRD-RES-750", "qty": 4, "unit_price": 45.00 }
]
}
Portal Link
For suppliers whose only acceptance method is their own web portal. The dispatch UI opens the portal URL in a new tab, drops the generated PO into the clipboard, and shows a "Paste this into the supplier portal" toast. Less elegant, but works for every supplier.
Mailto Fallback
Universal escape hatch. Any supplier without API or portal can be ordered via mailto: with a fully-populated body. The PO is also saved to the orders table so receiving and reconciliation still work.
Predictive Ordering
The get_pour_forecast RPC predicts how many ounces of a given bottle will be poured in the next N days. Formula:
daily_avg = mean(pos_pours_oz over last 30 days)
dow_mult = day_of_week_multiplier // e.g. Friday=1.42, Tuesday=0.78
forecast = daily_avg × dow_mult × forecast_days
Day-of-week multipliers are computed from the bar's own history — every bar has a different Friday-vs-Tuesday ratio, so we don't use industry averages.
Auto-reorder Draft
For any bottle below par, the system drafts a reorder quantity:
shortfall_oz = par_oz − live_oz + forecast_oz(7 days) qty_bottles = ceil(shortfall_oz / size_oz)
The draft is grouped by supplier, totals are computed, and the user reviews & approves before send. No order goes out without explicit confirmation.
08Receiving & Invoice OCR
Snap the invoice that came on the truck. The system OCRs it, matches lines against the open PO, lets you adjust discrepancies, and confirms in one tap.
The Receive Flow
- Open the PO on the receiving screen.
- Tap "Snap Invoice" — the camera opens.
- Photo is uploaded; OCR runs server-side.
- OCR'd lines are matched against PO lines using a fuzzy scorer.
- Each line shows expected qty / received qty / discrepancy badge.
- Operator adjusts any flagged line, taps Confirm.
- PO row updates, live inventory recomputes immediately.
Discrepancy Types
| Type | What it means |
|---|---|
| qty_short | Truck delivered fewer than ordered. Backorder credit owed. |
| qty_over | Truck delivered more than ordered. Operator decides accept or refuse. |
| price_changed | Invoice price differs from PO price by more than $0.50/unit. |
| substituted | Different SKU shipped (e.g. 1.0L instead of 750mL). |
| missing | PO line doesn't appear on the invoice at all. |
How Matching Works
OCR text is folded (diacritics removed, lowercased) and tokenized. Each invoice line scores against each PO line on:
- Brand-token overlap — common tokens between names (e.g. "patron" + "silver" both present)
- Size match — 750mL = 750mL is high-value, 1.0L vs 750mL is a flag
- Price proximity — within ±10% scores well, beyond that flags
price_changed
The composite score must clear 0.55 for an auto-match. Below that, the line surfaces as "needs operator review" with the top three candidates ranked.
What Gets Written on Confirm
For each line: received_qty, received_total_cost. For the order: status='received', received_at timestamp. Live inventory recomputes the moment confirm is tapped — no batch job, no nightly reconcile.
09Chat Agent
Ask questions in plain English. The agent grounds every answer in real data via 9 tools — never invents.
The 9 Tools
| Tool | Purpose |
|---|---|
| get_inventory_status | Live oz, par status, bottles-on-hand for one bottle or the whole library. |
| get_variance_report | Tonight's or any date's variance, ranked by dollars or volume. |
| get_anomaly_flags | Bottles currently flagged with z ≥ 2, severity-banded. |
| get_bartender_attribution | Pro-rata variance by bartender for a date range. |
| get_pour_forecast | Predicted ounces poured for next N days, by bottle. |
| get_recipe | Full recipe with ingredients, glass, technique, pour cost, margin. |
| get_supplier_orders | Open / received / cancelled POs by supplier or date. |
| draft_reorder | Compute auto-reorder quantities for bottles under par. Returns a draft, never sends. |
| export_chat_summary | Save the current conversation as a report row for sharing. |
Multi-turn Grounding
The agent keeps the last 8 turns of context. If you ask "why is Patrón flagged?" then "what about the night before?", the second question inherits the bottle context. Every numerical claim cites the tool that produced it. If a tool returns no data, the agent says so — it doesn't fill gaps with plausible-sounding numbers.
Cost
~$0.01 per question on average. A typical conversation runs 3-5 questions and costs under $0.05. The cost ceiling is enforced server-side; runaway loops get cut off at $0.25.
Sample Questions
- "Which bottles are below par right now?"
- "Why is Patrón Silver flagged tonight?"
- "How much tequila will we need for the next 7 days?"
- "Show me the margin on our top 5 cocktails."
- "Draft a reorder for Southern Glazer's."
- "What was last Friday's variance?"
What It Can't Do
Honest limits, posted up front:
- It will not place an order, void a count, or modify inventory without an explicit confirmation tap from a human.
- It only knows what its 9 tools expose — it can't read scheduling, payroll, or POS settings beyond pours.
- It does not predict theft. It surfaces variance and anomaly. The interpretation is the manager's job.
10Reports
Three report types. One auto-fires every close. One is on-demand. One is generated when chat surfaces something report-worthy.
Shift-Close Report (Auto)
Generated automatically the moment a close-count session lands. Payload includes: total variance ($ and oz), flagged bottle count, anomaly count, top 5 variance bottles, bartender attribution, total pours, total revenue from those pours. A short prose summary is generated alongside the structured payload so the report can be shared as text.
Daily Brief (Manual)
Chat-agent-generated executive summary spanning the trailing 24 hours. Fired manually with the "Daily Brief" button on the Reports tab — useful for sending to ownership before the morning meeting. Pulls from the same 9 tools the chat agent uses.
Chat Export
When a chat conversation surfaces something report-worthy ("here's why three bottles are flagged tonight" with the supporting data), tap Export. The conversation is saved to the reports table with full message history and any tool outputs cited.
Where Reports Surface
- In-app Reports tab — full history, searchable.
- Manager Dashboard widget — most recent 5 reports, with one-tap drill-in.
11Alerts
Two channels. Three auto-fire conditions. One rate limit so the system never spams.
Channels
- In-app — toast notifications + alerts panel badge.
- Slack — webhook, posts a structured block with the variance amount, bottle name, and a link back to the app.
Auto-fire Conditions
| Trigger | What fires |
|---|---|
| Bottle drops below 50% of par (CRITICAL) | Order-soon alert, in-app + Slack. |
| Anomaly z-score ≥ 3.0 | Critical-anomaly alert, in-app + Slack. |
| Order successfully sent to supplier | Confirmation alert, in-app only. |
Rate Limit
30 minutes per (kind, bottle_id). If Patrón Silver fires a critical anomaly at 8:14pm, another anomaly on Patrón Silver won't fire until 8:44pm — even if it's still flagged. This stops a single misbehaving bottle from spamming an entire shift.
Slack Setup
Set the SLACK_WEBHOOK_LIQUOR environment variable on the edge functions to a Slack incoming-webhook URL. No further config needed — the system auto-detects the variable and starts posting. Unset the variable to disable.
12Manager Dashboard Widget
Where ownership and managers consume CP Liquor without opening the bar app.
Where
admin.conciergepilot.co → Operations → Fulfillment → Liquor Inventory
Stat Strip
Four large numbers at the top:
- Tonight's variance — running total in dollars
- Flagged — count of bottles currently flagged (variance + anomaly)
- Below Par — count of bottles in LOW or CRITICAL
- Open Orders — count of POs in
sentorpartialstatus
Recent Reports List
The five most recent reports — auto shift closes, daily briefs, chat exports — with timestamp, type, and one-line summary. Tap any row to read the full report.
Top Variance Bottles
The five bottles with the highest dollar variance over the last 7 days, ranked descending. Each row links to the bottle detail in the live app.
Live App Link
One-tap deep link to the running bar app, which opens to the same view the bartender sees.
13Database & Edge Functions
For developers and integrators. Reference list of every table, edge function, and RPC.
Tables
| Table | Purpose |
|---|---|
| liquor_bottles | The library. One row per SKU. Includes calibration JSONB. |
| liquor_counts | One row per bottle per count event. Method, oz, confidence, photo URL. |
| liquor_sessions | Open/close brackets. One row per shift. |
| liquor_pos_pours | Every pour event from the POS. Bottle, oz, bartender, timestamp. |
| liquor_variance | Computed at session close. One row per bottle per session. |
| liquor_anomaly_flags | Z-score detections. Severity, baseline, current value. |
| liquor_recipes | 30 cocktails seeded. Plus per-tenant additions. |
| liquor_recipe_ingredients | Recipe-to-bottle joins, oz per ingredient. |
| liquor_suppliers | Directory of distributors with dispatch config. |
| liquor_orders | POs with status (draft / sent / partial / received). |
| liquor_order_lines | Per-line qty, price, received_qty. |
| liquor_reports | Auto + manual + chat-export reports. Full payload as JSONB. |
| liquor_alerts | Alert event log with channel, kind, fired_at. |
Edge Functions
| Function | Purpose |
|---|---|
| cp-liquor-vision-count | Vision model wrapper. Single-photo + shelf-scan + multi-photo merge. |
| cp-liquor-video-frames | Extract 6 frames from video, run vision in parallel, merge. |
| cp-liquor-close-session | Compute variance + anomaly, write report, fire alerts. |
| cp-liquor-place-order | Dispatch PO via supplier's configured method. |
| cp-liquor-receive-invoice | OCR an invoice photo, match to PO, return discrepancies. |
| cp-liquor-chat | Chat agent with 9 tools, multi-turn context. |
| cp-liquor-daily-brief | Generate the on-demand daily executive summary. |
| cp-liquor-alert-dispatch | Send to in-app + Slack with rate-limit logic. |
RPCs
| RPC | Purpose |
|---|---|
| get_live_inventory | Return current live_oz + par_status for all bottles. |
| get_pour_forecast | 30-day rolling avg × day-of-week multiplier. |
| compute_variance | Variance + z-score for a session. Idempotent. |
| get_bartender_attribution | Pro-rata variance by bartender for a date range. |
| draft_reorder | Quantity math for bottles under par. |
Calibration JSONB Shape
{
"reference_photo_url": "https://...",
"empty_weight_g": 530,
"full_weight_g": 1410,
"position_xy": [0.42, 0.18],
"trained": true,
"training_count": 7
}
14Glossary
One-line definitions, alphabetical.
| Term | Definition |
|---|---|
| Anomaly | A bottle whose tonight's variance is ≥ 2 standard deviations from its 30-day mean. |
| Bottles-on-hand | Display format: full bottles + remaining ounces. e.g. "2 full + 14oz". |
| Calibration | Per-bottle profile of reference photo, weights, position, training status. |
| Close count | End-of-shift count that triggers variance compute and shift report. |
| Confidence score | 0-1 value attached to every count row indicating measurement reliability. |
| Dispatch method | How a PO is sent: EDI / email / webhook / portal / mailto. |
| EDI 850 | X12 electronic purchase order standard accepted by major distributors. |
| Flagged | A bottle hit either a variance threshold or an anomaly z-score. |
| Live oz | last_count.total_oz minus POS pours since that count. |
| Margin % | (retail − cost) / retail × 100. |
| Open count | Start-of-shift count establishing the baseline for variance. |
| Par level | Target ounces on hand for a bottle. Below 50% triggers CRITICAL. |
| Position memory | Stored (x,y) of a bottle on the shelf-scan frame, used for occluded matches. |
| Pour cost | Sum of ingredient ounces times bottle cost per ounce. |
| POS pours | Pour events written by the POS, attributed to bottles via recipe ingredients. |
| Reference photo | Clean front-shot of a bottle at full pour, used as the visual anchor for fill. |
| Shift report | Auto-generated end-of-shift summary fired the moment close-count completes. |
| Variance | Gap between expected ounces (open − pours) and actual ounces (close). |
| Z-score | Standardized measure of how far a value is from its baseline mean. |
15Quick-start Workflows
Three step-by-step recipes for the things you'll do in the first week.
First-time Setup
- Add bottles. Open the bottle library, tap Add. Enter name, size (e.g. 750mL), category, retail price. Repeat for every bottle on the wall — for the live demo we seeded 12 to start.
- Set par levels. For each bottle, tap the par field and enter target ounces on hand. A reasonable starting point: 1.5 × your typical weekly pour volume.
- First open count. Open a session, run a shelf scan or manual entry, save. This becomes the baseline.
- Calibrate references. For each bottle, snap a clean front-shot at full pour and tap "Set as reference." This is the visual anchor the model uses on every future scan.
Nightly Close Routine
- Open the app at end of shift.
- Tap Close Session.
- Run a shelf scan (fastest), video walk-by, or multi-photo capture.
- Review the auto-merged counts. Re-shoot any low-confidence bottle.
- Save. The session closes, variance computes, and the shift-close report fires automatically.
- Skim the report. Anything flagged in red is worth a 30-second look before you walk out.
Investigating a Flagged Bottle
- Tap the flagged bottle on the home screen.
- Open chat. Ask: "Why is X flagged tonight?"
- Review the pour timeline the agent surfaces — look for clusters by hour.
- Open the bartender heatmap on the variance card. Note who poured the most from this bottle.
- Cross-check: does this bottle's recipe pour spec still match what the bar is actually pouring?
- Two outcomes: (a) update the recipe pour spec to match house custom, which collapses the variance, or (b) schedule a brief with the high-share bartender on next shift.