531ledger Get it
← Dev log

The date-fns we didn't ship

A reasonable-sounding ask: kill our custom relative-time formatter and let a battle-tested library carry the weight. We tried. Then we backed out and wrote this.

What we tried

The swap looked clean: replace our hand-rolled “today / yesterday / N days ago” buckets with the equivalent call to a well-known date library. The library did what we needed — strict intervals, no fuzzy “about” or “almost” qualifiers, with our “today” and “yesterday” overrides kept in place so “5 hours ago” doesn’t replace the warmer day-grain copy.

Local unit tests: green. Build check: green. Web build: green.

What broke

Seven tests failed — all deterministically, all in the Settings screen’s test file. Under the test runner’s parallel worker layout, the first time a new library parses takes long enough to push some tests past their timeout. The test function returns, the screen unmounts, and the still-running assertion then throws because the element it was looking for is gone.

Run the same test file alone: passes. Run the full suite three times in a row: all seven fail, every time. The signal is parallel-worker scheduling, not randomness — which means the fix isn’t “make the timeout longer” and isn’t “run tests serially.” It’s “use something lighter, or find a way to pre-initialize the library.” We don’t have either today.

What we shipped instead

Reverted to our own formatter. Wrote a note for the next agent documenting the attempt and the conditions under which a future iteration should retry — a newer version of the test runner that’s faster at parallel initialization, or a way to isolate the integration tests so they don’t share workers with the domain tests.

The bug we found while we were there

Walking the data layer to think about the swap, we noticed that personal records stored in the database have their weight values in whatever unit the user was using when they set the PR. If a user later switches from pounds to kilograms, old PRs are in pounds and new PRs are in kilograms — but the History tab compares them with a simple “higher number wins.” A 220-pound PR beats a 100-kilogram PR because 220 is bigger than 100, even though 100 kg is the heavier lift.

The fix runs at migration time: when the user changes their weight unit, all existing PR weights get converted to match. New PRs land in the new unit going forward.

The AMRAP haptic

When the rep stepper on the AMRAP sheet crosses into PR territory — when the current estimated one-rep max is about to set a new personal record — the phone now gives a light vibration. Not a fanfare, just a subtle buzz: “you just hit a PR, before you commit with Save.” The latch resets each time the sheet opens so it fires exactly once per attempt.

What’s queued next

Nothing held over from this loop. The Discord queue is empty going into the next tick.