Slipway

Availability and crewing for a retained fire crew, offline first, in daily use.

  • Self-directed
  • Sole-authored
  • Aug to Sep 26

The problem

A retained fire crew is on call from home and books itself off duty through a council system nobody can read at a glance and nobody carries in a pocket. One question decides whether a callout is answerable, and it has to be answerable before the callout: who is available right now, and does what is available still constitute a crew. A driver and no breathing apparatus is not a crew. Neither is four people who are all off next week.

The constraints

  • No signal. Not as an edge case: the station and most of the ground around it have none, so offline is the normal case and the network is the exception.
  • Not the official system, and it must never become a second source of truth. The council's own system remains the record of availability, and the app says so on screen.
  • A shared availability calendar is one design decision away from a surveillance tool pointed at colleagues. Four capabilities are specified as forbidden rather than deferred, and none of them will be built.
  • Adoption is the risk, not engineering. A tool a crew will not open is worth less than the paper roster it replaced.

What I built

An installable progressive web app with the database on the device: SQLite in the browser, replicated to Postgres when there is signal, so booking on and off works with the phone in flight mode and reconciles later. Supabase behind it, thirteen tables under row-level security with the authorisation rules tested in pgTAP rather than asserted, passwordless sign-in, and deployment to Cloudflare under cross-origin isolation.

The crewing calculus is the part that earns its keep: availability windows against a week-on-week-off rota, shortage warnings with hours of lead time rather than minutes, and a warning when the only person available to drive is about to book off. It lives in a domain package with no input or output of its own, so it is exhaustively testable and can be reasoned about without a database in front of you.

The measured outcome

Thirteen days from the prototype the Station Officer reviewed on 23 August 2026 to 1.0.0 on 5 September 2026, the release the station installed, and in daily use since. The repository itself starts on 29 July 2026, so first commit to that release is 38 days: the shorter figure is the one a crew would recognise as the product arriving, and both are here so neither has to be taken on trust. Every count that follows is taken at 1.0.0, the release the thirteen days end at, so it holds still while the repository does not. 121 commits, all mine. 48 released versions, counted from the changelog rather than from tags. 1,439 tests, all passing, across 79 test files, with a pgTAP suite beside them covering the authorisation rules. 503 files, 62,035 lines, measured with scc as code lines with blanks and comments excluded. Sixty-three architecture decision records, because the reasons are the part that goes stale first.

What was handed over

Two changelogs, kept in step by a check that fails the build when they drift: one for me and one written in plain words for the crew, because a release note nobody can read is not a release note. An install procedure somebody can carry to the station on a Tuesday night and work through handset by handset. And a specification that names what will never be built, so the next person to touch it inherits the constraint rather than rediscovering it.

Read the method behind it