How Glyph is hardened

We don't just claim Glyph is safe. We attack it.

A language whose whole pitch is “your agents can't quietly break it” has to earn that. So we run fleets of AI agents that role-play skeptical engineers — a frustrated React developer, a backend contrarian, a safety adversary — have them build real apps and try to break the guarantees, then fix everything they surface. Here is that loop, and exactly what it found.

The adversarial loop

Every finding is reproduced against the compiler, fixed at the root, and independently re-verified before it counts.

1

Explore

Persona agents build real programs — forms, backends, stateful UI — and record every place the language fought them.

2

Reproduce

Each finding is minimized to the smallest failing program and re-run against the compiler. Anything that can't be reproduced is dropped.

3

Fix

Root-cause fix in the Rust compiler with a regression test that fails before and passes after. The full suite must stay green, or the change reverts.

4

Verify

An independent pass re-runs every repro against the freshly built compiler — catching “the agent thought it was fixed” gaps before anything ships.

The ones that mattered most: silent-correctness holes

Each of these compiled clean and could ship wrong code that passed the type check — exactly the class of bug Glyph exists to prevent. Every one is now caught at compile time.

Critical — fixed

Nullary error variants in a Result match

A match over Result<T, Empty | BadQty(...)> lowered to two duplicate case "Err" branches; the first swallowed every error and silently dispatched to the wrong arm.

Now: one grouped case with a correct inner switch; each variant routes right.

Critical — fixed

Misspelled constructor as a silent catch-all

A typo'd variant in a match arm (Loded for Loaded) was silently bound as an irrefutable catch-all — masking non-exhaustiveness and misrouting at runtime, with no diagnostic.

Now: an unknown constructor is rejected at the typo, like a real name.

High — fixed

Partial bool match slipped past exhaustiveness

match n > 0 { true => ... } — missing the false arm — compiled and passed tsc, then threw at runtime.

Now: a compile-time non-exhaustive-match error.

High — fixed

Multi-parameter components mis-bound props

component C(a, b) emitted positional params, but React calls components props-first — so a silently bound to the whole props object. It passed tsc.

Now: a clear compile error pointing to the single props-record form.

High — fixed

JSX <match> skipped exhaustiveness

A JSX <match> with a missing <case> compiled green and threw at runtime — unlike value-level match, which was checked.

Now: the same exhaustiveness check applies; a missing case is a compile error.

High — fixed

Record payload bound to the wrong field

Binding a record-payload variant to a name (BadQty(b)) read a nonexistent .value off the flattened payload.

Now: the payload binds correctly, so b.sku works.

Rules the checker now enforces

Soundness the compiler used to leave to tsc — now caught by Glyph itself.

Fixed

Call arity

Too few or too many arguments were accepted (and ran to NaN). Now E0213 rejects a wrong argument count.

Fixed

The ? error type, across the stdlib boundary

The exact-error-type rule was skipped when the error came from a stdlib wrapper. Now it's enforced there too.

Fixed

Aliasing an owned handle

let g = f on a resource handle created a second binding that could double-close. Now rejected (E0215).

Fixed

Unreachable arms & deeper validation

An arm after a catch-all is now flagged; json.parse<T> now structurally validates Record fields instead of only checking presence.

First-contact friction, removed

The small things that made a bad first impression — none of which served any purpose.

Fixed

Accessibility & data attributes

aria-label and data-* parsed as subtraction. Now they work, and emit as proper React props.

Fixed

npm package names

Hyphenated and scoped specifiers (react-hook-form, @hookform/resolvers/zod) were rejected. Now they import directly.

Fixed

Browser-bundle first run

The runtime bootstrap wasn't loaded in a browser build, crashing on first run. Every emitted module now pulls it in.

Fixed

Error messages that misdirect

Writing if now points at match; a range pattern says so directly; JSX whitespace and a bare $ in text are handled.

Still open — on the record

Hardening means naming what isn't done, too. These are known and tracked, not hidden.

Deliberately deferred or awaiting a design decision:

A must-use warning for a dropped Result awaits a warning severity level (today diagnostics are errors-only, and a hard error would break valid code). Rejecting implicit-any closures in Glyph's own checker needs real contextual inference to avoid false positives on validly-typed callbacks. Ergonomic FFI — using a complex-typed TS library without a per-library adapter — and JSX prop spreading are the biggest interop questions, tracked in open questions. A record-payload variant in first position of a union declaration still needs a parser fix.

← See the engineer questions these fixes came from