Skip to content

Roadmap

Phases 0–3 are complete. Phase 5 (interop and developer experience) shipped in v0.1.6; Phase 5.5 (constructor / method arity safety) shipped in v0.2.0; Phase 6 (Python-annoyances surface — newtype, freeze let, pub, effect/safety diagnostics, cross-platform install) shipped in v0.3.0, with correctness sweeps and additive features layered in through v0.3.1, v0.4.0, v0.5.0, v0.5.1, v0.5.2, v0.6.0, v0.6.1, v0.7.0, v0.7.1, v0.8.0, v0.8.1, v0.9.0, v0.9.1, v0.9.2, v0.10.0, v0.11.0, v0.12.0, v0.13.0, v0.14.0, and v0.15.0. The current release is v1.0.0-beta.2, the first beta: four review-remediation waves on top of alpha.9 — checker soundness (field narrowings dropped wherever a call or write can reach them, loops joining their exit paths, match exhaustiveness over Result payloads, T?, bool, literal unions and nested sealed unions), lowering fixes (an inline ? keeps Python’s evaluation order, |> works in every expression position, an impl method may read a name bound after its class), VM ↔ CPython parity (C3 method resolution, CPython-ordered sets, an automatic CPython fallback for programs the VM does not model), and symlink-safe tooling. No new syntax; its new error-level diagnostics fire only on code that already crashed, [strictness] nullable-use now defaults to "error" as the documented exception, and the surface listed in the compatibility policy is frozen for the beta line. The prior v1.0.0-alpha.9 was a maintenance release with no language change: the warn-level tyc::contains_secret_literal keyword table grows from 16 entries to 55 (seven overlapping proposals consolidated into one longest-first-ordered table) plus one more name-word boundary — an uppercase keyword directly followed by a lowercase letter — alongside three compiler allocation reductions, the mid-August dependency wave, and docs-site keyboard-accessibility and reduced-motion polish. The alpha.5 → alpha.8 line before it: v1.0.0-alpha.5 was the performance release (VM Tier 1 — an allocation-light two-representation integer, method-dispatch caching, slot-resolved locals — plus the [optimise] profile / tyc build -O, seven advice-only perf lints, a free-threading parallelisation wave, and native PEP 810 lazy imports on 3.15 targets); v1.0.0-alpha.6 carried the July dependency wave safely across the toml 0.8 → 1.x major (fixing the venv-introspection allow-list regression that bump introduced before it shipped), added six secret-name keywords behind a single shared table, and re-pinned the release pipeline; v1.0.0-alpha.7 was the 2026-07-28 full-codebase-review remediation, closing all ten 1.0 blockers plus the Tier-0 gates that make them verifiable (type-checker and resolver soundness fixes, a shared-lexical-mask preprocessor rewrite, five emitter/preprocessor miscompilation fixes, VM ExceptionGroup / except* support, and a full-corpus VM ↔ CPython differential gate); and v1.0.0-alpha.8 fixed a VM ↔ CPython parity gap — an unseeded random now seeds from entropy under tyc run as CPython does, while explicit random.seed(n) still matches byte-for-byte — and widened the same secret-name lint to digit and TitleCase boundaries. The prior v1.0.0-alpha.4 was a focused hardening pass on top of alpha.3, closing the one HIGH finding the release-readiness review deferred (H5 — a locally declared class no longer unifies with a same-named foreign class of a provably different shape, evidence-gated so the example/stress corpus is byte-identically unchanged), restoring longest-first secret-name matching (APIKEY before KEY), finishing the release-engineering hygiene the alpha.3 review opened (SHA-pinned GitHub Actions, a pre-release-aware installer), and landing a round of dependency / advisory bumps (crossbeam-epoch RUSTSEC-2026-0204, regex, memchr, compact_str) — no new syntax, and, like the alpha.2 diagnostics, the H5 fix is a conservative narrowing that only rejects programs passing a provably-different-shaped class across a module boundary, so no previously-correct program changes behaviour. The prior v1.0.0-alpha.3 was a release-readiness remediation pass closing licensing / packaging gaps (a repository-root MIT LICENSE, the vendored Ruff notice, SECURITY.md / CONTRIBUTING.md / Dependabot, pre-release-tag + CI-gated release hygiene) alongside compiler / VM diagnostic-reporting and complexity fixes (identical errors at distinct locations are all reported, nested-generic assignability is linear again, four more flow-narrowing invalidation holes closed, six VM ↔ CPython parity gaps fixed) and tooling hardening (256 MiB LSP stack, atomic tyc fmt, TYC_NO_INTROSPECT kill-switch, Windows venv discovery, the wired-up [strictness] exhaustive-match knob) — no new syntax, and no previously-correct program changes behaviour. The prior v1.0.0-alpha.2 was the 2026-06-28 adversarial pre-release review remediation: a type-checker soundness sweep (non-local flow-narrowing invalidated across calls/aliases; short-circuit and/or narrowing fixed), more typed positions (slice reads, subscript assignments, tuple-unpack and match captures, walrus, parameter defaults), VM as! enforcement, and three conservative diagnostics (tyc::not_a_context_manager, tyc::raise_non_exception, tyc::frozen_inheritance_conflict) that reject only programs which already crashed at runtime — no new syntax, and no previously-correct program changes behaviour, though those diagnostics do narrow the accepted surface for already-runtime-crashing programs. The prior v1.0.0-alpha was Typhon’s first tagged alpha and first feature-complete milestone: the proven production surface plus the previously-deferred type-system frontier — HKT unification (constructor variables bind against concrete heads, with tyc::kind_mismatch on wrong arity / conflicting binding), user-generic variance inference (covariant / contravariant from usage, cross-module, with @covariant / @contravariant overrides), variance through generic interface bounds, the inter-procedural field-init audit, and 2-member non-nullable union modelling — rolling up the alpha plan’s M1 + M2 plus the rescue boundary sugar. The production path (tyc build → CPython 3.13+) is stable; as an alpha the surface syntax is not yet frozen and may change before 1.0.0 with a documented migration note, and embedded ty Phase 2 + typeshed pure-extension checking are deferred to beta. It follows the v0.15.2 → v0.15.7 robustness line (cross-module extend BUILTIN: propagation, cross-module structural-interface conformance, a ~198-program stress sweep, and third-party method-call arity checking). v0.15.1 was a compiler-performance + docs-site-accessibility point release (source-map generation O(N²)→O(N log N), a Result-exhaustiveness hot-path allocation removed, keyboard-focus ring + anchor-target highlight with a prefers-reduced-motion fallback), with no language or API changes. v0.15.0 sharpened Typhon at the library boundary: as! checked cast composes in any expression position, the try_result exception→Result combinator, compiler-bundled .dty stubs for httpx / requests, async_without_await understanding async contracts, and a qualified↔bare cross-module class-identity fix. v0.14.0 added the as! checked boundary cast (lowering to checked_cast in typhon_runtime/cast.py) and [emit] traceback-remap; v0.14.1–v0.14.3 completed cross-module shape propagation, added tyc::gather_opportunity advice + cross-module auto-gather, and made the LSP refresh diagnostics live on typhon.toml edits. v0.13.0 landed cross-module extend lowering, TypedDict-style dict-literal lowering, enum match exhaustiveness, the closed Result unwrap / query API, VM cooperative asyncio, recursive type aliases, and quoted-annotation forward references. The previous v0.12.0 delivered VM __lt__ parity, dict/str builtins, and deep library introspection. A VM-vs-CPython differential follow-up fixed sorted / min / max ignoring a user __lt__ and added the missing dict.popitem / dict.fromkeys / str.translate / str.maketrans builtins; venv signature introspection now captures parameter and return annotations, so a wrong-typed argument to a fully-typed third-party dependency — function or constructor — is caught at compile time via tyc::type_mismatch (an unintrospectable dependency now warns via [strictness] unintrospectable-dependency instead of silently skipping its checks), and Phase 1 of the typeshed-backed ty integration landed ([checker] external = "ty" / --with-ty on tyc build / tyc check). The previous v0.11.0 was the VM parity sweep + enum keyword. A fresh adversarial stress round against v0.10.0 surfaced 22 findings — almost entirely in the VM. This release closes every finding, lands the enum Name: keyword as a first-class declaration form (sugars over enum.Enum with enum.auto() for bare members), and adds two new VM value kinds: Value::Complex for native complex arithmetic (hashable for set / dict keys) and a dict-view kind backing dict.keys() / .values() / .items() (repr / iterate / in / len match CPython). Bare super() is rewritten by tyc-desugar to the two-arg form so @dataclass(slots=True) no longer crashes; __call__ dispatches on callable instances; __post_init__ fires after auto-generated construction; multi-level inheritance accumulates fields across the full MRO. New / expanded stdlib shims: native enum / datetime (naïve / UTC) / pathlib (/ join, .suffixes, .parts) / collections.defaultdict (factory invoked via subscript __missing__). Real re.Match capture groups, banker’s round, bytes methods, itertools.groupby(key=), str.split(maxsplit=), f-string {x=}, str % runtime formatting. VM value semantics align with CPython — dataclass eq / repr / hash is value-based (class-identity keyed, no cross-module collisions), set equality is order-independent, float repr matches CPython’s shortest round-tripping form. The type checker plugs three more coherence gaps: None flows into object, str % is type-checked, and (5).items() / 5["a"] / for x in 5: fire at check time. The previous v0.10.0 VM completeness release dispatched dunders and rich comparisons (__add__ + reflected forms, __eq__ / __lt__ / …, __str__ / __repr__ / __len__ / __getitem__ / __contains__) on user instances, ran finite generators (yield / yield from, capped at 1M items), modelled type(x) as a real type object, invoked @property getters and bound cls for @classmethod, and shipped the long tail of missing builtins (divmod, pow, format, ascii, int(str, base), full set algebra, the missing string methods, json.dumps(indent=…), time.perf_counter, math.gcd/lcm/factorial/…) plus max / min / list.sort key= / reverse= / default= kwargs and pydantic model_validate / model_dump for flat models. The type checker plugged three exhaustiveness / augmented-assign false positives. The previous v0.9.0 stress-test cleanup release closed 32 findings from a v0.8.1 stress sweep — the VM became the daily-driver runner (Result combinators, open() write/append/binary modes, class patterns on built-ins, deep freeze let, comptime inlining, lazy import, class! exception fields, dataclass factories, collections.deque / heapq / contextlib / pydantic shims, multi-file projects, @property / super() / @contextmanager) and the type checker plugged silent-correctness gaps (Sequence covariance, variant-to-parametric-union flow, while True: reachability + narrowing, assert narrowing, *args annotation policy, extend list[T] dispatch, exhaustive match on T?, with-chain error mismatch, comptime let T: type). v0.9.1 fixed four tyc fmt round-trip corruption modes and a pub * facade hole; v0.9.2 fixed a cross-module class! Sub(Foreign): attribute_not_found false positive. See Project Status for the per-release breakdown. Phase 4+ items continue to land as they prove their value. The minimum-viable Typhon — non-null types + sealed unions + Result + dataclass emit — is shippable today.

Phase 0 — Foundation ✅

  • Cargo workspace with crate-per-stage layout.
  • Vendored Ruff fork (ruff_text_size, ruff_source_file, ruff_python_trivia, ruff_python_ast, ruff_python_parser) with let / mut soft keywords and Mutability field.
  • clap-based tyc shell.
  • miette + thiserror diagnostics.
  • tyc fmt, tyc check, tyc init.

Phase 1 — Core types ✅

  • Salsa DB with preprocessed_text / module_decl_names queries.
  • Name resolution + let / mut enforcement.
  • Nominal types: signatures, assignment compatibility, primitives, classes, generics containers.
  • Non-nullable by default with flow narrowing on is None / is not None / isinstance.
  • T? sugar.
  • tyc check emits “unknown name”, “type mismatch”, “nullable use” diagnostics.

Phase 2 — Class and value features ✅

  • class → @dataclass(slots=True); model → Pydantic BaseModel(extra="forbid").
  • Sealed unions + exhaustive match.
  • Result[T, E], ? operator, with-chains.
  • comptime let with env() lookup; required env vars declared in [env].
  • impl blocks merged into class bodies.
  • tower-lsp-server backend: diagnostics + hover.

Phase 3 — Structural typing and advanced ✅

  • Generics syntax: PEP 695 brackets, bidirectional inference, conflict-widening, bounded type vars.
  • interface → class Name(Protocol): with structural conformance check.
  • unsafe: lexical region with Unsafe[T] boundary marker.
  • @pure / @memo / @pure(memo=True) with six-condition purity check.
  • gather: → asyncio.TaskGroup (or asyncio.gather(return_exceptions=True) for best-effort).
  • go → typhon_runtime.tasks.spawn (strong-ref registry).
  • lazy import np = numpy (proxy class); lazy from … import … rejected. lazy let (module + class-body forms).
  • Pipe operator (|>).
  • extend ClassName: and extend BUILTIN:.
  • .dty stub files → .pyi emission; tyc check --stubs AST diff.

