Skip to content

A sibling-const AsyncResult sequence races, and no rule catches it — unthrown needs must-use #92

Description

@btravers

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1Should land before 1.0 — real DX cost, or a decision blocking other workenhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions