Skip to content

Annotation-Based Metrics

Metrics derived purely from human annotations on shift videos. No POS data required.


M-1: Manager Touch Count

Field: manager_touch_count · Type: int · Default: 0

Count of manager-at-table annotations in the session.

Formula: count(annotations where type == "manager-at-table")


M-3: Time to Drink Arrival

Field: time_from_session_start_to_drink_arrival_seconds · Type: int | null

Seconds from start-session to first drinks-arrived annotation.

Gate: Requires start-session annotation. Returns null without it.

Formula: max(0, drinks_arrived.time - start_session.time)

gantt
    title M-3 Time to Drink Arrival
    dateFormat X
    axisFormat %s
    section Timeline
    start-session :milestone, m1, 0, 0
    M-3 duration :active, 0, 120
    drinks-arrived :milestone, m2, 120, 120

M-4: Waiter Touch Count

Field: waiter_touch_count · Type: int · Default: 0

Count of waiter-touch annotations in the session.


M-5: Hand Raise Count

Field: hand_raise_count · Type: int · Default: 0

Count of hand-raise annotations in the session.


M-6: Table Cleaned Time

Field: table_cleaned_time_seconds · Type: int | null

Seconds from end-session to the first table-cleaned annotation found in the table-level annotations (not session-level), within the window between session end and the next session start.

Gate: Requires end-session annotation. Returns null without it.

Formula: max(0, table_cleaned.time - end_session.time)

Search window: (end_session.time, next_session_start) — exclusive on both bounds. If no next session, the window extends to the end of the video.

gantt
    title M-6 Table Cleaned Time
    dateFormat X
    axisFormat %s
    section Session N
    session :0, 300
    end-session :milestone, m1, 300, 300
    section Cleaning Window
    M-6 duration :active, 300, 420
    table-cleaned :milestone, m2, 420, 420
    section Session N+1
    next start :milestone, m3, 500, 500

M-8: First Waiter Contact Time

Field: first_waiter_contact_time_seconds · Type: int | null

Seconds from start-session to the first waiter-touch annotation.

Gate: Requires start-session annotation. Returns null without it.

Formula: max(0, first_waiter_touch.time - start_session.time)


M-12: Drink Delivery from Order Taken

Field: drink_delivery_from_order_taken_seconds · Type: int | null

Seconds from order-taken annotation to first drinks-arrived.

Gate: Requires both order-taken and drinks-arrived. No POS data needed.

Formula: max(0, drinks_arrived.time - order_taken.time)


M-13: Food Delivery from Order Taken

Field: food_delivery_from_order_taken_seconds · Type: int | null

Seconds from order-taken annotation to first food-arrived.

Gate: Requires both order-taken and food-arrived. No POS data needed.

Formula: max(0, food_arrived.time - order_taken.time)


M-14: Post-Payment Linger Time

Field: bill_payment_to_end_session_seconds · Type: int | null

Seconds from bill-payment to end-session. Measures how long guests stay after paying.

Gate: Requires both end-session and bill-payment.

Formula: max(0, end_session.time - bill_payment.time)

Note

In production, bill-payment always occurs BEFORE end-session (median -103s). The formula yields end minus bill, giving a positive value.


M-25: Menu Delivery Time

Field: menu_delivery_time_seconds · Type: int | null

Seconds from start-session to the first menu-dropped annotation. Measures how quickly the waiter delivers the menu after guests sit down.

Gate: Requires both start-session and menu-dropped. Returns null without either.

Formula: max(0, menu_dropped.time - start_session.time)

Changed in version 2.5.0

Values only appear in sessions annotated with menu-dropped after 2026-04-11.


M-26: Bill Closing Time

Field: bill_closing_time_seconds · Type: int | null

Seconds from bill-dropped to bill-payment. Measures payment friction after the bill is presented. Distinct from M-14, which measures post-payment linger.

Gate: Requires both bill-dropped and bill-payment. Returns null without either.

Formula: max(0, bill_payment.time - bill_dropped.time)

Changed in version 2.5.0

Values only appear in sessions annotated with bill-dropped after 2026-04-06.


M-27: Courtesy Delivery from Session Start

Field: courtesy_delivery_from_session_start_seconds · Type: int | null

Seconds from start-session to first courtesy-arrived annotation. Measures how quickly a courtesy item (bread, water, amuse-bouche) reaches the table after guests sit down.

Gate: Requires start-session annotation. Returns null without it.

Formula: max(0, courtesy_arrived.time - start_session.time)

gantt
    title M-27 Courtesy Delivery from Session Start
    dateFormat X
    axisFormat %s
    section Timeline
    start-session :milestone, m1, 0, 0
    M-27 duration :active, 0, 150
    courtesy-arrived :milestone, m2, 150, 150

Added in version 2.5.1

Values only appear in sessions annotated with courtesy-arrived. Currently only Parmessano uses this tag (196 annotations, 60 sessions as of 2026-04-20).


Annotation Event Types Reference

Event Type Kebab-case Level Used By
Session start start-session Session M-2, M-3, M-8, M-27
Session end end-session Session M-6, M-14
Waiter touch waiter-touch Session M-4, M-8, Compliance
Drinks arrived drinks-arrived Session M-3, M-9, M-10, M-12
Food arrived food-arrived Session M-7, M-11, M-13
Manager at table manager-at-table Session M-1
Hand raise hand-raise Session M-5
Order taken order-taken Session M-12, M-13
Bill payment bill-payment Session M-14, M-26
Menu dropped menu-dropped Session M-25
Bill dropped bill-dropped Session M-26
Courtesy arrived courtesy-arrived Session M-27
Table cleaned table-cleaned Table M-6
No waiter touch no-waiter-touch Session Compliance