Phase 5.5 — Constructor / method arity safety ✅ (v0.2.0)

The flagship bug v0.2.0 catches: a class declared with class ApiClient: api_key: str that the user instantiates as ApiClient(base_url="…") — passing tyc check and tyc build in 0.1.6, crashing at runtime with TypeError: missing 1 required positional argument. v0.2.0 surfaces the same bug at check time, before the build ever runs.

  • ✅ Constructor arity (tyc::arg_count). The auto-generated __init__ of class / model declarations is arity-checked at every call site. Fields without an = default are required; fields with a default are optional. T? without an explicit = None is still required (Typhon does not auto-inject the default).
  • ✅ Method arity (tyc::arg_count). impl methods now carry full ArityInfo on their MethodSig (param names, defaults, *args / **kwargs). u.greet() is flagged when greet declares a required prefix: str parameter; previously method calls fell into the permissive arity shape.
  • ✅ Cross-module shape propagation. from foo import ApiClient and import foo as f; f.ApiClient(…) both flow through the new arity checks. .ty source and .dty stubs participate on equal footing through a project-wide ExternalShapes registry built once per invocation; stubs win on name collisions. Works in tyc check, tyc build, and the LSP.
  • ✅ Salsa-cached LSP shape extraction. A new tyc_db::module_shapes_query(file) salsa-tracked query caches per-file shape extraction by file text. The LSP backend keeps a per-project-root HashMap<dotted_name, SourceFile> so handles survive across keystrokes; a keystroke in one file only re-runs shape extraction for that file.
  • ✅ tyc::missing_field_init post-construction audit. Catches X.__new__(X) / object.__new__(X) bypass patterns: if the constructed instance escapes the function (return / call argument) with required fields unassigned, the audit fires. Dropped conservatively on setattr, on method calls, and inside unsafe: regions.

