You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
From the 2026-08-23 DX audit. Generalizes #83, which is the same finding scoped
to tapped — this covers the mechanism everywhere it survives.
The problem
Module.build and Module.scoped gate unmet dependencies with a conditional
rest tuple, and an arity error carries no type:
error TS2554: Expected 5 arguments, but got 2.
Neither the UNSATISFIED DEPENDENCIES label nor the missing ports reach the
message. This is the worst diagnostic in the framework, and the prose knows it:
the root CLAUDE.md, packages/di/CLAUDE.md, docs/explanation/compile-time-wiring.md
and docs/reference/di/entry-points.md all carry paragraphs explaining what the
bare arity line actually means and how to hand-spell the phantom arguments to
make the port print. Four documents apologizing for one gate is the measure of
the cost. GHC treats custom type errors as first-class; the marker technique is
this repo's equivalent, and every OTHER gate already uses it.
The replacement is proven in-repo
#50's NeedsGate rides an intersection whose failure branch is an object with
one required property, and the diagnostic ends on the port (measured):
Property '"UNDECLARED NEEDS — name it in `needs`"' is missing in type
'{ provides: [...]; }' but required in type
'{ readonly "UNDECLARED NEEDS — name it in `needs`": Logger; }'.
The same shape now guards routerFor's scope gate (#90) and start's StartGate. The arity tuple predates all of them.
The work
Module.build(module) / Module.scoped(module, use): replace the ..._missing rest tuple with a marker intersected onto the module
parameter — unknown when Needs (minus Scope, for scoped) is never,
the named-property object otherwise.
Every @ts-expect-error — UNSATISFIED DEPENDENCIES arm in the type tests
re-measured; examples/order-application/src/needs-gate.test-d.ts pins the
new sentence.
The four apology paragraphs shrink to one line each, stating what now prints.
Risks worth measuring first
The rest tuple has one property the intersection must not lose: it fires on the
CALL, not on the argument, so --pretty's related-information span differs.
And Module<X, E, N>'s covariant _needs must still refuse the laundered
assignment — the marker sits beside that check, not instead of it. Both are a
scratch-file measurement before the sweep, per house rules.
From the 2026-08-23 DX audit. Generalizes #83, which is the same finding scoped
to
tapped— this covers the mechanism everywhere it survives.The problem
Module.buildandModule.scopedgate unmet dependencies with a conditionalrest tuple, and an arity error carries no type:
Neither the
UNSATISFIED DEPENDENCIESlabel nor the missing ports reach themessage. This is the worst diagnostic in the framework, and the prose knows it:
the root
CLAUDE.md,packages/di/CLAUDE.md,docs/explanation/compile-time-wiring.mdand
docs/reference/di/entry-points.mdall carry paragraphs explaining what thebare arity line actually means and how to hand-spell the phantom arguments to
make the port print. Four documents apologizing for one gate is the measure of
the cost. GHC treats custom type errors as first-class; the marker technique is
this repo's equivalent, and every OTHER gate already uses it.
The replacement is proven in-repo
#50's
NeedsGaterides an intersection whose failure branch is an object withone required property, and the diagnostic ends on the port (measured):
The same shape now guards
routerFor's scope gate (#90) andstart'sStartGate. The arity tuple predates all of them.The work
Module.build(module)/Module.scoped(module, use): replace the..._missingrest tuple with a marker intersected onto themoduleparameter —
unknownwhenNeeds(minusScope, forscoped) isnever,the named-property object otherwise.
tapped(tapped's gate is an arity error: the last rest tuple #83): same move, which closes that issue.@ts-expect-error — UNSATISFIED DEPENDENCIESarm in the type testsre-measured;
examples/order-application/src/needs-gate.test-d.tspins thenew sentence.
Risks worth measuring first
The rest tuple has one property the intersection must not lose: it fires on the
CALL, not on the argument, so
--pretty's related-information span differs.And
Module<X, E, N>'s covariant_needsmust still refuse the launderedassignment — the marker sits beside that check, not instead of it. Both are a
scratch-file measurement before the sweep, per house rules.