ANTIGRAVITY LABJP
Articles/Integrations
Integrations/2026-07-13Advanced

Pour Your Unattended Run Logs into SQLite and Count Failures Across Every Run

Running Antigravity CLI on an unattended schedule leaves a growing pile of JSON Lines logs. Instead of grepping files, I pour them into a tiny SQLite database and answer 'which step fails, and when' with a single query. Here is the ingest script, six diagnostic queries, and six weeks of real numbers.

antigravity431sqliteobservability19logging3unattendedoperations24

Premium Article

An Antigravity CLI task I run unattended had been quietly doing nothing for three days before I noticed. Each run wrote its own JSON Lines log, and the logs/ directory held more than 200 files named by date and task. When I got suspicious and ran grep -l error logs/*.jsonl, all it told me was which files contained the word "error." Which step failed, how often, and at what time of day — to learn that, I still had to open the files one by one and read them with my eyes.

I had kept the logs. I just could not read them. The problem was on the reading side, not the storage side. When you have a few dozen runs, grep is enough. But once you are an indie developer handing chores across several apps to agents, runs climb into the hundreds per week. At that scale you do not need search — you need counting. I was slow to make that switch, and for a while I was melting thirty minutes each morning into reading logs.

Scattered JSON Lines gets harder to read as it grows

JSON Lines — one event per line — is a good format: append-friendly and hard to corrupt. But storing it as "one file per run" cannot answer questions that cut across runs. "Has the dependency-install step gotten flakier since last week?" "Do failures cluster in one time window?" You cannot even phrase those questions at the file level.

grep returns lines; it does not count. grep -c counts occurrences within a single file, but it cannot build a "failures ranked by step" table spanning 200 files. You can get close by stacking awk and sort, but you end up rewriting a one-liner every time the question changes, and it always ends as a throwaway.

What you want is a foundation where you can change the question as many times as you like. Structure each event once, gather them in one place, and then swap only the query to count from a different angle. For that foundation I chose SQLite, precisely because it means standing up no extra service.

The destination is one small SQLite file

"Observability platform" makes people picture an external analytics service, but the logs from runs an indie developer schedules mostly fit inside a single SQLite file. Six weeks, 214 runs, and roughly 19,000 events came out to about 18 MB. No server, no migration machinery — just sqlite3 to read and write.

Split it into two tables: runs, which holds the run-level facts (when, which task, success or failure), and steps, which holds the result of each step inside a run. Keeping the grains separate lets you count run-level and step-level failure rates independently.

TableKey columnsRole
runsrun_id / task / started_at / status / duration_msOne row per run. The anchor for aggregation
stepsrun_id / seq / step / status / duration_ms / errorEach step in a run. Used to pinpoint where it failed

That is the whole schema. Join the two tables on run_id, and add composite indexes on the task and status columns you will filter on most.

CREATE TABLE IF NOT EXISTS runs (
  run_id      TEXT PRIMARY KEY,
  task        TEXT NOT NULL,
  started_at  TEXT NOT NULL,      -- ISO8601 (UTC)
  status      TEXT NOT NULL,      -- success / failure
  duration_ms INTEGER
);
 
CREATE TABLE IF NOT EXISTS steps (
  run_id      TEXT NOT NULL,
  seq         INTEGER NOT NULL,
  step        TEXT NOT NULL,
  status      TEXT NOT NULL,
  duration_ms INTEGER,
  error       TEXT,
  PRIMARY KEY (run_id, seq)
);
 
CREATE INDEX IF NOT EXISTS idx_runs_task_status  ON runs (task, status);
CREATE INDEX IF NOT EXISTS idx_steps_step_status ON steps (step, status);

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
A 40-line shell ingest that folds scattered JSON Lines logs into two tables, runs and steps
Six diagnostic SQL queries that count which task, which step, and which hour fails — with six weeks of real results
Operating rules to grow the database safely: append-only, composite indexes, and a weekly VACUUM
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

Integrations2026-05-24
Catching "Running but Doing Nothing" Antigravity Subagents — A 3-Layer Observability Pattern Across Six Production Apps
Running Antigravity subagents across six production apps surfaced more than ten silent failures every month — exits clean, logs green, but nothing actually happened. Here is the Heartbeat / Output Trace / Decision Log pattern I now use to catch them inside 60 seconds, with code, GCP costs, and four months of running numbers.
Agents & Manager2026-06-17
Tracing Parallel Agents After the Fact: Observability with Structured Logs and Spans
Running multiple agents in parallel on the Antigravity 2.0 desktop makes it impossible to tell which one is doing what. I share an observability design that drops tangled print debugging for run_ids and spans you can trace afterward, with a solo-operator implementation and numbers.
Integrations2026-07-08
When Successful Automation Quietly Stops Earning Its Keep: Designing for Value Decay and Retirement
A dashboard full of green success logs often hides the most dangerous kind of failure. Here is a design — with working code — for surfacing automations that keep succeeding while producing no value, and deciding whether to retire, repair, or keep them.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →