Sleep through the night.

Lullops is an AI on-call engineer. When the 3am alert fires, it reads the logs and the last few deploys, finds the likely cause and opens the fix as a draft pull request. It wakes a person only for calls that need judgment.

14 days free. Read-only until you say otherwise.

Replay: Lullops handles a 3am checkout outage

parcelryproductionINC-2291

Investigating Mitigated

03:14:06 Priya, on callAsleep

Investigation

P1 checkout-api Firing

5xx error rate above threshold

14.2%of checkout requests failing. Threshold is 2%.

Source
Alerting, rule checkout-5xx
Started
03:10:58
Affects
Web and app checkout

Paged first, per your escalation policy. Starting triage.

Logs checkout-api, last 15 min

2,418 lines read

  1. 03:10:52.018INFOPOST /v2/checkout 200 138ms cart=c_81fa
  2. 03:10:58.442INFOPOST /v2/checkout 200 121ms cart=c_81fe
  3. 03:11:02.301ERRORPOST /v2/checkout 500 TypeError: Cannot read properties of undefined (reading 'code')
  4. 03:11:02.302ERRORat formatPrice (src/pricing/format.ts:88:31)
  5. 03:11:04.877ERRORPOST /v2/checkout 500 TypeError: (reading 'code')
  6. 03:11:05.120WARNpayments-gw retry budget 40% used
  7. 03:11:09.614ERRORPOST /v2/checkout 500 TypeError: (reading 'code')
  8. 03:11:11.035INFOPOST /v2/checkout 200 144ms cart=c_8203
  9. 03:11:13.908ERRORPOST /v2/checkout 500 TypeError: (reading 'code')
  10. 03:11:15.270ERRORPOST /v2/checkout 500 TypeError: (reading 'code')

412 of 418 errors share one stack trace: formatPrice at format.ts:88. Every one is a cart saved before May 2.

Deploys checkout-api, last 24 h

3 deploys

  • #4471 Use the new currency helper in formatPrice maya.r, nightly release 03:09 Errors began 94 s later
  • #4470Retry wallet payments once on timeoutjonas.k, yesterday 18:21
  • #4469Bump the runtime to 22.9release-bot, yesterday 14:02

Traces agree: 97% of failing requests pass through formatPrice, which #4471 changed.

src/pricing/format.ts

+3 −1

