From a DX review of the constraints this repo holds deliberately.
The compile-time gates are the product. Their error messages are the UI, and the UI has never been reviewed.
Measured, during PR #49. The composing form's uncovered-key gate was documented in eight places — three CLAUDE.md files among them, labelled "measured, not stylistic" — as naming the missing key. It does not, reliably. Stripping the @ts-expect-error and reading the raw diagnostic:
Type 'Provider<PortInstance<"AmqpHandler:left", …>>' is not assignable to type '"UNCOVERED HANDLERS"'.
The key never appears. It appears only when the array's length happens to match the marker tuple's, or in TypeScript's overload-list rendering — which is where it was read from, and why the claim propagated wrongly through the docs. The gate was correct the whole time; nobody could tell what it was saying.
The same character shows up across the repo's other gates:
start's phantom rest tuples — ["UNSATISFIED DEPENDENCIES", missing], NO RUNTIME, UNSATISFIED RUNTIME NEEDS.
- di's unmet-need channel, which surfaces as
Type 'Env | Logger | Scope' is not assignable to type 'Env | Scope' — accurate, and it takes a moment to see that the message means "you forgot to provide Logger".
- The keyed
HttpRouter(contract)(controllers) gates.
- Anything involving a contract type, where the expansion runs to several hundred characters of Zod internals before reaching the part that matters.
None of these is a correctness bug. All of them cost a reader time at the exact moment they are already confused, which is the worst moment to spend it.
Worth exploring rather than assuming a fix: shorter named aliases so the printer has something to display instead of an expansion; & {} and branded-alias tricks that force TypeScript to keep a name; ordering overloads so the useful failure is the last one reported (already load-bearing in both worker starters, and the reason the composer sits last in the intersection); and putting the human sentence in the first line of the type rather than the last.
Acceptance
- A pass over the repo's compile-time gates that treats the diagnostic as the deliverable, with the raw
tsc output recorded before and after for each.
- Every documented claim about what a diagnostic says is verified by stripping the directive and reading the real output — the practice that caught the original defect.
- Where a diagnostic cannot be improved, the docs say what it actually prints rather than what it ought to.
From a DX review of the constraints this repo holds deliberately.
The compile-time gates are the product. Their error messages are the UI, and the UI has never been reviewed.
Measured, during PR #49. The composing form's uncovered-key gate was documented in eight places — three
CLAUDE.mdfiles among them, labelled "measured, not stylistic" — as naming the missing key. It does not, reliably. Stripping the@ts-expect-errorand reading the raw diagnostic:The key never appears. It appears only when the array's length happens to match the marker tuple's, or in TypeScript's overload-list rendering — which is where it was read from, and why the claim propagated wrongly through the docs. The gate was correct the whole time; nobody could tell what it was saying.
The same character shows up across the repo's other gates:
start's phantom rest tuples —["UNSATISFIED DEPENDENCIES", missing],NO RUNTIME,UNSATISFIED RUNTIME NEEDS.Type 'Env | Logger | Scope' is not assignable to type 'Env | Scope'— accurate, and it takes a moment to see that the message means "you forgot to provideLogger".HttpRouter(contract)(controllers)gates.None of these is a correctness bug. All of them cost a reader time at the exact moment they are already confused, which is the worst moment to spend it.
Worth exploring rather than assuming a fix: shorter named aliases so the printer has something to display instead of an expansion;
& {}and branded-alias tricks that force TypeScript to keep a name; ordering overloads so the useful failure is the last one reported (already load-bearing in both worker starters, and the reason the composer sits last in the intersection); and putting the human sentence in the first line of the type rather than the last.Acceptance
tscoutput recorded before and after for each.