CI / Integration + conformance (Node 22) (push) Failing after 17m12s
The single-flight gate queued its follow-up as
`leader.catch().then(() => this.flush())`. That waiter is settled ONLY by
resolving the very promise the leader is being awaited through, so the moment
anything inside a flush body awaits flush(), the promise graph closes on itself
and nobody resolves — an unbounded hang, not a slow flush, presenting exactly
like a bulk write timing out. No current call site awaits a flush from inside
one, so this is a latent cycle rather than an observed one; the gate should not
depend on that staying true.
The queue is now a bare deferred. The leader's finally opens the gate and
PROMOTES the waiter to a new leader, settling the deferred from that run; the
finally returns nothing, so the leader never awaits its own follower. Every
exit runs the same promotion — the leader resolving, the leader rejecting, the
promoted run rejecting — so a queued caller is settled exactly once on every
path, and a synchronous failure starting the promoted run is reported to the
waiter instead of thrown into the leader's finally. close() drains both handles.
tests/unit/brainy/flush-single-flight.test.ts pins the invariant on each path
that must settle a waiter: many callers during one flush all resolve within a
bound (one body, one follow-up, peak concurrency 1); a REJECTING leader still
runs and settles the queued waiter; a rejecting follow-up settles its waiter
and leaves the gate open; and the leader returns without waiting for a
deliberately slower follower.
CI / Integration + conformance (Node 22) (push) Waiting to run
CI / Bun (latest) (push) Waiting to run
Delta Gate / Delta gate — candidate vs control (push) Waiting to run
Four pins in real child processes under real SIGTERM, following the
writer-lock-clean-close spawn pattern:
(a) A host owner registered on SIGTERM closes two brains while the engine's
hooks are live: exactly one close entered and one close body run per brain,
the writer lock given up exactly ONCE per brain, the handler announcing
that it stepped aside, no "Writer fence lost", no failed instance, both
durability markers written, exit 0, and both reopens adopting rather than
folding. The release count is the discriminating assertion — against the
old handler it reads {a: 2, b: 2}, one release from the owner's close and
one from the handler's own finally.
(b) No host owner: the engine's handler closes every instance by the same
path — one close each, markers written, clean exit, clean reopen.
(c) Two concurrent close() callers share one promise (by identity) and one
execution; a third call after they settle runs nothing.
(d) Eight kicks during a running flush — five through the cadence door, three
direct — arm exactly ONE follow-up: two flush bodies total, and the
concurrency high-water mark stays at 1.
The counts come out of the child through a file written synchronously on the
way out: the engine calls process.exit(0) when it is the sole shutdown owner,
and a console.log to a pipe can be dropped by that exit.