Atoms mode
Purpose¶
Atoms mode is the configuration of Sea Suite for businesses whose management layer lives in shifts, locations, inventory, equipment, and safety rules rather than tickets and pull requests. This document specifies what changes: the Org Model extensions, the scheduling solver's inputs, constraints, and objective, the shift lifecycle, the exact SMS conversations for swaps and call-outs, location-level health, checklist and compliance cadences, inventory and equipment signals, the voice line, the POS/time-clock/scheduling integrations, and the safety escalation path. The Chonks are the same; the surface moves to phones and the floor, and the math moves into the scheduling solver.
Scope / Non-scope¶
In scope
- Org Model extensions for locations, shifts, skills, availability, labor rules, checklists, equipment, and inventory.
- The scheduling solver as a function service: inputs, hard and soft constraints, objective, outputs, explanation, and re-solve triggers.
- The shift state machine and the autonomy class of every transition.
- SMS conversation flows for swaps, call-outs, open shifts, and no-shows, including message text.
- Location-level health, checklist and compliance cadences, inventory and equipment signals.
- The voice line for deskless staff.
- Integration contracts for POS, time clocks, scheduling platforms, and IoT feeds, with system-of-record rules.
- The safety escalation path.
Non-scope
- General communication rules, caps, and tone (see Communication Engine); this document only adds Atoms-specific flows.
- Connector authentication and API specifics (see Integrations Spec).
- Payroll computation. Sea Suite exports hours and premiums; the payroll system is the system of record.
- Legal determination of which labor rules apply to a tenant. Rule sets are configured at onboarding under counsel review; this document specifies how they are represented and enforced.
- The demand forecasting model's internals; this document specifies its interface.
What changes in Atoms mode¶
| Dimension | Bits (default) | Atoms |
|---|---|---|
| Primary IC surface | Slack/Teams thread | SMS thread; voice line; app optional |
| Unit of work | Ticket, PR, project | Shift, checklist, station |
| Unit of org | Team | Location (and station within it) |
| Capacity | Person-hours per week from allocation | Scheduled hours from the solver against a demand forecast |
| Stall detection | Unreviewed PR, bouncing ticket | Uncovered shift, missed checklist, late punch, cold fridge |
| Function services added | — | Scheduling solver, demand forecast interface, labor-rule engine, checklist engine |
| Integrations added | — | POS, time clock, scheduling platform, IoT/sensors |
| Manager of record | Department lead | Location lead (per location), with a backup |
| Listening period | Read systems, meet everyone | Same, plus one full schedule cycle observed before the solver publishes |
| Graduation | Per function per org | Per function per location; the solver starts in SHADOW (produces schedules that are compared to the human's, not published) |
Everything in Atoms mode is per location. A tenant with three restaurants has three locations, three location leads, three labor-rule resolutions, and three graduation states, sharing one Org Model.
Org Model extensions¶
type Location = {
id: LocationId; org: OrgId;
name: string; tz: string; address: Address;
jurisdiction: JurisdictionId; // resolves the labor rule set
hours: Array<{ day: 0|1|2|3|4|5|6; open: "HH:MM"; close: "HH:MM" }>;
stations: StationId[];
lead: PersonId; backupLead: PersonId; // manager of record for the location
minStaffing: Array<{ station: StationId; window: TimeWindow; headcount: number }>;
keyholderRequired: boolean;
};
type Station = { id: StationId; location: LocationId; name: string; requiredSkills: SkillId[] };
type Skill = { id: SkillId; name: string; certification?: { kind: string; expiresField: true } };
type Availability = {
person: PersonId;
recurring: Array<{ day: 0|1|2|3|4|5|6; from: "HH:MM"; to: "HH:MM"; preference: "prefer" | "available" | "avoid" }>;
exceptions: Array<{ date: Date; from?: "HH:MM"; to?: "HH:MM"; kind: "unavailable" | "available" }>;
maxHoursPerWeek?: number; // the person's own cap, may be below the legal one
minHoursPerWeek?: number; // the person's stated target
locations: LocationId[]; // shared staff work at more than one
offHoursShiftAlerts: boolean; // consent to open-shift SMS during quiet hours
updatedAt: Instant; actor: PersonId; // availability is always the person's own edit
};
type Shift = {
id: ShiftId; location: LocationId; station: StationId;
start: Instant; durationMin: number; // absolute duration; local start is derived
assignee?: PersonId;
status: ShiftStatus; // see state machine
requiredSkills: SkillId[];
premiums: Array<{ kind: "overtime" | "clopening" | "predictability" | "reporting_time" | "split"; amount?: Money; ruleId: string }>;
history: Array<{ at: Instant; from: ShiftStatus; to: ShiftStatus; actor: PersonId | ChonkId; reason: string }>;
publishedAt?: Instant; version: number;
};
type LaborRuleSet = {
jurisdiction: JurisdictionId; version: string; reviewedBy: string; reviewedAt: Date;
rules: LaborRule[];
};
type LaborRule =
| { kind: "max_hours_day"; hours: number; appliesTo?: "minor" | "all" }
| { kind: "max_hours_week"; hours: number; appliesTo?: "minor" | "all" }
| { kind: "min_rest_between_shifts"; hours: number; consentAllowed: boolean; premium?: Money }
| { kind: "meal_break"; afterHours: number; durationMin: number; paid: boolean }
| { kind: "rest_break"; everyHours: number; durationMin: number }
| { kind: "overtime"; dailyAfter?: number; weeklyAfter: number; multiplier: number; approvalRequired: boolean }
| { kind: "minor_hours"; schoolDay: { earliest: "HH:MM"; latest: "HH:MM"; maxHours: number }; nonSchoolDay: { earliest: "HH:MM"; latest: "HH:MM"; maxHours: number } }
| { kind: "predictive_notice"; days: number; changePremium: Money; reportingTimePayHours: number }
| { kind: "max_consecutive_days"; days: number }
| { kind: "collective_agreement"; ref: string; constraints: string[] }; // compiled per agreement
type TimePunch = { person: PersonId; location: LocationId; kind: "in" | "out" | "break_start" | "break_end"; at: Instant; source: "clock" | "app" | "sms" | "manual"; shift?: ShiftId };
type Checklist = { id: string; location: LocationId; kind: "opening" | "closing" | "food_safety" | "equipment" | "custom"; items: ChecklistItem[]; cadence: CadenceRef; assigneeRole: StationId | "lead"; evidence: "tap" | "photo" | "reading" };
type ChecklistItem = { id: string; text: string; evidence: "tap" | "photo" | "reading"; reading?: { unit: string; min?: number; max?: number } ; critical: boolean };
type Equipment = { id: string; location: LocationId; name: string; sensors?: SensorRef[]; serviceVendor?: VendorId; thresholds: Array<{ metric: string; min?: number; max?: number; durationMin: number; severity: "info" | "warn" | "critical" }> };
type InventoryItem = { id: string; location: LocationId; sku: string; onHand: number; par: number; reorderLeadDays: number; source: "pos" | "count" | "manual"; observedAt: Instant };
Rules that follow from the schema:
Availabilityis edited only by the person (or by an admin withreasonand the person notified). The manager never edits someone's availability.Shift.startis absolute anddurationMinis absolute; paid hours, rest gaps, and overtime are computed in absolute time, which makes DST correct by construction (EC-SCHED-004).LaborRuleSetcarriesreviewedByandreviewedAt. A location with an unreviewed rule set cannot leaveSHADOW.- A minor is represented by
Person.minor: booleanandPerson.minorUntil: Date, derived from the HRIS at sync and never displayed beyond the scheduling constraint.
Scheduling solver¶
The solver is a deterministic function service. The Chief Operating Orca calls it, explains its output, and never overrides it in prose. Every solve is replayable from its inputs.
Inputs¶
type SolveRequest = {
location: LocationId;
horizon: { from: Date; to: Date }; // typically 2 weeks, aligned to the notice rule
demand: DemandForecast; // from the forecast interface, below
people: Array<{ person: PersonId; skills: SkillId[]; availability: Availability; hoursThisWeek: number; recentShifts: Shift[]; wage: Money; minor: boolean; certifications: Array<{ skill: SkillId; expires: Date }> }>;
rules: LaborRuleSet;
policy: { overtimeApprovalRequired: boolean; maxLaborCostPct?: number; fairness: FairnessWeights; lockedShifts: ShiftId[] };
existing: Shift[]; // published shifts inside the horizon (locked unless consented)
seed: number; // determinism
};
type DemandForecast = {
location: LocationId;
granularityMin: 30;
series: Array<{ start: Instant; expected: number; p10: number; p90: number; drivers: string[] }>; // expected transactions or covers
staffingCurve: Array<{ station: StationId; perUnitDemand: number; minimum: number }>; // demand → required headcount per station
source: { model: string; version: string; trainedThrough: Date; inputs: string[] };
confidence: number; // calibration engine, not model-stated
};
type FairnessWeights = { hoursTargetGap: number; weekendSpread: number; closingSpread: number; preferenceHonored: number; consecutiveDays: number };
The demand forecast is produced by a separate service from POS history, reservations, weather, local events, and the location's hours. Its interface is the DemandForecast above; the solver treats it as data. When forecast confidence is below 0.5 for a window, the solver uses the location's minStaffing for that window and the schedule is annotated "forecast low-confidence; minimum staffing used."
Constraints¶
Hard constraints are never violated; the solver returns INFEASIBLE with the binding constraints named rather than relax them. Soft constraints carry weights in the objective.
| Constraint | Kind | Source | Notes |
|---|---|---|---|
| Person available for the whole shift | Hard | Availability | Exceptions override recurring |
| Required skills held, certifications unexpired | Hard | Station, Person | Expiring within horizon → warning, not violation |
| Keyholder on every open and close, if required | Hard | Location | |
| Minimum staffing per station per window | Hard | Location.minStaffing | Below this the location is "short," see health |
| Max hours per day and per week (legal) | Hard | LaborRuleSet | Minor-specific limits when minor |
| Minor hours windows and school-day limits | Hard | LaborRuleSet | School calendar per jurisdiction fixture |
| Minimum rest between shifts | Hard, or consent-gated where the rule allows | LaborRuleSet | Consent flow in swaps; premium recorded |
| Meal and rest breaks schedulable within the shift | Hard | LaborRuleSet | Break slots are emitted with the shift |
| Max consecutive days | Hard | LaborRuleSet | |
| Published shifts inside the notice window unchanged | Hard | predictive_notice | Changes only through consented swaps |
| No overlapping shifts across locations | Hard | Availability.locations | Shared staff |
| Overtime only if policy allows without approval | Hard when approvalRequired, else soft |
Policy | Overtime shifts are emitted as pending_approval |
| Person's own max hours | Hard | Availability | The person's cap is honored even below legal |
| Collective agreement constraints | Hard | LaborRuleSet | Compiled per agreement, e.g., seniority ordering for preferred shifts |
| Locked shifts | Hard | Policy | Lead-locked assignments |
| Demand coverage above minimum | Soft (high weight) | DemandForecast | Under-coverage cost per 30-minute slot |
| Labor cost within budget | Soft (high weight) | Policy, ledger | Overrun cost per unit |
| Hours target gap (person min/max targets) | Soft | Availability, fairness | |
| Weekend and closing spread | Soft | Fairness | Rolling 8-week fairness window |
| Preference honored (prefer/avoid) | Soft | Availability | |
| Consecutive days below fatigue threshold | Soft | Fairness | Below the legal max |
| Schedule stability vs last published | Soft | Existing | Penalize churn |
Objective¶
minimize w_cover · Σ_slots underCoverage(slot)
+ w_cost · max(0, laborCost − budget)
+ w_fair · Σ_people ( a·|hours − target| + b·weekendShare² + c·closingShare² + d·avoidedShifts )
+ w_pref · Σ_people preferMisses
+ w_churn · Σ_shifts changedFromPublished
+ w_ot · overtimeHours // when overtime is allowed but discouraged
subject to all hard constraints
Weights are tenant policy with defaults w_cover = 100, w_cost = 40, w_fair = 10, w_pref = 5, w_churn = 8, w_ot = 20. The owner can change weights in the direction editor; the solver refuses weights that zero out coverage or fairness (an IC protection floor, as in EC-ADV-007).
Output and explanation¶
type SolveResult = {
status: "FEASIBLE" | "INFEASIBLE" | "TIMEOUT";
shifts: Shift[]; // status = draft
breaks: Array<{ shift: ShiftId; start: Instant; durationMin: number; kind: "meal" | "rest" }>;
pendingApprovals: Array<{ shift: ShiftId; reason: "overtime" | "budget" | "consent_rest" }>;
metrics: { coverage: number; laborCost: Money; laborCostPct: number; fairness: Record<PersonId, { hours: number; weekends: number; closings: number }>; churn: number };
explanations: Array<{ shift: ShiftId; why: string[]; alternativesConsidered: number }>; // deterministic templates, not model prose
bindingConstraints?: string[]; // on INFEASIBLE
inputsHash: string; seed: number; solverVersion: string;
};
Explanations are templated from the solver's own trace ("Sam on Saturday close: Sam is available, holds bar and keyholder, is 4 hours under target this week, and has had 1 of the last 4 Saturday closes; Alex was considered and is at 38 hours"). The Chief Operating Orca may reword an explanation at the tone level but may not add reasons the trace does not contain.
Solve cadence and re-solve triggers¶
| Trigger | Horizon touched | Autonomy of the result |
|---|---|---|
| Weekly planning cadence (default Tuesday 10:00 local) | Next unpublished period | PROPOSE_CONFIRM to the location lead until the location's scheduling function graduates to AUTONOMOUS; then AUTONOMOUS publish with the lead notified |
| Availability change | Unpublished periods only | Same as above |
| Forecast update beyond ±15% for a window | Unpublished periods; published periods produce open-shift or swap proposals, never silent changes | AUTONOMOUS for unpublished; consented flows for published |
| Call-out or no-show | Single shift | AUTONOMOUS open-shift flow |
| Closure | Whole day(s) | PROPOSE_CONFIRM (EC-SCHED-006) |
| Labor rule set update | Everything unpublished; published shifts checked, violations flagged to the lead | PROPOSE_CONFIRM |
Re-solves are damped like re-plans (EC-PLAN-002): more than two forecast-driven re-solves in a week fold into the weekly cadence unless coverage would drop below minimum staffing.
Solver pseudocode¶
solve(req):
model = new CPModel(seed = req.seed)
vars = { assign[p][s] ∈ {0,1} for p in people, s in candidateShifts(req.demand, req.location) }
for each hard constraint in table: model.add(constraint(vars, req))
model.minimize(objective(vars, req))
result = model.solve(timeLimit = 60s)
if result == INFEASIBLE:
core = model.irreducibleInfeasibleSubset()
return { status: INFEASIBLE, bindingConstraints: describe(core) }
shifts = materialize(vars, breaks = scheduleBreaks(vars, req.rules))
return { status: FEASIBLE, shifts, explanations: explain(model.trace), metrics: score(shifts, req), inputsHash: hash(req) }
Shift lifecycle¶
stateDiagram-v2
[*] --> draft: solver output
draft --> published: lead confirms (PROPOSE_CONFIRM) or graduated publish (AUTONOMOUS)
draft --> [*]: discarded on re-solve
published --> swap_requested: assignee requests swap (AUTONOMOUS)
swap_requested --> covered: eligible taker accepts, validation passes (AUTONOMOUS) or lead approves premium (PROPOSE_CONFIRM)
swap_requested --> published: no taker; request expires; original assignee keeps it
published --> open: call-out (AUTONOMOUS) or no-show at T+15 (AUTONOMOUS)
open --> covered: taker accepts broadcast (AUTONOMOUS)
open --> short: no taker after 2 rounds; lead decides to run short (PROPOSE_CONFIRM)
covered --> published: taker withdraws before T-2h, original re-notified
published --> worked: clock-in and clock-out recorded
covered --> worked: clock-in and clock-out recorded
short --> worked: shift time passes (recorded as unstaffed)
published --> cancelled: closure confirmed (PROPOSE_CONFIRM) or lead cancels
covered --> cancelled: closure confirmed
worked --> reconciled: punches matched, breaks verified, premiums computed, exported
worked --> disputed: punch mismatch or missing punch
disputed --> reconciled: lead resolves (HUMAN_ONLY for pay-affecting edits)
reconciled --> [*]
cancelled --> reconciled: reporting-time pay computed if owed
Transition rules¶
| Transition | Actor | Autonomy | Validation | Notifications |
|---|---|---|---|---|
| draft → published | Location lead, or the harness after graduation | PROPOSE_CONFIRM / AUTONOMOUS |
Notice window respected; every hard constraint re-checked at publish | Every assignee by SMS: shifts for the period with a link; changes vs previous publish highlighted |
| published → swap_requested | Assignee | AUTONOMOUS |
Shift is more than 2 hours away | Eligible takers per the swap flow |
| swap_requested → covered | Taker, then validator | AUTONOMOUS unless premium or overtime → PROPOSE_CONFIRM to lead |
All hard constraints for the taker; overtime and rest rules on projected hours (EC-SCHED-003) | Both parties; lead if approval was needed |
| published → open (call-out) | Assignee | AUTONOMOUS |
None; a call-out is always accepted as stated | Lead; eligible takers via broadcast |
| published → open (no-show) | Harness | AUTONOMOUS |
No punch at T+15; one check-in SMS sent at T+15 before opening | Lead; broadcast |
| open → short | Location lead | PROPOSE_CONFIRM |
Two broadcast rounds exhausted | Lead's decision recorded with options shown |
| worked → reconciled | Harness | AUTONOMOUS |
Punch pairs match shift within tolerance (±15 min); breaks present; premiums computed by the rule engine | Payroll export; person sees their hours in the thread |
| worked → disputed | Harness | AUTONOMOUS flag; HUMAN_ONLY edit |
Any pay-affecting edit is made by the lead with a reason and is visible to the person | Person and lead |
| any → cancelled | Lead or closure flow | PROPOSE_CONFIRM |
Reporting-time pay computed if the rule applies | Every assignee by SMS, then voice if unacknowledged within 30 minutes and the shift is within 12 hours |
Every transition appends to Shift.history with actor and reason. A shift's history is visible to its assignee on the "what my manager knows" page.
Swaps and call-outs by SMS¶
All flows run in the person's SMS thread with the location's manager Chonk (the Chief Operating Orca by default; tenants can assign a different persona per location). Every inbound is matched against the keyword grammar first and a classifier second; ambiguous inbounds get one clarifying question and then a human. STOP and HELP are honored at the carrier layer before anything else.
Keyword grammar¶
| Inbound | Meaning | Notes |
|---|---|---|
SWAP or SWAP <date> |
Request a swap for the next shift, or the shift on that date | Date formats per locale; "SWAP SAT" resolves to the next Saturday |
SICK, OUT, CANT, CAN'T MAKE IT |
Call out of the next shift | Never asks why |
TAKE or TAKE <n> |
Accept an open or swap shift (nth in the last list) | |
PASS |
Decline the current offer | Also any silence past the offer window |
SCHED |
Send my shifts for the next 7 days | |
HOURS |
Send my hours this week and projected | |
LATE <min> |
Running late by n minutes | Lead informed; no-show timer extended |
HUMAN or LEAD |
Escalate to the location lead | Cannot be suppressed |
PLAIN / OTTER |
Set my tone ceiling | |
STOP / HELP |
Carrier keywords | STOP is permanent for that number |
Swap flow¶
sequenceDiagram
participant A as Assignee (SMS)
participant O as Orca (harness)
participant T as Eligible takers (SMS)
participant L as Location lead
A->>O: SWAP SAT
O->>A: Got it. Sat 14 Sep 16:00–23:00, Bar. Offering it to 4 people who are eligible. I'll tell you when someone takes it. You still own it until then.
O->>T: Open swap: Sat 14 Sep 16:00–23:00, Bar, Harbor. Reply TAKE to claim, PASS to skip. Offer closes 20:00 tonight.
T-->>O: TAKE
O->>O: validate(taker): skills, availability, rest, hours, minor, notice, cross-location overlap
alt passes, no premium
O->>T: It's yours. Sat 14 Sep 16:00–23:00, Bar. Added to your schedule.
O->>A: Jordan took Sat 16:00–23:00. You're off it. Schedule updated.
O->>L: FYI swap: Sat Bar 16:00–23:00, Sam → Jordan. No premium.
else passes with overtime or rest premium
O->>L: Swap needs your OK: Sat Bar 16:00–23:00, Sam → Jordan. Jordan would be at 42h (2h OT, est. $54). Reply YES or NO.
L-->>O: YES
O->>T: Approved. Sat 14 Sep 16:00–23:00, Bar is yours.
O->>A: Jordan took Sat 16:00–23:00.
else fails
O->>T: Can't give you this one: it would break the 8-hour rest rule after your Friday close. I'll offer the next one that fits.
end
Rules:
- The taker list is computed by the solver's validator: skills, availability, projected hours, rest, minor rules, notice-window consent, cross-location overlap, not on leave, not
STOPed. The list is ordered by fairness (most under target first), then by preference. - Offers go out in rounds of up to 4 people, each round open for 2 hours or until the shift is within 6 hours, whichever comes first, maximum 2 rounds (EC-SCHED-009). Off-hours rounds go only to people with
offHoursShiftAlerts = true. - The requester owns the shift until it is covered. The message says so every time.
- A swap is one-directional (give-away). Trades ("I'll take your Tuesday if you take my Saturday") are two linked swaps validated together and committed atomically.
- Names are shared between the two parties and the lead only; broadcast messages never say who is giving the shift away.
Call-out flow (including the 05:00 sick call)¶
sequenceDiagram
participant A as Opener (SMS)
participant O as Orca (harness)
participant T as Eligible keyholders (SMS)
participant L as Location lead (SMS + voice)
A->>O: sick
O->>A: Got it, you're off today's 06:00–14:00. Feel better. Nothing else needed from you. (05:02:40)
O->>O: shift → open; compute takers: keyholder skill, available, rest OK, hours OK, offHoursShiftAlerts = true
O->>T: Open shift now: today 06:00–14:00, Opener/keyholder, Harbor. Reply TAKE to claim. Closes 05:30.
alt taker by 05:30
T-->>O: TAKE
O->>T: It's yours. Today 06:00–14:00, keyholder. Code box is 4471. Thank you.
O->>L: Covered: today's open, Jordan for Sam. No OT.
else no taker by 05:30
O->>L: Today's 06:00 open is uncovered. Options: (1) Alex can take it at 2h OT, est. $54, reply 1; (2) open at 08:00 with you, reply 2; (3) something else, reply LEAD. I'll call you in 5 min if no reply.
L-->>O: 1
O->>T: (to Alex) Open shift approved for you: today 06:00–14:00, keyholder. OT approved by Riley.
O->>L: Done. Alex is opening. Logged.
end
Rules:
- Acknowledgement within 60 seconds, always, before any other processing. The acknowledgement never asks for a reason, a note, or an ETA to return (EC-SCHED-002). If tenant policy requires documentation after a threshold of absences, that request is a quiet-HR flow initiated by the lead,
HUMAN_ONLY. - The broadcast bypasses quiet hours only for
offHoursShiftAlerts = true. Everyone else is not messaged. - At T-30 minutes uncovered, the lead is messaged with numbered options that include the cost delta from the ledger. If the lead does not reply in 5 minutes, the voice line calls them. The lead decides. The manager never decides to run short, open late, or authorize overtime on its own.
- Overtime cover is
PROPOSE_CONFIRMto the lead in every case where the policy hasovertimeApprovalRequired = true, regardless of graduation. - The call-out is recorded factually on the shift history: "call_out, 05:02, by assignee." No pattern commentary is ever sent to the person; patterns are a lead-facing packet (EC-SCHED-005).
Open shift broadcast (from forecast or lead)¶
Same mechanics as the call-out broadcast, without the urgency: two rounds of up to 4 people, each open 4 hours, then the lead's options. Messages name the shift and the station, never the reason it is open.
No-show¶
T+15, no punch:
O→A: "Your 14:00 shift started 15 minutes ago and I don't see a clock-in. Are you okay? Reply LATE <minutes> or OUT."
no reply by T+25 → shift → open; broadcast; lead informed: "No-show at Bar 14:00, no reply to check-in. Broadcasting."
reply LATE 20 → lead informed; no-show timer extended; no broadcast
Failure handling¶
| Situation | Behaviour |
|---|---|
| Unverified number sends SWAP | Reply with verification only ("Reply with your first name and the last 4 of the phone number on file"); no schedule data until verified |
| Reply is ambiguous ("maybe," "idk") | One clarifying question with the keyword options; on a second ambiguous reply, the lead is informed and the flow pauses |
| Two people TAKE within the same second | First write wins at the harness; the second gets "Someone else got it first; I'll offer you the next one that fits" |
| Taker withdraws after acceptance and before T-2h | Shift returns to published with the original assignee re-notified; the withdrawal is recorded factually |
| Taker withdraws inside T-2h | Treated as a call-out by the taker |
| STOP received mid-flow | Flow aborts for that number; the lead is informed; the person's schedule stands |
| Carrier outage | Voice line calls with the same content; app push if installed; lead informed if any shift within 12 hours is affected |
Location-level health¶
Location health is computed hourly by the capacity model and the checklist engine and shown in the Owner Console with reasons, never only a color.
| Component | Signal | Green | Amber | Red |
|---|---|---|---|---|
| Coverage | Scheduled vs required headcount per slot, next 7 days | ≥ 100% all slots | Any slot 80–99% | Any slot < 80% or below minStaffing |
| Coverage risk | Open shifts and unanswered swaps within 72 hours | 0 | 1–2 | ≥ 3 or any within 12 hours |
| Labor cost | Actual + scheduled vs budget for the period | ≤ 100% | 100–105% | > 105% or unapproved overtime present |
| Compliance | Rule violations in the last 14 days (rest, breaks, minors, notice) | 0 | 1 flagged and resolved | Any unresolved or any minor-rule violation |
| Checklists | Completion of critical items on time, last 7 days | 100% | 1 miss, resolved | Any critical miss unresolved or any food-safety reading out of range |
| Equipment | Sensor thresholds and open service tickets | None open | Warn-level open | Critical open |
| Inventory | Items below par with lead time exceeding days of cover | None | 1–3 | > 3 or any critical SKU at zero |
| Punctuality and attendance | No-shows and late punches > 15 min, last 14 days, location-level rate | < 2% | 2–5% | > 5% |
| Staff signal | Reply rate to briefs and swap offers, tone downgrades, escalations to lead | Baseline | Reply rate down > 20% | Any escalation about the manager, or a safety event |
Rules:
- Attendance and punctuality are reported at the location level. Person-level attendance is visible to the lead in the reconciliation view and to the person in their thread, and appears nowhere else.
- Health reasons are templated from the signals ("Red: Friday 18:00–21:00 is at 60% of required Bar headcount; two open shifts unanswered").
- The weekly owner update rolls locations up with the same components; the portfolio view (OW-5) rolls tenants up.
Checklist and compliance cadences¶
Checklists are Cadence objects owned by the Chief Security Officer's charter, executed at the location by the assigned station or the lead, with evidence.
| Checklist | Cadence | Assignee | Evidence | Critical items (examples) | On miss |
|---|---|---|---|---|---|
| Opening | Daily at open − 30 min | Opener (keyholder) | Tap per item; photo for critical | Alarm disarmed and logged; walk-in cooler temp; hand-wash stations stocked; cash drawer count | Reminder at open − 10; lead informed at open + 15 if critical items incomplete |
| Closing | Daily at close | Closer (keyholder) | Tap; photo for critical; reading for temps | Equipment off; temps logged; cash reconciled; doors and alarm | Lead informed at close + 30; opener's brief next day includes the miss |
| Food safety (temperature log) | Every 4 hours during open, per unit | Station on duty | Reading | Each cooler and hot-hold within range | Out-of-range → immediate corrective-action prompt with the location's SOP; lead informed; second consecutive out-of-range → equipment ticket and HUMAN_ONLY disposal decision packet |
| Equipment | Weekly, and on sensor event | Lead or designated station | Tap; photo | Hood filter; fire suppression tag date; slicer guard | Service vendor chase (vendor cadence); lead informed |
| Cash and deposit | Daily at close | Closer | Reading (POS reconciliation) | Variance within tolerance | Variance beyond tolerance → lead packet; never a message to the closer implying blame |
| Custom (tenant SOPs) | Configured | Configured | Configured | Configured | Configured within the same escalation ladder |
Rules:
- A checklist reminder is a
brief-class message on SMS and app; it never goes to a public channel. - Misses are stated factually to the assignee once, then to the lead. No streaks, no leaderboards, no per-person completion rates in any group surface.
- Readings are validated against the item's range at entry; out-of-range readings prompt the SOP step and cannot be dismissed without a reason.
- Cadences never silently skip. A checklist that did not run because the location was closed is recorded as
skipped: closure; one that did not run for any other reason is a missed cadence and is escalated (reliability requirement). - Compliance reporting (the 14-day violation list, temperature logs, break records) is exportable per location for inspections, and the export is an audit event.
Inventory and equipment signals¶
Inventory and equipment are signals into the management layer, not a replacement for the inventory or maintenance system.
| Signal | Source | Threshold | Action | Autonomy |
|---|---|---|---|---|
| Item below par with days-of-cover < reorder lead time | POS depletion, counts | Per item | Reorder packet to the lead with quantity and vendor; if the vendor and amount are within policy, the Chief Fin Officer pre-approves | PROPOSE_CONFIRM (reorder), AUTONOMOUS (in-policy pre-approval) |
| Critical SKU at zero during open hours | POS | Immediate | Lead informed; menu or service impact noted in the shift brief | AUTONOMOUS notify |
| Count variance vs POS-implied on-hand > tolerance | Counts | Per item class | Lead packet; never a person-level accusation | AUTONOMOUS flag |
| Cooler temperature out of range for > durationMin | IoT sensor | Equipment thresholds | Corrective-action prompt to the station on duty; lead informed; equipment ticket; food-safety HUMAN_ONLY packet if the excursion exceeds the SOP limit |
AUTONOMOUS prompt and ticket; HUMAN_ONLY disposal |
| Sensor offline > 30 min | IoT | Lead informed; manual reading requested in the next temperature-log slot | AUTONOMOUS |
|
| Equipment failure reported (SMS "BROKEN |
Staff | Ticket; service vendor chase; lead informed; schedule impact assessed (e.g., station cannot open) | AUTONOMOUS ticket and chase; PROPOSE_CONFIRM any schedule change |
|
| Service vendor unresponsive | Vendor chase cadence | 3 attempts / 5 business days | Options packet to the lead (EC-BLOCK-003) | PROPOSE_CONFIRM |
| Fire suppression, gas, electrical, or structural fault | Sensor or staff report | Immediate | Safety escalation path (below) | HUMAN_ONLY |
Inventory and equipment facts carry observedAt and the staleness rules in EC-DATA-001; a count more than 7 days old is shown as stale in health.
Voice line for deskless staff¶
The voice line is a phone number per location that connects to the location's manager Chonk. It exists for people who do not have or will not use an app, and for moments when typing is not possible (driving, gloves, no signal for data).
Capabilities¶
| Caller says | The line does | Autonomy |
|---|---|---|
| "What's my schedule" | Reads the caller's next 7 days; offers SMS copy | AUTONOMOUS |
| "I'm sick" / "I can't come in" | Runs the call-out flow; confirms by SMS | AUTONOMOUS |
| "Can I swap Saturday" | Starts the swap flow; confirms by SMS | AUTONOMOUS |
| "I'm running late" | Records LATE; informs the lead |
AUTONOMOUS |
| "Take the open shift" | Runs the TAKE validation; confirms by SMS | AUTONOMOUS |
| "How many hours am I at" | Reads hours this week and projected | AUTONOMOUS |
| "I need Friday off" (future) | Records an availability exception; confirms by SMS | AUTONOMOUS |
| "Something is broken" | Records the equipment report; asks which unit | AUTONOMOUS |
| "I want to talk to a person" / "lead" / any frustration | Transfers to the lead; if unanswered, leaves the lead a page and tells the caller the SLA | Cannot be suppressed |
| Anything about pay rates, discipline, another person, HR policy beyond a document link | "That's one for [lead name]; I've let them know you called about it" | Routed; never answered by the line |
| Anything indicating danger or injury | Safety path | HUMAN_ONLY |
Rules¶
- Verification first. Caller ID matched to the number on file plus a spoken PIN, or a callback to the number on file. Nothing personal is read before verification (EC-ADV-006).
- Own data only. The line never speaks another person's schedule, hours, contact details, or location.
- Scripted plus constrained. Turns are scripted for the flows above; free-form Q&A is limited to the caller's own schedule and hours and to policy documents (read a paragraph, then SMS the link). The line does not persuade, negotiate, or discuss performance.
- Read-back. Every action is read back ("You're off Saturday 16:00 to 23:00; I'm offering it to four people. Right?") and confirmed by SMS after the call.
- Transcript. The call transcript is stored in the caller's thread, visible to them; the audio is not retained beyond transcription unless the tenant's retention policy says otherwise.
- Persona. The voice line caps at tone 2 by default. Level 3–4 synthesized character voices are an opt-in per tenant and never apply to the safety path.
- Languages. Per location, the line offers the languages the location's staff have on file; unsupported languages transfer to the lead.
Integrations¶
Atoms mode adds four integration families. Each has a system-of-record rule; Sea Suite is the system of record for nothing in this table except the schedule when the tenant has no scheduling platform.
| Family | Targets (v1) | Reads | Writes | System of record | Sync |
|---|---|---|---|---|---|
| POS | Square, Toast, Shopify POS | Transactions per 30-min slot, items sold, refunds, cash reconciliation, labor % where exposed | Nothing | POS for sales; Sea Suite derives demand history | Webhook plus 15-min poll; nightly reconciliation |
| Time clock | Deputy, When I Work, 7shifts, Homebase, standalone clocks, POS-integrated clocks | Punches, breaks, geofence flags | Shift schedule (for clock enforcement), approved hours after reconciliation | Time clock for punches; payroll for pay | Webhook; reconciliation at shift end and nightly |
| Scheduling platform | Deputy, When I Work, 7shifts, Homebase | Existing schedules, availability, roles, locations | Drafts and published schedules; swaps; open shifts | Configurable: the platform (Sea Suite pushes) or Sea Suite (platform mirrors); never both | Bidirectional with version vectors; conflicts below |
| IoT / sensors | Temperature probes, door sensors, equipment monitors via MQTT/HTTP | Readings, offline events | Nothing | Sensor platform | Streaming with 5-min heartbeat |
Conflict resolution¶
- Schedule. One system of record per tenant. If the platform is the record, a change made in the platform wins and Sea Suite re-validates it against rules, flagging violations to the lead rather than reverting (the harness never silently undoes a human's edit). If Sea Suite is the record, the platform is a mirror and its edits are rejected with a message to the editor.
- Availability. The person's most recent edit wins wherever it was made; both systems are updated; the person is shown the result.
- Punches. The time clock is authoritative. Sea Suite's reconciliation flags mismatches; edits happen in the time clock or through the lead's
HUMAN_ONLYreconciliation with a reason, and are written back. - Demand history. POS is authoritative; Sea Suite stores derived series with the source snapshot hash.
Failure modes¶
| Failure | Behaviour |
|---|---|
| POS outage | Forecast uses history; health shows "sales data stale since |
| Time clock outage | Shifts stay worked not reconciled; SMS "reply IN/OUT" fallback is offered only where the tenant enabled SMS punches; lead informed |
| Scheduling platform outage | Publishes queue; swaps and call-outs still run in Sea Suite and sync when the platform returns; the lead is told which shifts changed during the gap |
| Sensor offline | Manual reading requested; equipment health amber |
| Webhook gap | Backfill before acting (EC-DATA-004) |
Safety escalation path¶
Safety is HUMAN_ONLY in every deployment mode, at every graduation level, and for every persona. The manager's job in a safety event is containment and notification, never decision.
Triggers¶
- Any inbound (SMS, voice, app, checklist note) classified as injury, medical emergency, violence or threat, fire, gas, structural hazard, food-safety event with customer impact, or harassment.
- Sensor events at
criticalseverity for fire suppression, gas, or CO. - A staff report using
HELP-adjacent language on the voice line or SMS, or the keywordSAFETY. - Repeated impersonation attempts targeting one person on the voice line (EC-ADV-006).
The classifier is tuned for recall. False positives cost a page to the lead; false negatives are unacceptable.
Flow¶
flowchart TD
A["Trigger classified as safety"] --> B["Scripted reply to reporter: the jurisdiction's emergency number, then 'I have alerted Riley, the location lead, and their backup.'"]
B --> C["Page location lead: SMS + voice + app, bypassing quiet hours and caps"]
C --> D{"Lead acknowledges within 5 min?"}
D -- No --> E["Page backup lead, then owner; keep paging every 5 min until acknowledged"]
D -- Yes --> F["Lead is the human in charge. Manager stops all non-safety outreach to the location for 2 hours or until the lead clears it"]
E --> F
F --> G["Manager records: trigger time, who was paged, who acknowledged, when. Nothing else is summarized into memory."]
G --> H["Lead records the outcome and any incident report in the HRIS or safety system. Sea Suite links the record ID; it does not author the report."]
H --> I["Chief Security Officer charter review within 1 business day: was the path followed? Learn step updates classifier fixtures and the catalog."]
Rules¶
- The scripted reply is human-written, reviewed by counsel per jurisdiction, localized, and identical at every tone level. It contains the local emergency number, states who has been alerted, and nothing else. The persona does not appear.
- Paging bypasses everything: quiet hours, caps, leave (the backup is paged instead), incident mode, and channel preferences. Voice is always attempted.
- The manager does not triage. It does not ask the reporter questions beyond the script, does not assess severity, does not tell anyone to do anything except the lead ("please respond"), and does not contact emergency services itself; the script tells the reporter how to.
- The manager does not decide anything afterward. Whether the location closes, whether a person goes home, whether an incident report is filed, whether anyone is disciplined: all human. The manager prepares a packet only if the lead asks for one, and the packet contains facts from the systems (who was scheduled, sensor readings, checklist state), never a narrative of what happened.
- Data minimization. The event record holds timestamps and who was paged. The content of the report is retained in the thread under the tenant's retention policy and is excluded from people memory, feedback evidence, calibration, and health computation, except that the location's health shows "safety event this week" without detail.
- Escalation is a right. The reporter can reach the lead directly at any time with
LEADor by voice; the safety flow never replaces that path. - Test every path in the sim.
chowder-houseincludes a safety fixture per trigger type; the test asserts the scripted reply verbatim, paging order and timing, the outreach freeze, and the absence of any analysis in memory (EC-HUMAN-004).
Risk controls¶
Uncovered critical shift¶
- Detect. Open shift with
keyholderRequiredor aminStaffingbreach within 12 hours. Owner: Chief Operating Orca charter. Metric: critical shifts uncovered at T-30 per location per month. - Contain. Broadcast rounds; lead options packet at T-30; voice page.
- Correct. Lead decides; the solver re-solves the rest of the day.
- Learn. If the same slot goes uncovered twice in a month, the solver raises its coverage buffer for that slot and the owner sees a staffing-level signal.
Labor rule violation reaches a published schedule¶
- Detect. Publish-time validation or reconciliation finds a violation. Owner: Chief Security Officer charter. Metric: violations per 1,000 shifts; target 0 for minor rules.
- Contain. Freeze scheduling for the location to
PROPOSE; flag every affected shift to the lead. - Correct. Fix the rule set or the validator; re-validate the horizon.
- Learn. Add the rule fixture to the sim; catalog entry if novel; counsel review of the rule set version.
Food-safety excursion missed¶
- Detect. Out-of-range reading with no corrective action within the SOP window, or a sensor excursion without a prompt. Owner: Chief Security Officer charter. Metric: excursions without corrective action per month; target 0.
- Contain. Immediate prompt to the station and the lead; disposal decision packet (
HUMAN_ONLY). - Correct. Repair the cadence or sensor threshold.
- Learn. Adjust thresholds and add the excursion pattern to the sim.
SMS flow misfire¶
- Detect. A swap or call-out results in a wrong assignment, a duplicate cover, or a message to a
STOPed or unverified number. Owner: Chief Operating Orca charter. Metric: misfires per 1,000 flows. - Contain. Freeze SMS flows for the location to
PROPOSE; lead runs swaps manually with the packet. - Correct. Fix the grammar, the validator, or the race handling.
- Learn. Add the transcript to the sim as a fixture; catalog entry.
Open questions¶
- Which scheduling platform is the system of record by default when the tenant has one? Pushing to the platform preserves the tenant's existing workflows; making Sea Suite the record avoids two-way conflicts. The minimum viable Atoms deployment question in the PRD depends on this.
- Should the demand forecast be a Sea Suite service or a pluggable interface with the tenant's own forecast (some POS vendors ship one)? The interface in this document allows either.
- The 4-person, 2-round broadcast defaults are guesses. What are the right values for a 60-person location versus a 12-person one, and should rounds scale with eligible-pool size?
- Off-hours shift alerts require explicit consent. Should consent expire and be re-asked (e.g., every 6 months), given how easy it is to forget having opted in?
- Should the voice line support a spoken PIN at all, given shoulder-surfing on a floor, or only callback verification?
- Reporting-time pay and predictability premiums vary widely by jurisdiction. Should the labor-rule engine ship with a curated rule set per jurisdiction (with counsel review per release), or should every tenant configure from a template?
- Portfolio-level health for multi-location owners (OW-5) needs a normalization for locations of very different sizes. Which components normalize per headcount and which per revenue?