I build and operate a four-component real-time platform — a Discord bot, a Next.js web app, a zero-dependency log-parsing agent, and an auto-updating Electron desktop client — as one monorepo, solo, for a live user base that depends on it during scheduled four-hour events three nights a week.
It started as a single Discord bot with a respawn timer. It is now a platform: telemetry flows from software running on end-user machines, through an authenticated HTTP ingest layer, into Postgres, and back out to a Discord bot, a public web app, and a set of always-on-top desktop overlays that update sub-second while a user is mid-event. Every component ships on its own version, to its own target, with its own rollback story.
It is public, and it is checkable: wolfpack.quest is the platform this page describes.
The interesting constraint is not scale — it is that the users are all present simultaneously, on a fixed schedule, and a bad deploy is visible to everyone at once. That single fact drove most of the engineering below: a deploy freeze during live windows, load-shedding controls that need no deploy, per-uploader admission budgets, kill switches, and crash-loop auto-rollback on the desktop client.
Multiple independent clients observe the same event and upload overlapping, partially contradictory records. Reconciling them into one authoritative row — without double-counting and without losing the observer who saw the most — is the core data problem the platform solves.
Bearer-authenticated HTTP ingest across ~20 endpoints plus ~83 user-facing commands. Handles deduplication, merge-on-conflict, background schedulers, and timezone-aware nightly jobs. Discord doubles as a durable store: state is reconstructed from posted messages on boot, because the host filesystem is ephemeral.
Tails append-only logs that grow into the gigabytes, filters at byte level before parsing so private content never leaves the machine, and ships batches through a durable on-disk queue with exponential backoff. Serves its own localhost dashboard. Runs on machines I cannot access, debug, or update on demand.
Bundles the agent and its own runtime. Frameless, transparent, click-through overlays composited over a fullscreen application, driven by a named-pipe bridge to a third-party game modification. Three release channels, delta auto-updates, and automatic rollback to last-known-good after a crash loop.
OAuth sign-in with a two-stage authorization gate, role-gated admin surfaces, server actions with optimistic UI, and a public marketing/story front end. Row-level security in Postgres so the browser client can never read what the viewer is not entitled to.
The public stats page issued nine count() queries plus three full scans that
pulled 200k rows into the app to deduplicate in JavaScript. Folding it into a single
database function of exact counts was still 32 seconds, because exact counts are
full scans on million-row tables.
The fix was changing what was asked, not how: planner row estimates for display-only counts where the last three digits are worthless, and reading headline aggregates from the small authoritative table instead of the large derived one.
Time-series snapshots were supposed to bind to the encounter they belonged to. Only 96 of 3,651 did. Two independent causes, both invisible because the query returned rows either way: the read side compared identifiers in two different formats, and the write side only ever claimed the submitting client's own rows, inside a window narrower than the clock skew between machines.
I measured the actual skew across real client machines — 22 to 56 seconds, with one drifting past 67 — and sized the matching window from the measurement instead of the guess.
Users author matching rules in a small DSL with placeholder tokens. A token intended to capture a name compiled to a non-greedy wildcard, which happily matched the timestamp prefix instead — so rules looked correct, were enabled, and silently never fired. An anchor-rewriting pass now makes user-written anchors mean what users think they mean.
It shipped through ten releases undetected because the branch it lived on carried 35 test files while the main branch carried 90. Re-syncing that gap is now part of the release checklist — the defect was a process failure that happened to express itself as a regex bug.
A feature needed the exact text a server emits on a rare event. The obvious answer — from documentation, forums, and an AI assistant — supplied two strings. One was real. The other described an event the server never emits at all, and had already been implemented once from a guess, sitting enabled and matching nothing for weeks.
Reading the server's actual source settled it in minutes and produced three facts no secondary source had: the message is name-form even for the affected user, it is range-limited so distant clients never see it, and the failure case is silent by design.
Per-stream load shedding, per-client admission budgets, and a fleet-wide pause are all configuration read on a 60-second cache. Durable streams are structurally exempt — the code refuses to shed them even if the flag is set, so nobody can switch off the data collection by mistake.
Pushing to production restarts everything users depend on, so deploys are frozen during live windows and enforced by CI. Desktop updates are pull-based and therefore always safe; server changes wait.
Request timeouts and a consecutive-failure circuit breaker around the database, a durable client-side queue so an outage costs latency rather than data, and post-acknowledgement deferral of anything presentational so clients never wait on a third-party API.
Every judgement call lands in a dated decision log with the reasoning and where it took effect. Work happens across machines that cannot share context, so a decision that lives only in someone's head is already lost.
This is AI-assisted development run at a deliberate cadence, and the interesting part is not the volume — it is what had to be true for the volume to be safe. Work happens in short, discontinuous sessions that cannot share context with each other, so the repository carries the memory instead of my head: a dated decision log, a feature-to-file index read before anyone concludes something does not exist, and a status ledger of what is done, blocked, or deliberately abandoned. A session can end mid-thought without losing the thread, which is the whole reason this compounds instead of stalling.
Speed with no gate is just faster breakage, so the gates came first: 86 test suites, golden-file regression over recorded real-world input, a dedicated checker for a template that had shipped a blank-page bug twice, and continuous integration on every branch that ships to a user — after a release once slipped through on a branch that quietly had 55 fewer test files than the one it was cut from.
The same skepticism applies to the assistance itself. Generated answers are treated as hypotheses: the case above — where documentation, forums and an assistant all supplied a string the server never emits — is exactly why the habit is to verify against source, schema, or a live measurement before shipping. Knowing which claims must be checked is the skill that makes the rest of it usable.
Operationally it is built to run without a person watching it. Releases are automated per channel, client updates are pull-based, and the runtime controls — load shedding, per-client budgets, a fleet-wide pause — are configuration read on a short cache rather than a deploy. When something needs attention during a live window, the response is a settings change, not an emergency.
The full working method — memory as files, two-tier retrieval, the audits that paid off →
Roles, dates and history live on LinkedIn rather than here. This page argues from the work itself, which is the part that can be checked.