Engineering notesView as Markdown ↗

Every record needs a visible bucket

A dashboard that sorts rows into named buckets with independent predicates will eventually drop a row into none of them. It stays in the database, it counts in the totals, and it renders nowhere. Treat the buckets as a partition, assert that their union equals what you fetched, and remember that an open obligation is not a window slice.

If a view sorts records into named buckets — today, upcoming, done — and each bucket is an independent predicate, then sooner or later a record satisfies none of them. It is still in the database. It is still counted in the header totals. It renders nowhere. Users call this "the job disappeared"; the logs show nothing wrong at all.

The remedy is to stop thinking of buckets as filters and start treating them as a partition: every fetched record lands in at least one, and a test asserts that union so the next status added to the enum cannot silently fall through.

How the gap opens

The buckets usually start honest. In one dispatch board they were:

unassigned  → no contractor and status = scheduled
today       → date = today and not finished and not cancelled
upcoming    → date > today and has a contractor
completed   → status in (completed, approved, invoiced, paid)
cancelled   → status in (cancelled, declined)

Read those five predicates against a job whose status is missed. It is not in the completed set and not in the cancelled set, so it is not terminal. Its date is in the past, so it fails today and fails upcoming. It is assigned, so it is not unassigned. No bucket. The same hole swallows an assigned job whose date has passed while it is still in a working state — precisely the two categories an operator most needs to see.

Nothing was broken in isolation. Each predicate is defensible; the set of them is not exhaustive. That is the failure mode: exhaustiveness is a property of the collection, and nobody owns the collection.

The cheapest detector you already have

The bug announced itself in a contradiction that was on screen the whole time: the summary strip said 41 records with 2 assigned, while the buckets contained 36 unique rows and the upcoming list was empty. Totals were computed over everything fetched; sections rendered only what bucketed. When those two disagree, the difference is the invisible set.

So make it an assertion rather than an observation:

const inBuckets = new Set(Object.values(view.buckets).flat().map(r => r.id));
expect(fetched.filter(r => !inBuckets.has(r.id))).toEqual([]);   // zero orphans
expect(view.stats.overdue).toBe(view.buckets.overdue.length);    // counts match rows

Seed the fixture with one record per status value, across a past date, today, and a future date. That is a dozen rows and it pins the partition permanently: add a status to the enum without giving it a home and the test names the orphan.

The second-order trap: an obligation is not a window slice

Adding the missing bucket — overdue: any non-terminal record dated before today, plus anything explicitly marked missed — made the tests pass. The deploy went green. Then the screen said Overdue: 0 while dozens genuinely were.

The reason is worth internalising. The view fetched a date window, and the window's default start is today. Overdue records are, by definition, before that start, so they were never fetched at all. The bucket was correct and empty.

Every test had passed because every fixture chose an explicit window that happened to contain the past dates. The product's default window never does. The fixture and the default configuration were different products.

The fix follows from naming the concept properly: overdue work is an open obligation, not a slice of the current period. It gets its own unbounded-below query, merged into the windowed result and de-duplicated by id:

// windowed rows for the period on screen, plus every open obligation, regardless of window
const overdueRows = await q.lt("scheduled_date", tenantToday)
                           .not("status", "in", TERMINAL_STATUSES);
const merged = dedupeById([...windowRows, ...overdueRows]);

Volume is self-limiting in practice: unresolved past work is a backlog the user is supposed to be clearing, and if it is large that is information, not noise.

Checklist

  • Enumerate the buckets against the status enum, not against the happy path.
  • Assert union(buckets) == fetched, and assert summary counts against rendered rows.
  • Ask of every bucket: is this a slice of a period, or a standing obligation? Obligations ignore the window.
  • Fixtures must use the product's default parameters at least once, not only convenient ones.
  • Render the urgent bucket first, and style it so an empty one is obviously empty rather than absent.

Related

Last updated 2026-07-30