Back to blog
EngineeringOct 13, 2026· min read

When Research Writes the Design: Lists Phase 0

Most features start with a design and add research later if there's time. Lists board went the other way: 22,000 lines of research and executable models before a single route was written.

Unified Lists v1 shipped in September with drag-and-drop cards grouped by a flat theme string. It worked, but it felt thin. No nesting. No spatial memory. No way to organize your organization.

Phase 2 was supposed to fix that: nested groups, a spatial board, AI filing that actually works. But before writing a line of implementation code, we ran Phase 0—spikes, research, and executable models that would either prove the design or kill it.

This commit ships the result: 22,754 insertions documenting what we learned and what we're building.

Two Research Briefs, 1,800 Lines

Brief A was market and user behavior: what do people praise, what makes them abandon list apps, how deep do they actually nest? We analyzed outliners (Workflowy, Dynalist), task managers (Todoist, Microsoft To Do), spatial tools (Notion, Miro), and AI-native apps to find patterns.

The findings: users praise one-to-two layers of nesting. They complain about apps that force flat lists (Microsoft To Do's "no real subtasks" ranked fifth among most-hated behaviors). But nobody asked for infinite depth. The sweet spot is shallow structure with occasional nesting, not deep trees.

That research made the first design decision: depth cap of 5 levels, UI defaults to shallow, treat 5 as a ceiling nobody is asked to use.

Brief B was technical: which tree algorithm (ltree vs adjacency list vs closure table), which board library (tldraw vs @dnd-kit vs react-grid-layout), how to make drag-and-drop work on touch without breaking scroll.

The technical evaluation closed tldraw—it's the best whiteboard toolkit available, but production requires a paid annual key that's host-scoped (no public answer on whether one license covers Sabine and Poppin), stops rendering after a 30-day grace, and weighs 530 kB gzip. For a bounded grid with no rotation or connectors, plain pointer events beat the dependency.

Phase 0 Spikes: Prove It or Kill It

The design document specified a move function with per-user advisory locks, cycle detection, depth checks, and version compare-and-swap. Before writing that into production, we built it as a throwaway spike against scratch PostgreSQL and ran nine test suites.

The cycle race was the critical one: 200 trials, two sessions trying to move A under B and B under A at the same time. Result: 200 of 200 trials produced exactly one success and one CYCLE error. Zero loops persisted. Zero deadlocks.

Then we ran the same test with serialization switched off—no advisory lock, no up-front row locks. Loops persisted in every trial. The test is sensitive to the bug it's designed to catch, and the lock discipline actually works.

The stress test ran 8 concurrent movers, 6 renamers, and 4 two-row writers (deliberately creating deadlock opportunities) making 16,000 move calls across 60 nodes. Result: about 6,800 moves applied, refusals only for designed reasons (DEPTH_CAP, CYCLE, ANCHOR_NOT_FOUND), zero deadlocks, zero thread failures, invariants held.

The real-device touch test ran on an iPhone. Everything worked except auto-scroll, which was too fast—a 120 Hz screen scrolled twice as fast as 60 Hz because speed was per-frame, not per-second. The v2 test page fixed it: time-based speed ramping toward the edge with a 400 ms hold before scrolling starts. That finding is now a design requirement.

An Executable Model, Not Documentation

The undo model went further than spikes. It's 2,400 lines of SQL (schema, functions, grants) and 3,800 lines of tests covering move, delete, restore, rename, undo, redo, purge order, and role-based access. It runs against real PostgreSQL. It's not a prototype—it's the implementation, proven before a single production route calls it.

The model includes 70 mutants (remove the advisory lock, skip version checks, drop the cycle guard) and 500 fuzz runs to prove the tests catch what they're supposed to catch. A test suite that passes with the bug present isn't a test suite.

Phase 1a's implementation plan specifies porting the model, not rewriting it. The race suites and stress tests re-run against the port to prove nothing was lost in translation.

A 567-Line Design Document

The design document is dense. It specifies schema (workspaces, workspace_members, boards, list_groups, list_layout, list_activity, list_requests), data model rules (client-generated UUID v7, server-minted fractional-indexing keys, soft delete, one write path through apply_ops), and product behavior (suggest-first filing by default, auto-filing earned after five accepted suggestions with no correction).

It also specifies what not to build: realtime sync and sharing wait for Poppin's phase, item-level moves between lists are out of v1, connectors and infinite canvas stay on the roadmap but aren't this iteration.

The decisions table documents 24 design choices with dates and rationale: why the board is full-screen, why groups never delete their contents, why tldraw was closed, why Phase 1 splits into 1a (tree) and 1b (filing).

What's Next

Phase 1a builds the tree. That means migrating production's three lists from flat themes to top-level groups with an expand-then-contract rollback plan, shipping the activity log and undo system, rewriting the Organized view to show nested groups, building the Move-to picker (the non-drag path for keyboard and screen readers), and adding Recently Deleted and Archived views.

Phase 1b adds the Inbox tray and AI filing with suggest-first as the default. A group unlocks auto-filing after five accepted suggestions with no correction. Users can override per-group (never auto-file) or account-wide (always auto, always suggest, or off).

Phase 2 ships the compact tile, info/actions card, and tags. Phase 3 builds the spatial board—but only after passing a dogfood gate. If the prototype board isn't reopened unprompted on five separate days in two weeks, or if Inbox size grows because the board is open, Phases 3-4 shrink to Organized view plus saved views. The board stays on the roadmap; it just doesn't block other work.

The design-first artifact is Poppin's lists flow brief, written October 11 and awaiting Gate 1 review. Every lists screen will be built from Poppin's Coordinator components re-themed in Sabine's colors, brain-pack-aware from the start even though only Coordinator renders in Sabine today.

This is what research-driven design looks like: 22,000 lines of documentation, spikes, and executable models before the first implementation commit. Not because we love process, but because building the wrong thing confidently is worse than building the right thing slowly.