84import { money } from "./money"
85
86export function formatPrice(cart: Cart) {
87 const currency = cart.currency
88− return money(cart.total, currency.code)
88+ // Carts saved before May 2 have no currency.
89+ const code = currency?.code ?? cart.store.defaultCurrency
90+ return money(cart.total, code)
91}
92
93export function formatTotal(order: Order) {

Test added and passing: “formats legacy carts without a currency”. Falls back the same way invoices already do.

Runbook action03:13:52

Rolled back checkout-api from #4471 to #4470

Allowed by rule 4: “Roll back a deploy under 30 minutes old if 5xx stays above 5%.”

5xx rate, was 14.2% 0.3%

Draft#4472

Guard legacy carts without a currency

  • +3 −1
  • 1 test added
  • 14 of 14 checks passed
  • Review: maya.r

Watching checkout-api for 15 minutes before I call it resolved.

incidents

14 members, 0 awake

Lullops is typing

LullopsApp3:14 AM

Checkout was failing for about 14% of customers from 03:10.

Cause: deploy #4471 reads currency.code, and carts saved before May 2 have no currency.

Done: rolled back to #4470 at 03:13 (runbook rule 4). Errors are back to 0.3%.

Next: the fix is in PR #4472 for @maya.r to review after coffee.

I didn't page anyone. Full timeline in INC-2291.

Status page updated: checkout degraded 03:10 to 03:13, now operational.

You slept through it.

Mitigated in 1 min 48 s. Nobody paged. The fix waits for review.

People paged 0Elapsed 2m 02s

Sample workspace: Parcelry, a made-up checkout team. Replays at about five times real speed.

Read it as text
  1. 03:12:04. An alert fires: checkout-api is failing 14.2% of requests, against a 2% threshold. Lullops is paged first, per the escalation policy.
  2. 03:12:09. It reads 2,418 log lines. 412 of the 418 errors share one stack trace in formatPrice, all from carts saved before May 2.
  3. 03:12:38. It lines the errors up with deploys. #4471, shipped in the 03:09 nightly release, changed formatPrice; errors began 94 seconds later. A trace of a failing checkout stops in formatPrice and never reaches payments.
  4. 03:13:05. It drafts a three-line fix that falls back to the store's currency, plus a test that passes.
  5. 03:13:52. Runbook rule 4 allows a rollback, so it rolls back to #4470. Errors drop from 14.2% to 0.3%. The fix opens as draft PR #4472 with 14 of 14 checks passing.
  6. 03:14:03. It posts the cause, the action and the next step in #incidents.
  7. 03:14:06. It decides nobody needs waking. You slept through it.

to

Priya slept through five alerts last night

This is her sleep tracker, with Lullops on the pager. Flip the switch to see the same night without it.

Show the night
Woken by alerts
0 times5 times
Awake for alerts
0 min2 h 24 min
Deep sleep
2 h 28 min1 h 22 min
An illustrative night for the engineer on call. Sleep stages from a made-up tracker; the shaded bands are time awake.
  1. Disk at 85% on worker-3

    Cleared 40 GB of stale build cache, per runbook step 2.Priya wakes up, clears the cache and lies awake until 00:15.

  2. Search health check flapping

    Matched a known flaky check, muted it for 2 hours and filed a ticket.Priya acknowledges it from bed and takes twenty minutes to fall back asleep.

  3. Checkout failing 14% of requests Replayed above

    Rolled back #4471 in under two minutes. The fix is waiting in PR #4472.Priya finds the bad deploy at 03:51 and rolls it back at 04:02.

  4. Email queue 9 minutes behind

    Scaled workers from 4 to 6, inside the limit in lullops.yml.Priya scales it by hand, then watches the graph until 04:55.

  5. TLS certificate expires in 9 days

    Not urgent, so it filed a ticket for the morning.A 5:52am page for something due next week.

When it does wake you, it brings the whole story.

If a call is outside your runbooks, or it touches customer data or money, Lullops pages one person. The page says what it found and which of two fixes it would pick.

  • It pages whoever is primary this week and nobody else.
  • Anyone in their quiet hours or on leave is skipped.
  • You can approve a fix from the lock screen, or ask it to call you.
Lullops now

Needs a human To Jonas K., primary on call

The primary database is 91% full and growing 2% an hour.

This is outside my runbooks, so I'm asking before I touch it.

Found
orders_archive grew 38 GB since Monday. Backfill job #812 is still writing to it.
Time left
About 4 hours at this rate.

APause backfill #812. Safe and reversible, and it buys about 20 hours. My pick

BAdd 200 GB of storage. No downtime, about $46 a month more.

Setup is one YAML file per service

You need read tokens for your alerting, logs and repo, plus your runbooks folder. Shadow mode starts on the next alert, and everything Lullops may do stays in a file you review like code.

  1. Connect, read-only

    Point it at your alerts, logs, traces, deploys and repos. It starts with read access to everything and write access to nothing.

  2. Hand over your runbooks

    Paste in the docs you already have, messy ones included. Lullops turns them into steps it can follow and flags the ones too vague to trust.

  3. Draw the lines

    Decide what it may do alone, like rolling back a deploy, and what always goes to a person. You set this per service.

  4. Shadow first, then the pager

    For a week it works every alert next to your on-call engineer, and you grade its calls. Change one word when you trust it.

lullops.yml

checkout-api

# What Lullops may do. Reviewed like code.
service: checkout-api
reads: [alerts, logs, traces, deploys, repo]
runbooks: ./runbooks/checkout.md
may:
  rollback: { age: "< 30m", when: "5xx > 5%" }
  restart:  { max: 2, per: hour }
  scale:    { replicas: "+50%" }
never: [merge_prs, primary_db, billing]
page_a_human_when:
  - confidence < 0.8
  - customer_data_at_risk
mode: shadow   # promote to: on_call

It works with the tools that page you now

Nothing runs on your servers. Lullops connects through the APIs your tools already have, and anything that sends a webhook or speaks OpenTelemetry works too. Here's each tool it touched during INC-2291.

INC-2291 through Parcelry's tools, 03:12:00 to 03:14:10

It listens to

  • Alerting checkout-5xx fired
  • Uptime checksnot needed tonight
  • Error trackingnot needed tonight

It reads

  • Metrics 5xx at 14.2%
  • Logs 2,418 lines
  • Deploys matched #4471
  • Traces 97% via formatPrice
  • Your code fix drafted in format.ts
  • Runbooksrule 4 allows a rollback
  • Feature flagsnot needed tonight

It acts in

  • Deploysrolled back to #4470
  • Pull requestsopened draft #4472
  • Team chatposted in #incidents
  • Status pagecheckout degraded 03:10 to 03:13
  • Pagingnobody paged
  • Ticketsnot needed tonight

What it's allowed to touch

Here's the permissions screen for checkout-api, as maya.r left it two days ago, and what the audit log kept from INC-2291.

Training
None. Your code, logs and traces never train shared models.
Hosting
US or EU, or inside your own cloud on the Scale plan.
Audits
SOC 2 Type II report and a yearly outside pen test, on request.
Sign-in
SSO on every plan.
checkout-api permissionsEdited by maya.r, 2 days ago
ActionSetting
Read logs, traces and metricsAllowed
Read code and deploy historyAllowed
Open draft pull requestsAllowed
Roll back a deploy under 30 min oldAllowed, logged
Restart pods, twice an hour at mostAllowed, logged
Change infrastructure configAsks a human
Merge pull requestsNever
Touch the primary databaseNever
audit.logINC-2291, exported as it was written
  1. readlogs, checkout-api, 2,418 lines
  2. readdeploys #4469 to #4471
  3. rollbackcheckout-api #4471 to #4470, rule 4
  4. open PR#4472 as a draft, review maya.r
  5. not allowedmerge #4472 (never: merge_prs)
  6. pagenobody, confidence 0.93 is over 0.8

The postmortem is drafted before standup

Every incident Lullops handles gets a write-up, with the timeline filled in from what it actually did. This is the one for last night's checkout outage.

docs / postmortems / INC-2291.md Draft

INC-2291: checkout failed for carts saved before May 2

Drafted by Lullops at 08:52 · P1 · 2 min 54 s of customer impact

What happened

From 03:10:58 to 03:13:52, about 14% of checkout requests failed with a 500. Nobody was paged. Lullops rolled back the deploy that caused it and opened a fix for review.

Timeline

  1. The nightly release ships #4471, which changes formatPrice.
  2. Checkout starts failing for carts saved before May 2.
  3. Alert checkout-5xx fires. Lullops is paged first.
  4. Lullops rolls back to #4470 under runbook rule 4. Errors fall to 0.3%.

Root cause

#4471 reads currency.code. Carts saved before May 2 have no currency, so the field is undefined and formatPrice throws. Invoices already fell back to the store's currency; checkout didn't.

Follow-ups

  • Review and merge PR #4472, the fallback to the store's currencymaya.r
  • Backfill a currency on legacy cartsjonas.k

Parcelry and its people are made up for this concept site.

Changelog

Releases go out on Tuesdays.

  1. v2.9

    Trace-aware triage

    Lullops now follows a failing request across services before it blames one.

  2. v2.8

    Runbook dry runs

    Test a runbook against last month's alerts before you let it act on tonight's.

  3. v2.7

    Quiet hours per person

    Pages wait for each person's own time zone, holidays and leave.

  4. v2.6

    Postmortem drafts

    A first draft lands in your docs by 9am, with the timeline already filled in.

Start a 14-day trial in shadow mode

Lullops works your next alerts next to your on-call engineer and shows its work. It stays read-only until you change that, and you won't need a card.

New workspaceShadow mode

This is a concept site, so the form never sends anything.