Home

Iteration

What I changed my mind about.

AgentUX has shipped every few days since March 2026, and the changelog records all of it. This is the edited version: the work I removed, the things that broke and why, and the one rule I never let move. Each source date comes from the product's own changelog, so any line here can be checked against its log.

Subtraction

What I removed.

Building the wrong thing is cheap to admit and expensive to undo. These are the ones I undid, including four features in a single deletion.

Four features, in one deletion

Flow Builder, Compare, Dev Mode, and the Figma integration all shipped, and all four came back out in a single release.

144 files removed, 28k lines trimmed.

27th April 2026

Grouped saved links by host, then ungrouped them the same day

Saved links shipped grouped into per-host sections laid out in two columns. The grouping came out the same day, replaced by a flat grid sorted newest first, with the hostname surfaced on each card so the context survived the change.

added noise and made link scanning harder

13th June 2026

Two search patterns became one

Mobile had its own inline expandable search row alongside the modal everywhere else. The mobile-only pattern was removed and the modal became the single way to search on every viewport.

one mental model for everyone

3rd April 202613th May 2026

Removed emoji from groups, all the way down

Group appearance briefly included an emoji. Taking it out meant the editor, the data layer, and the schema, not just the control: the icon_emoji column was dropped. A removal that stops at the UI leaves the cost behind.

7th to 8th April 20262nd to 4th May 2026

Cut a keyboard shortcut, then brought it back narrower

Command-K opened search until it was cut for duplicating the slash key. It returned three weeks later, scoped to searching within Videos, where it was not competing with anything. The second version was not a reversal of the first, it was the first one aimed properly.

redundant with /

13th May 202629th May 20266th June 2026

Replaced chip switchers with comboboxes, twice

Quick Upload and Bulk Change Group both used rows of chips to pick a group. Both became a single combobox within two days of each other. The same fix landing twice is usually a sign the pattern was wrong, not the screen.

wall of chips

28th April 202630th April 2026

Failure modes

What broke, and why.

Each of these has a cause, not just a fix. The one at the top is the reason I now distrust any check that reads a list in a single request. The last one is not a defect at all: it is a conclusion I had measured, argued for, and got wrong, corrected in the log the same day I published it.

A cleanup tool that could have deleted live screenshots

Storage cleanup compared the bucket against the list of files still in use. That list came back from a single request that silently capped its results, so every file past the cap looked orphaned and was offered up for deletion. The comparison now runs inside the database, where there is no cap, and the button reports what it found and deletes nothing until you confirm it.

it read the list of in-use files in a single request that came back capped, so anything past that cap looked unreferenced

28th July 2026

Three ways the back end was open

Three findings closed in one pass. An unset allowlist variable meant any Telegram user could upload. Row-level security was enabled, but its policy permitted everything. The screenshots bucket accepted public inserts and deletes. The webhook now verifies its secret before any write reaches the database.

RLS was technically on, but the policy said 'anyone can do anything'

13th to 14th May 2026

A fix that caused the next bug

Correcting which screenshots the Labelling Studio could see meant some clicks could no longer resolve to a screenshot family, so the lightbox stopped opening for them. Worth keeping in the record: the cause was the previous fix, not the original code.

fallout from the recent Studio scope fix

1st June 2026

Two people annotating at once, one surviving annotation

Annotations were stored as a single JSON array per screenshot, so two concurrent edits meant the second write replaced the first. Each pin and area became its own row, with the existing data migrated across. A concurrency problem solved in the data model rather than in the interface.

29th April 2026

The catalogue cache outlived the session

Caching the screenshot list on the device took a warm load from a full fetch down to around thirty milliseconds. It also meant the next person to sign in on a shared machine would have seen the previous account's catalogue. Signing out now clears it.

8th June 2026

A label edit that quietly erased someone else's

Setting a flow label wrote the entire metadata column, rebuilt from whatever the page had loaded when it opened. Anything written since, by the AI labeller or by another member, was replaced. Flow changes now merge against current server state instead. This is the same failure as the annotations one from April arriving on a different column: a whole-object write standing in for a field update, and no error either time to say that anything had been lost.

overwritten and gone with no error

15th August 2026

A new feature the cleanup tool had never heard of

Screenshots gained a small thumbnail for the grid. The orphan-storage sweep treats any file in the bucket that no database column names as abandoned, and nothing named the thumbnails, so every one of them was a day from deletion. The sweep knows about them now. The tool was doing exactly what it was built to do, which is the point: the cost of the new feature landed in a system nobody thought to go and look at.

Thumbnails would have been deleted a day after they were written

15th August 2026

Every request rebuilt a server that was never kept alive

The MCP transport is stateless, so nothing survives between calls. Each request was reconstructing all 19 tools and their 61 argument validators, using them once and discarding them. That cost was paid regardless of what the request actually asked for, and it put two of the three measured call types over Cloudflare's 10ms free-tier CPU ceiling. The validators now build once per worker. The last row below is the one doing the work: the interface an agent sees came out byte for byte identical.

use them once and throw them away
Measured on production, before and after. Cloudflare's free tier allows 10ms of CPU per request.
MeasurementBeforeAfter
Tool call returning a fixed string8.2ms3.0ms
Listing the tools10.2ms5.5ms
Three-image search26.2ms14.5ms
Fixed cost every request paysabout 8msabout 3ms
Tool surface an agent sees33,179 bytes33,179 bytes

13th August 2026

Measured it, cancelled the plan, shipped it anyway for a different reason

The plan was to stop inlining screenshots into every search reply, on the assumption that encoding them was expensive. Measurement disagreed: one inline image and three both came out at a 7ms median, because fetching an image is network wait rather than billed compute. The plan was dropped that morning, in writing. It shipped the same afternoon regardless, for a reason the measurement had never been about, which is that a default reply was 514,338 bytes of an agent's context window and returning text and ids instead made it 1,017. The entry that shipped it opens by withdrawing the number from the morning.

Honest correction to this morning's note: this change saves no measurable CPU

13th August 2026

The invariant

What never moved.

Six months of changing almost everything, with one rule that got an exception request each time a new surface opened, and never got the exception.

No AI label becomes reference material without a person verifying it

Four different routes opened for AI to write labels: pasted batches in the Studio, the AI review queue, agent submissions over MCP, and agents revising their own earlier work. Each one had a reason to make an exception, and none of them got one. Pasted batches are forced to Needs review so a label cannot quietly inherit a Verified status. Agents can pick up any draft, including one started by hand, but anything already verified is left alone and every revision returns to the queue. Speed was the argument for the feature every time. Trust was the argument against the exception, and it kept winning.

everything submitted still waits for human verification

6th to 7th May 202622nd July 202623rd July 202627th July 2026

Shipped with a stated gap, closed it the next day

The offline queue went out covering renames, grouping, and comments, and said in writing that crop was not included yet. Crop landed offline the following day, including the case where the same screenshot is cropped twice before reconnecting, where only the last one syncs.

Crop still requires being online, it's coming in the next release

10th June 202611th June 2026