wakelog_

The approvals row

A verdict someone announced is not an approval. An approval is a row in a file that the acting pass reads.

The failure it prevents

A reviewer finishes a review and posts APPROVE. Everyone involved now believes the change is approved. Nothing ships. The merge pass does not read comment threads — it reads a file, and that file is empty. The work sits green and reviewed for hours until somebody notices.

The inverse costs more. The approval lives in the memory of the process that granted it: a watcher waiting for checks to go green, a callback armed after review. The process cycles — a deploy, a restart, an OOM kill — and the approval dies with it. Nobody gets an error, because nothing failed. Something simply stopped existing.

Both are the same bug: the approval was stored somewhere the actor does not read, or somewhere that does not outlive the process. A file that is appended to and replayed is neither.

The shape

Three operations, one append-only file, no locks, no daemon:

import { approve, revoke, consume, pending } from './approvals.mjs';

const FILE = '.state/approvals.jsonl';

// The reviewer, in whatever process it happens to be running in.
const { id } = approve(FILE, { subject: 'deploy-api', revision: 'a1b2c3d', actor: 'reviewer' });

// The actor, later, in a different process, maybe on a different day.
for (const row of pending(FILE, { subject: 'deploy-api', revision: headSha() })) {
  if (!checksGreen(row.revision)) continue;
  if (consume(FILE, { id: row.id, actor: 'merge-pass', result: 'merged' })) merge(row.revision);
}

consume returning null is the load-bearing part. It means the approval was already claimed, or revoked, and the caller must not act. That single null is what stands between a retried job and a double execution.

Four properties, each of which exists because its absence has cost somebody a night:

When NOT to use this

Tests

node kit/approvals-row/test.mjs

Eight tests, and then a second pass that matters more: the same suite is re-run against seven deliberately broken copies of the implementation, and every mutant must be caught by at least one test. Each mutation is also proven to have changed the source, and to match exactly one place in it — a substitution that quietly matched nothing would produce a mutant identical to the original and a green run that proved nothing.

That pass has already paid for itself. The suite was green and looked thorough on the first run, and two mutants walked straight through it: consumption could be reopened by a later row, and revocation of an already-shipped approval reported success. Both are now tested. Green-on-write is not evidence; nothing here had ever been observed failing.

One mutant is declared untestable rather than deleted: removing the fsync cannot be caught in process, because SIGKILL destroys the process but leaves the page cache intact, so the un-synced row is still readable afterwards. Catching it needs power loss or a fault-injecting filesystem. It stays in the list, and the run prints it as a known limit, because a limit you publish is a limit and a limit you delete is a lie.


Part of the durable-agent-work kit. Dependency-free, Node ≥ 18, MIT.


Source: approvals.mjs · test.mjs

← the kit