Blog
2026
- how a tool call reaches a tab in the chrome you already useautopilot lets an ai agent drive the browser you are signed into, in the background, without ever bringing a tab forward. the four hops from an mcp client to a page, why each exists, and the design rule that pays for all of it.
- five things chrome does to a background tab, and what each one breaksan agent that never brings a tab forward runs into five undocumented chrome behaviors: a suspended worker that turns 100 ms into five seconds, an input dispatch that blocks for five seconds, a hidden tab that answers nothing until you tell it it is visible, a capture that never returns, and a stale module cache. each has a fix, and the largest one is a three-tier capture path.
- the click that reports success and does nothingthe most common failure in browser automation is a call that returns ok while the page never moved. every input in autopilot arms a watch before dispatch and reports what actually changed, and the contract found six bugs of its own.
- what i learned measuring my browser agent against anthropic'stwo bridges, one chrome, one signed-in profile, three runs each with the order alternated. mine was three to six times faster per call and 12x on screenshots, it was also the one that lost a tab to a debugger refusal, and the finding that mattered was a failure mode neither tool's docs mention.
- cancel is not enough: recheck at fire timesend an email only if the recipient has not read the message within n minutes, across multiple api pods. four mechanisms ruled out, one chosen, and the two properties that make it correct: a cancel path plus a fire-time recheck, and a dedup key that includes the triggering event.
- one port, three protocols, eight byteslechat is voice, video, screen share, and chat for a group of friends, hosted from one home machine with no accounts and no central service. the sfu hardcodes turns on port 443, so a web server and a turn relay share one tls certificate and one public port, split on the first eight bytes.
- why your migration reconciliation script agreed with the buga backfill that never converged because javascript truncates microseconds, a parity check that reported 100% over wrong rows because both sides truncated identically, and a not-over-nullable predicate that made 28 defects read as zero.
- three ways a dashboard can be correct and still liean availability tile red for client mistakes, a capacity ceiling drawn 3x too high, and a latency percentile blended with failures, each from a query that was correct. plus the monitor nobody writes: alert when nothing happens.
- why we polled for real-time chat, and what the poll costa since-cursor poll ships on infrastructure you already have. then you measure it: the poll was 22% of the backend's requests before a single real user, apm sampling hid that by 70x, and one client-only change captured 72% of the available saving.
- ai made this feature cheap to build, which is why we did not build it yetwhen an assistant makes a feature cheap to write, the expensive half (operating it, reviewing it, carrying it on call) still starts at ship time. the option to build later is worth more than the feature is today, and the published evidence on ai-assisted throughput and stability says why.
- the capacity plan was 100x too big, and the real bug was a timeout defaulta real-time prototype sized for 5,000 concurrent connections when production had about fifty, and the finding that mattered: a 30-second router dial timeout turning every rolling deploy into a 32-second stall for every client.
- the index that helps your read and taxes every writefour index decisions on one hot postgres table, each measured: a sort-key index that would have taken hot updates from 85% to 0%, an include column costing 18 buffers per write to save 9 per read, a partial index the planner silently skips under a generic plan, and a desc index that could never serve a nulls-last sort.
- buying a chat vendor as pure transport still makes you build a chat producta build-versus-buy for a real-time channel was three questions wearing one name. separating them settled two immediately, and the third turned on a structural finding: a chat-product vendor's access-control model forces you to mirror your membership state into their system even when you only want a signal.
- the llm feature had a prompt-injection hole before it shippedvoice notes for a crm: whisper transcribes, two providers draft the note, an eval tool scores them in production. the design detail worth writing down is the one code review caught: the client was sending the prompt.
- replacing a live feature with no flag daytwo years of phased cutover from a single-owner saved-listings feature to a shared workspace, with the old and new systems running side by side the whole time. the backfill ran in sixteen batches keyed by one hex character.
- why your orm cannot give you a global soft-delete filterprisma's extension hook cannot filter nested reads, manual filtering existed at 40% of the call sites that needed it, a federation architecture makes the uncoverable case coverable for free, and migrating orms to fix it costs ten times more than fixing it in place.
- name your feature flag for what happens when it cannot be reada client flag library falls back to its declared default whenever the flag service is unreachable, so an enable-flag defaulting to off hides the feature during every cold start and outage. the kill switch is a disable-flag defaulting to false, and the visibility rule is one pure function.
- building a link preview fetcher without becoming an ssrf vectorthe guard checklist for a server that fetches urls on behalf of untrusted users: address screening on every redirect hop, a dns wrapper that connects to the screened address, a byte cap that broke ordinary pages, and why verified-bot registration was declined.
- the sync echo that never happensscalidraw is excalidraw plus an encrypted sync layer. applying a remote update to a component that fires onchange on every mutation creates a feedback loop, and the guard against it is one ordering rule instead of version-vector bookkeeping: mark the update seen before applying it.
- the list header remount that broke contact searchthree react native crash classes root-caused from session replays: dangling apollo cache references after an eviction, no-cache fetch-policy races, and a flatlist header that remounted with the list and reset search state. fixing each class rather than each symptom cut the mobile crash rate about 60%.
- a dictation app with no windowdictate is 1,492 lines of rust: hold a key, speak, release, and the text lands where you were typing. it stays that small because the absence of a ui is the architecture, and four threads that each own one job replace the framework.
- designing authorization without a security teamsix authorization decisions on a multi-tenant social surface, made by the engineers building it: edit versus delete, uniform not-found, bounded arrays, a client-supplied content type that let a file render itself, write-side validation, and a presigned upload that can enforce size after all.
- the corruption came from the searchan unreproducible corruption bug in openplc editor, where a function block's name turned into raw html. the reported steps described the wrong trigger: the real one was search state that outlived the search, and a decorated display string that had become the element's identity.
- exactly-once lead assignment under concurrencya routing engine that gets an inbound prospect to an agent quickly, then round-robin on top of it: rule precedence, priority, skip logic, and a row lock over a persisted position marker so two leads arriving in the same instant cannot double-assign or drift the rotation.
- removing a network hop with graphql federationcross-service reads went through a legacy backend-for-frontend that made an http call per request to another service. federating the shared entity across subgraphs let both domains resolve it in-graph. one hop gone per request, cross-service p95 down about 30%, and two backends that stopped depending on each other's apis.
- the client already knows what just happenedan activity feed showed the user's own action about eight seconds late because it round-tripped through an event bus. the client already has the event it produced, so write it into the normalized cache and let the durable one reconcile when it arrives. zero backend changes, and a rollback difference in apollo worth knowing before you try it.
- the same stylesheet, shipped twiceastro's server output builds your site in two environments, and tailwind makes the two copies of one stylesheet different enough that content hashing cannot deduplicate them. the fix i sent upstream keys deduplication on source-module identity instead.
- a sync server that cannot read the datashoal syncs three of my local-first apps through one rust server that only ever stores ciphertext, so it has nowhere for app-specific logic to accumulate and has not changed as apps were added. the two hard parts were compaction and a stored head.
- the clipboard race every dictation app hasevery local dictation tool on windows pastes your previous clipboard sometimes. the fix is asking windows to tell you when the target has read the text, through delayed rendering, and even that hid a bug only real speech could find.
- rate limiting is two different problems wearing one namea dry complaint about a third duplicate guard led to a cross-repo sweep: about eight implementations, four mechanisms, one ignored shared library, zero observability. the fix was naming two tiers and assigning owners, not writing more code.
- the practices that outlived mepair programming as the squad norm, engineers testing their own work instead of a handoff stage, and a recurring team health check, introduced with no mandate from the vantage point of the code-review queue. the falsifiable claim is that the team kept all three after i moved on to other work.
- one token source, two themes, no dark prefixash lumen is a monochromatic design system compiled from dtcg token json into css custom properties, a tailwind preset, and a js module. both themes generate the same property names, so no component carries a dark variant, and three apps run the same pipeline instead of importing compiled css.
- the flashcard scheduler that keeps its own statemnemonic stores fsrs-5's full field set on the card row instead of a derived due date. that makes the scheduler auditable, lets the ui preview all four ratings before you commit, and surfaces exactly where the library's types disagree with what a truthful ui has to show.
- the streak that is not zero in the morninga habit tracker with no backend and no account. streaks are derived at read time, and the one decision that matters is that today is optional: an unmarked today continues yesterday's streak instead of resetting it to zero before you have had coffee.