From the 2026-08-23 DX audit, borrowing Rust's #[must_use]. The fix lands in
@unthrown/oxlint; this issue tracks the need from the consumer that documents
the hole.
The hazard, in this repo's own words
The root CLAUDE.md already states it:
An AsyncResult is eager: constructing it starts the work. So the
readable spelling of a sequence — each step in its own const, then chained —
is a race, and a silent one: it still type-checks and still returns a
Result, it just runs the steps concurrently. Nothing in the gate catches it,
which is the one place this repo's "mistakes are compile errors" thesis does
not hold.
Measured there too: the sibling spelling logs start:a start:b end:b end:a
where flatTap logs start:a end:a start:b end:b. Today the only guards are
convention (flatTap / DoAsync) and two example specs asserting ordering —
a runtime test in one workspace, for a hazard every workspace has.
The shape of the rule
Rust's compiler warns on any unused #[must_use] value, which is how a dropped
Result cannot be silent. The TS translation: flag an AsyncResult-typed
binding that is neither awaited, returned, nor consumed by a combinator before
a sibling AsyncResult is constructed in the same scope.
const a = repository.save(order); // work already started
const b = outbox.append(event); // ← flag: `a` unconsumed, `b` races it
return a.flatMap(() => b);
The blessed spellings stay untouched: flatTap, DoAsync().bind(...), an
immediate await, a single construction per statement.
Why upstream
unthrown is where the Result came from and @unthrown/oxlint is where its
other seven rules live; this repo dogfoods every one of them, so the rule lands
here the day it ships. A false-positive budget matters — prefer-ensure was
removed for flagging correct code — so the rule should start narrow: same-scope
sibling constructions only.
Acceptance
- A rule in
@unthrown/oxlint (name theirs to choose) that flags the sibling
spelling above.
- This repo's
.oxlintrc.json adopts it; the two ordering specs in
examples/order-temporal-worker stay as the runtime proof.
- The root
CLAUDE.md paragraph gets its ending changed: the thesis holds.
From the 2026-08-23 DX audit, borrowing Rust's
#[must_use]. The fix lands in@unthrown/oxlint; this issue tracks the need from the consumer that documentsthe hole.
The hazard, in this repo's own words
The root
CLAUDE.mdalready states it:Measured there too: the sibling spelling logs
start:a start:b end:b end:awhere
flatTaplogsstart:a end:a start:b end:b. Today the only guards areconvention (
flatTap/DoAsync) and two example specs asserting ordering —a runtime test in one workspace, for a hazard every workspace has.
The shape of the rule
Rust's compiler warns on any unused
#[must_use]value, which is how a droppedResultcannot be silent. The TS translation: flag anAsyncResult-typedbinding that is neither awaited, returned, nor consumed by a combinator before
a sibling
AsyncResultis constructed in the same scope.The blessed spellings stay untouched:
flatTap,DoAsync().bind(...), animmediate
await, a single construction per statement.Why upstream
unthrownis where the Result came from and@unthrown/oxlintis where itsother seven rules live; this repo dogfoods every one of them, so the rule lands
here the day it ships. A false-positive budget matters —
prefer-ensurewasremoved for flagging correct code — so the rule should start narrow: same-scope
sibling constructions only.
Acceptance
@unthrown/oxlint(name theirs to choose) that flags the siblingspelling above.
.oxlintrc.jsonadopts it; the two ordering specs inexamples/order-temporal-workerstay as the runtime proof.CLAUDE.mdparagraph gets its ending changed: the thesis holds.