Limitations carried forward:

  • ⏳ Dotted-attribute annotations (let c: f.Cls = …) don’t resolve to the foreign class shape; use from foo import Cls or drop the annotation for now. (The constructor call itself still arity-checks correctly.)
  • ⏳ The post-construction audit doesn’t track container-literal escapes (return [c]) or outer-scope assignment escapes, and is intra-procedural.

Phase 5 — Interop and developer experience ✅ (v0.1.6)

Real-world adopters were landing on Typhon and hitting the same handful of papercuts in the same order. Phase 5 was the friction list — fix the friction before adding more features.

  • ✅ plain class X: keyword — symmetric with frozen class X:. Emits a bare Python class with no @dataclass decoration and no synthesised __init__. The canonical Python-interop escape hatch.
  • ✅ Auto-skip @dataclass for Enum / IntEnum / StrEnum / Flag / IntFlag / ABC / ABCMeta subclasses (in addition to the always-skipped Protocol / TypedDict / NamedTuple); project-specific bases via [emit] skip-decoration-bases matched on last identifier segment.
  • ✅ [emit] class-default validation. Unknown values ("struct", "plain", "none", …) now fail config load with tyc::invalid_config_value instead of silently falling back to "dataclass".
  • ✅ Python-semantic alignment. or / and typed as Union[truthy(lhs), rhs] (and the falsy dual); generator functions structurally assignable to Iterable[T] / Iterator[T] / AsyncIterable[T] / AsyncIterator[T]. Tracked via the tyc::python_semantic_drift audit warning.
  • ✅ Discoverability. tyc explain <code> for the diagnostic catalog (mirrors rustc --explain); tyc cheatsheet for the 30-second syntax refresher; tyc init scaffold ships a frozen dataclass + impl block + Result/?/match example, plus a fully-commented typhon.toml; tyc --help footer links docs, language reference, migrate, lsp, cheatsheet, and explain.
  • ✅ .py interop in build output. Stray .py files in src/ copy verbatim into the build dir (honouring __pycache__/ / tests/ / .venv/ / dotfile exclusions); tyc::orphan_py_import warns when a relative .py import resolves outside src/.
  • ✅ Diagnostic deep-links. Every tyc:: diagnostic carries a url(https://github.com/CodeHalwell/Typhon/blob/main/docs/diagnostics/<code>.md) clause renderable by miette and surfaced by tyc explain; 50+ catalog pages under docs/diagnostics/ embedded into the binary.
  • ✅ Build UX. tyc build --check dry-runs the pipeline and lists every file that would be written without touching disk; tyc::contains_secret_literal flags inlined env values whose binding name matches a credential suffix (KEY, TOKEN, PASSWORD, …).
  • ✅ tyc fmt wraps ruff format after the in-process whitespace pass, with a guarded check for residual Typhon-only tokens.
  • ✅ tyc debug --break <ty-file>:<line> translates Typhon source locations via .py.map and forwards them to the chosen debugger via -c "break …". Repeatable.

Phase 4+ — Beyond v1 (landing as they prove value)

Shipped:

  • ✅ Automatic asyncio.gather inference ([strictness] auto-gather), with cross-module folding of imported @gatherable callees (v0.14.2) and tyc::gather_opportunity advice that suggests an explicit gather: for runs of 2+ adjacent independent awaits (v0.14.2, default on, surfaced live in the LSP).
  • ✅ enum Name: keyword as a first-class declaration form, sugaring over enum.Enum with enum.auto() (v0.11.0); enum match exhaustiveness over the closed member set (v0.13.0, cross-module in v0.14.1).
  • ✅ as! checked boundary cast — the sound one-line replacement for the unsafe:-plus-re-assert dance, lowering to checked_cast in typhon_runtime/cast.py (v0.14.0); composes in any expression position since v0.15.0.
  • ✅ try_result(thunk[, on_err]) exception→Result combinator, a prelude name typed Result[T, E] (v0.15.0).
  • ✅ Compiler-bundled .dty stubs for the most-imported third-party libraries (httpx, requests) whose packaging defeats venv introspection, seeded before venv enrichment (v0.15.0).
  • ✅ [emit] traceback-remap — auto sys.excepthook rewriting of uncaught tracebacks to .ty via .py.map, default off (v0.14.0).
  • ✅ Typeshed-backed ty integration Phase 1 — [checker] external = "ty" / --with-ty runs Astral’s ty over the emitted Python and re-attributes its diagnostics to .ty source (v0.12.0).
  • ✅ Deep compile-time library introspection — venv signature introspection captures parameter and return annotations, so a wrong-typed argument to a fully-typed third-party function or constructor is caught via tyc::type_mismatch (v0.12.0); an unintrospectable dependency now warns instead of silently skipping its checks.
  • ✅ Loop parallelisation for pure list comprehensions on free-threaded Python ([strictness] auto-parallel).
  • ✅ PGO via tyc profile ([strictness] pgo-memoise).
  • ✅ LSP completions (visible bindings + keywords + builtins + venv-driven member-access introspection + from-import members from sibling files) and “Remove unused import” code action.
  • ✅ Cross-file go-to-definition across .ty / .py.
  • ✅ tyc migrate (typed .py → .ty).
  • ✅ tyc repl; tyc debug (with --break source mapping); tyc run (defaulting to the in-process tyc-vm tree-walking interpreter — no build/, no CPython spawn).
  • ✅ tyc add / remove / sync over uv.
  • ✅ Runtime stubtest probe via tyc stubtest (shells out to python -m mypy.stubtest).
  • ✅ VM performance Tier 1 — small-int fast path, per-class method cache, direct method-call dispatch, and slot-resolved locals compress tyc run’s slowdown vs tyc build + CPython from ~5–18× to ~3–14× (startup-adjusted). See Performance Baseline and docs/vm-performance-plan.md.
  • ✅ [optimise] config profile + tyc build -O — a single level dial that flips the default of auto-memoise / auto-gather / auto-parallel / pgo-memoise to true.
  • ✅ Performance-advice lint family — seven advice-level lints (perf_membership_in_loop, perf_list_shift_in_loop, perf_str_concat_in_loop, perf_sort_in_loop, perf_sorted_first, perf_keys_membership, lazy_import_opportunity), gated by [strictness] suggest-perf, default on.
  • ✅ Free-threading parallelisation wave — widened auto-parallel comprehension shapes (filters, multi-arg calls, nested pure calls), integer accumulator-loop reductions (auto-parallel-reductions), parallel_opportunity / shared_mut_across_tasks advice lints, and a parallel-backend = "interpreters" (PEP 734) option.
  • ✅ Native PEP 810 lazy imports on [python] target = "3.15" / "3.15t" — lazy import lowers to CPython’s native statement instead of the typhon_runtime helper; 3.13/3.14 output unchanged.

Deferred:

  • ⏳ Richer comptime: broader stdlib in the sandbox, for loops in comptime def. (Types as values shipped in v0.5.0; container literals and pure string methods in v0.3.1.)
  • ⏳ Full structural subtyping with proper variance and bounded higher-kinded forms.
  • ⏳ ty integration Phase 2 as an embedded in-process library sharing the Salsa db (today: Phase 1 ships the subprocess path via [checker] external = "ty" / tyc ty, v0.12.0). Phase 2 was prototyped and proven feasible but is not shipped — it needs a git dependency that the project’s cargo deny policy disallows.
  • ⏳ A Typhon-native source-mapping debugger UI (today: pdb launcher with --break TY:LINE translation).
  • ⏳ Broader tyc::python_semantic_drift audit beyond the or/and and generator → Iterable cases already fixed.
  • ⏳ Corpus round-trip sweep across third-party Python projects.
  • ⏳ Async public-API stability rules if async inference is ever added.

Scope-cutting rule

The minimum-viable Typhon is non-null types + sealed unions + Result + dataclass emit. That is the floor. Everything else is layered on without changing the meaning of any program written against the minimum core.

Where next