Overview
v0.9.0 narrows tywrap to one job and does it well: generate typed TypeScript wrappers that move serializable values between TypeScript and Python. It ships as ten reviewable PRs and releases as a single breaking minor.
Three threads:
- Type accuracy — describe what the generated types actually cover, and emit
unknown for returns tywrap can't resolve (unannotated, *args/**kwargs, or third-party/out-of-module types) instead of a bare, undeclared name. Add a SECURITY.md and document the bridge trust model.
- Runtime validation — check a decoded Python return against its declared TypeScript type before the call resolves (by shape for Arrow/columnar data, so the fast path isn't affected), and pin the generated IR as a versioned, diffable contract.
- Remove the stateful instance API — generated class wrappers no longer hand back live object handles (
create() / callMethod / disposeHandle) backed by instantiate / call_method / dispose_instance. They expose only static and classmethod members that return serializable values.
Why
tywrap's strength is moving values: functions in, typed data out, with Apache Arrow for scientific payloads. The instance-handle API doesn't fit that model. A handle lives in one worker process, but the pool routes each call to any free worker, so with maxProcesses > 1 a method call can land on a worker that doesn't hold the handle and return the wrong result. Adding object lifetime, affinity, and cross-process GC is a much larger surface than the value path needs. Removing it keeps the worker pool simple and the behavior predictable. Stateful work moves into a single Python function that takes and returns values.
Breaking changes / semver
Pre-1.0, minors may break. v0.9.0 is openly breaking: generated output changes (more unknown, validators emitted, no handle members), the wire protocol narrows to call | meta, and IR_VERSION goes 0.3.0 → 0.4.0 (regenerate wrappers after upgrading). 1.0 stays distant.
Tracking
Landing order matters: server → client → generator for the removal, then the type and validation work.
Overview
v0.9.0 narrows tywrap to one job and does it well: generate typed TypeScript wrappers that move serializable values between TypeScript and Python. It ships as ten reviewable PRs and releases as a single breaking minor.
Three threads:
unknownfor returns tywrap can't resolve (unannotated,*args/**kwargs, or third-party/out-of-module types) instead of a bare, undeclared name. Add aSECURITY.mdand document the bridge trust model.create()/callMethod/disposeHandle) backed byinstantiate/call_method/dispose_instance. They expose only static and classmethod members that return serializable values.Why
tywrap's strength is moving values: functions in, typed data out, with Apache Arrow for scientific payloads. The instance-handle API doesn't fit that model. A handle lives in one worker process, but the pool routes each call to any free worker, so with
maxProcesses > 1a method call can land on a worker that doesn't hold the handle and return the wrong result. Adding object lifetime, affinity, and cross-process GC is a much larger surface than the value path needs. Removing it keeps the worker pool simple and the behavior predictable. Stateful work moves into a single Python function that takes and returns values.Breaking changes / semver
Pre-1.0, minors may break. v0.9.0 is openly breaking: generated output changes (more
unknown, validators emitted, no handle members), the wire protocol narrows tocall | meta, andIR_VERSIONgoes 0.3.0 → 0.4.0 (regenerate wrappers after upgrading). 1.0 stays distant.Tracking
Landing order matters: server → client → generator for the removal, then the type and validation work.
SECURITY.mdand document the bridge trust modelunknownfor return types tywrap can't resolve #267 — emitunknownfor return types tywrap can't resolve