The bot-specific problem
Reviewing a 30-file bot PR is impossible if you have to read every body to know what each function does. TypeScript signatures lie by omission: a function typed (x: string) => string can hit the network, mutate globals, and read env vars.
Proposal
Extend uses { net } so every effect, mutation, and external dependency is part of the signature. No escape hatches: any is banned, untyped imports are banned, hidden globals are banned. A reviewer can audit a function from its header alone.
fn loadUser(id: string)
uses { net, time }
reads { cache }
writes { metrics }
throws { HttpError, TimeoutError }
-> Result<User, string> { ... }
What the compiler does
- Effect inference pass; if the body uses an effect not declared, compile error
- Bans
any, as unknown, untyped imports, untyped require, globalThis access
- Effects are part of the type — a function expecting
(x) => Result<T> with no effects rejects a body that hits the net
Why it's killer for bots
- Reviewer reads headers, trusts the body. Scales to bot-volume PRs.
- Bots can't lie about side effects, even by accident.
Open questions
- How granular do effect kinds get?
net.http? net.dns? Or just net?
- Inferred vs. declared: does the compiler infer and require declaration, or just check the declaration?
- Interop with untyped npm packages — wrapper modules with declared effects?
The bot-specific problem
Reviewing a 30-file bot PR is impossible if you have to read every body to know what each function does. TypeScript signatures lie by omission: a function typed
(x: string) => stringcan hit the network, mutate globals, and read env vars.Proposal
Extend
uses { net }so every effect, mutation, and external dependency is part of the signature. No escape hatches:anyis banned, untyped imports are banned, hidden globals are banned. A reviewer can audit a function from its header alone.fn loadUser(id: string) uses { net, time } reads { cache } writes { metrics } throws { HttpError, TimeoutError } -> Result<User, string> { ... }What the compiler does
any,as unknown, untyped imports, untypedrequire,globalThisaccess(x) => Result<T>with no effects rejects a body that hits the netWhy it's killer for bots
Open questions
net.http?net.dns? Or justnet?