# Topaz v5.20 documentation > Complete English manual in agent-readable Markdown. > Use https://topaz.ooo/llms.txt when a compact index is more appropriate. > > This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. --- route: https://topaz.ooo/v5.20/en/docs/introduction markdown: https://topaz.ooo/docs/introduction.md covers: §0, §1, §2, §3, §4, §5, §6, §7, §12, §13, §13a, §14, §15, §16, §17, §20, §22 # Introducing Topaz This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. Topaz is a compact language for application intent. It reads like Python or TypeScript but prefers a single, consistent way to express each common idea. The same rules apply whether code is written by a developer or generated by a tool. The source of Topaz 5.20 is public at topaz.ooo/dl. Topaz works one layer above the machine, on command-line tools, data transformation, service logic, configuration, and the orchestration of SQL, shell and paths. You can run the same source directly or build a native program, Python package, or web artifact. This page explains where the language fits and how its code feels. If you are new to Topaz, follow the [Learning path](/en/docs/learn) or run your first program in the [Playground](/playground). Use [Syntax at a Glance](/en/docs/language-tour) for a concise overview of the language, and read the [language philosophy](/en/docs/philosophy) to understand its design choices. Every example on this page runs through the Topaz toolchain, and the output under each one is what it printed. ## What Topaz is good at ### Four products, one source `topaz run` executes the file you just wrote. `topaz build` turns that same file into a self-contained native binary, into `program.py` beside its runtime support file `topaz_py_rt.py`, or into a web package for a browser host. Moving a program to a new deployment is a flag on one command. ### One settled form Every ordinary idea has one canonical form and the language holds that line. Recoverable failure goes through `Result`, expected absence goes through `Option`, and cleanup goes through `defer`. No file in a Topaz codebase drifts into a dialect of its own. ### Failure has a shape Failure is part of the type, so a reader meets it in the signature. The postfix `?` carries an `Err` to the caller and `defer` runs on every exit path, including the one that failed. `match` is checked for missing cases. ### Your words are names Identifiers are Unicode from the lexer level upward and no normalization pass runs behind them. Two names are equal when you typed the same characters. The module resolver rejects identifiers that invite confusion, so your domain keeps its own vocabulary. ### Small on purpose The surface stays small because the small surface is the product. The same compact grammar is what a developer reads and what a code generator writes. A new form arrives with defined behavior, a working implementation, examples and documentation. ## What Topaz code feels like ### Unicode-first Identifiers support Unicode from the lexer level upward. Domain terms remain in your native language rather than becoming romanized approximations. ```topaz function greet(name: string, language: string) -> string { return match language { case "한국어" => "안녕하세요, {name}님!" case "Русский" => "Привет, {name}!" case _ => "Hello, {name}!" } } let 사용자 = "김토파즈" print(greet(사용자, "한국어")) print(greet("Topaz", "Русский")) ``` ```text output 안녕하세요, 김토파즈님! Привет, Topaz! ``` `사용자` is a standard variable. Unicode identifiers are a normal part of the language. Topaz does not silently rewrite entered names, and the module resolver rejects identifiers that cause confusion. ### A coherent canonical surface Common choices have a canonical form. Recoverable failures use `Result`, expected absence uses `Option`, resource cleanup uses `defer`, and `match` evaluates whether all cases are covered. The language fixes these choices so that no file diverges into its own dialect. ```topaz function parsePort(raw: string) -> Result { let n = toInt(raw) ?? -1 if n < 1 { return Err("not a port: {raw}") } return Ok(n) } function startup(raw: string) -> Result { defer print("config closed") let port = parsePort(raw)? return Ok("listening on {port}") } print("{startup("8080")}") print("{startup("http")}") ``` ```text output config closed Ok(listening on 8080) config closed Err(not a port: http) ``` The `?` operator propagates an `Err` to the caller, while `defer` executes on every exit path. That is why `config closed` appears before both results. ### Templates keep intent structured The template registry includes `p`, `r`, `sh`, and `sql`. For example, `p"..."` is a path template, while `sql"..."` keeps SQL text separate from interpolated parameters instead of collapsing everything into a single string. ```topaz let table = "users" let q = sql"select * from {table} where active = {true}" print("{q}") ``` ```text output ``` The output displays a structural summary. The template retains the distinction between literal text fragments and interpolated values. ### Check once, choose an output Topaz is statically typed. The checker validates the complete program before execution. ```topaz type TrafficLight = "red" | "yellow" | "green" function next(light: TrafficLight) -> TrafficLight { return match light { case "red" => "green" case "green" => "yellow" case _ => "red" } } ``` `topaz run` parses, resolves, checks, and executes the program. From that same source, `topaz emit` and `topaz build` create alternative outputs. The Rust target produces a self-contained native binary. The Python target writes `program.py` alongside its support file `topaz_py_rt.py`. Rust and Python outputs are evaluated with shared language scenarios. [Toolchain Status](/en/docs/toolchain/status) describes target-specific behavior and selection guidance. ```bash topaz build app.tpz --target python --out-dir py-out ``` ## When not to use Topaz Topaz focuses on managed application workflows. Rust, C, or Zig fit work that requires direct ownership and lifetime control, unsafe memory boundaries, embedded targets, manual performance tuning, or systems-level control. Topaz targets high-level application workflows: deployable application logic without a systems-level source surface, domain language expressed in code, and a minimal grammar that developers and tools can produce consistently. When that describes your project, proceed with the [Learning path](/en/docs/learn). **One way to say it.** --- route: https://topaz.ooo/v5.20/en/docs/learn markdown: https://topaz.ooo/docs/learn.md covers: §0 # Learning path This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. **Outcome:** Select the appropriate starting environment and follow the step-by-step progression from a single `.tpz` file to a tested two-module product. **Prerequisites:** You only need a text editor and basic familiarity with the terminal. The final lesson requires Python 3.11 or newer. ## Choose your starting point Use the [Playground](/playground) to modify and run short code snippets without installing local dependencies. It covers the first four lessons, but it cannot generate the package or artifact built in the final lesson. Install Topaz locally to complete the entire learning path. [First Program](/en/docs/getting-started) provides installation options for standard shells, PowerShell, and npm, followed by consistent `topaz` CLI commands across all platforms. Most readers complete the five hands-on lessons in about an hour. You can pause at any readiness check and resume later. Each lesson leaves the study-plan application in a working state. If you already program in another language, start with [Coming From Other Languages](/en/docs/learn/coming-from-other-languages). It shows what transfers unchanged and what does not, so you can take only the lessons you need. ## What you will build The application evolves incrementally across lessons: 1. **[First Program](/en/docs/getting-started)** — Write a study-plan value, run `check` and `run`, and resolve a static type diagnostic. 2. **[Values and Functions](/en/docs/learn/values-functions)** — Add explicit and inferred types, introduce a function, and manage the single mutable variable required by the application. 3. **[Data and Control](/en/docs/learn/data-control)** — Model study tasks using nominal data types, store them in a collection, and control execution using `if`, `match`, and `for`. 4. **[Failures and Resources](/en/docs/learn/failures-resources)** — Process invalid input using `Option`, `Result`, and `?`, ensure cleanup with `defer`, and inspect an intentional error condition. 5. **[First Application](/en/docs/learn/first-application)** — Modularize the codebase into `src/main.tpz` and `src/plan.tpz`, add a test suite, lock and verify package dependencies, and build a Python product. The final command executes the generated artifact from a directory containing no `.tpz` files, operating independently of the Topaz project structure and compiler. Topics such as package registries, browser lifecycle management, HTTP hosting, and backend architecture are optional next steps rather than initial prerequisites. ## Decision: Playground or local tools? Choose the Playground to evaluate expressions quickly in the browser. Choose a local installation to build the tested application and retain its output artifact. You can begin in the browser and transition to a local environment before the final lesson without losing context. ## Try this Decide where you will complete the course. Do you require a local installation now? ### Answer Use the Playground for an initial syntax overview. Install Topaz locally before the First Application lesson, as it generates source files, executes test suites, and builds a Python distribution package. If you plan to complete the entire guide in one session, starting locally avoids switching tools midway. ## Ready to continue when You have selected your starting environment, understand that the target output is a tested two-module Python product, and have either opened the Playground or prepared your environment for installing `topaz`. Continue to [First Program](/en/docs/getting-started). Keep [Syntax at a Glance](/en/docs/language-tour) open as a quick reference whenever you need to review language constructs. --- route: https://topaz.ooo/v5.20/en/docs/learn/coming-from-other-languages markdown: https://topaz.ooo/docs/learn/coming-from-other-languages.md covers: §3, §4, §7, §8, §12, §13 # Coming From Other Languages This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. **Outcome:** Translate your existing programming habits into the patterns used here, and identify which patterns are intentionally omitted. **Prerequisite:** Ability to read code in at least one other language. No prior experience with this language is required. ## Start from what transfers unchanged Much of the syntax will look familiar. Values, names, functions, conditionals, loops, and collections perform recognizable roles, while the checker explicitly highlights key differences. You should be able to understand what the code snippet below does before learning every detail. ```topaz record Task { title: string, minutes: int } function totalMinutes(tasks: Array) -> int { let mut total = 0 for task in tasks { total = total + task.minutes } total } ``` If you have written Python, JavaScript, Java, or Rust, you can already determine what this code does. That is the rationale for starting here. The key differences worth noting are few, as outlined below. ## Four differences that actually matter **Mutation is opt-in.** A plain `let` binding cannot be reassigned. Declaring `let mut` specifies that reassignment is intended. Languages where variables are mutable by default often require examining more of a function to determine which names change. Here, the declaration answers that question. **Recoverable failure is a value.** There are no exceptions to catch. A function with a recoverable failure returns a `Result`, which the caller either handles or propagates using `?`. The call boundary carries the failure structure instead of requiring you to infer it from a function body or exception convention. Runtime faults remain a separate, non-recoverable concern. **Absence is represented in the type.** Standard optional values use `Option`. External nullable data uses `T | null`, keeping the two models distinct. In both cases, the checker requires handling the absent case before treating the value as present. **The selected profile defines host authority.** File and I/O operations use the selected host or product profile, and web products receive the capabilities declared by that profile. A function signature indicates data and recoverable failure. Imports, package configuration, and the selected profile complete the authority scope. [Reading a Real File](/en/docs/learn/reading-topaz) uses all of these mechanisms. ## What is deliberately absent Understanding which patterns are omitted helps avoid reaching for unmapped habits early on. There are no class hierarchies or inheritance structures. Data shapes are records, and behavior lives in functions operating on them. If your instinct is to design with base classes and overrides, model with records and explicit functions instead. There is no runtime reflection over your program, no dynamic dispatch via protocol tables, and no row polymorphism. Details on deferred capabilities are in [Records & Nominal Data](/en/docs/language/records-nominal-data). Implicit conversions between numeric types are not supported. An `int` does not convert to a `float` for convenience in surrounding expressions. These design boundaries are intentional. They keep critical choices visible in code declarations and configuration without assuming a function signature captures every host interaction. ## A translation table for the first hour A `dict`, `Map`, or `HashMap` maps to a collection, detailed in [Collections & Comprehensions](/en/docs/language/collections-comprehensions). A data-only `class` often maps to a record. Define interface boundaries using explicit function signatures. Use a static protocol when a generic contract requires one, without runtime dynamic dispatch. Recoverable `try`/`except` logic maps to returning a `Result`. Standard optional results use `Option`. External nullable data uses `T | null`, with the checker maintaining an explicit distinction between them. ## Try this Consider a function that looks up a task by title and might not find one. Before reading the answer, decide what its return type should be. ### Answer An `Option` of a task, rather than a task type alone. A missing item is an ordinary outcome of a lookup operation rather than an error, so it is expressed in the type as absence. A `Result` would be appropriate if the lookup operation itself could fail, such as when reading from a file. Distinguishing between absence and failure is an explicit choice required by this language, and defining it in the signature saves every caller from guessing. ## Ready to continue when You can list the four key differences, identify which habits from previous languages are omitted here, and choose between absence and failure for a function you are about to write. Work through the course starting at [First Program](/en/docs/getting-started) if you have not done so, or move directly to [Syntax at a Glance](/en/docs/language-tour). --- route: https://topaz.ooo/v5.20/en/docs/learn/what-a-program-is markdown: https://topaz.ooo/docs/learn/what-a-program-is.md covers: §3, §4, §7, §16 # What a Program Is This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. **Outcome:** Read a short program and explain how each line contributes to the observed output. **Prerequisite:** None. This guide assumes no prior programming experience. Experienced developers can skip directly to [First Program](/en/docs/getting-started). ## A program is a description, not an instruction shouted at a machine Programming is often perceived as commanding a computer step by step. In practice, it resembles writing a precise specification that eliminates ambiguity. You define entities such as values and functions, and specify how results are derived from those inputs. The runtime engine does not guess what outcome you intended. It executes this written specification literally, including any errors in logic. That final point defines software development. An execution engine cannot infer developer intent or correct mistaken assumptions automatically. Computers follow the given rules and definitions without applying human intuition. Learning a language involves expressing precise logic without unintended ambiguities, ensuring that the specification matches the intended goal. ## Four ideas cover most of it Most programs are constructed from four core concepts. Understanding how these four elements work together allows you to read code by identifying what data exists and how it moves through the system. A **value** represents data, such as the integer `45`, the string `"study plan"`, or the boolean `true`. Values are the concrete pieces of information that a program evaluates and displays. In the example code below, `"study plan"`, `45`, and `20` are values defined on lines 5, 6, and 7. A **name** references a value for subsequent use. For example, writing `let total = 45` binds the identifier `total` to the value `45`. Naming values prevents repeating raw data throughout a file and makes the code readable. In lines 5 through 7, `plan`, `total`, and `spent` are names created to hold data for later calculation. A **function** is a named unit of computation. It accepts input values and returns a result, allowing calculation logic to be defined once and reused across a codebase. Lines 1 through 3 define the `minutesLeft` function, which receives inputs and computes the difference `total - spent`. A **run** is the execution of the program description to produce observable output. During a run, the system evaluates expressions line by line to produce final results. Line 9 executes during a run to combine the function output with strings and display the final text on the screen. The following example combines all four concepts in a single file. ```topaz function minutesLeft(total: int, spent: int) -> int { total - spent } let plan = "study plan" let total = 45 let spent = 20 print("{plan}: {minutesLeft(total, spent)} minutes left") ``` If Topaz is installed, save the code as `what-a-program-is.tpz`, then check and run it using the commands below. Otherwise, review the explanation for now. The [First Program](/en/docs/getting-started) section covers toolchain installation prior to writing custom files. ```bash topaz check what-a-program-is.tpz topaz run what-a-program-is.tpz ``` The command produces the following output: ```text output study plan: 25 minutes left ``` ## Read the result backwards into the code Start from the output and trace it back to the source. The printed output `study plan: 25 minutes left` is constructed from separate pieces evaluated during execution. The string `study plan` originates from the variable `plan`, which references `"study plan"`. The numeric value `25` is not hardcoded inside the print statement; it is dynamically evaluated by `minutesLeft` from inputs `45` and `20`. Look at the function definition again. The signature shows `total: int, spent: int` and `-> int`. The `: int` following a name indicates that an integer value is expected in that position, while `-> int` indicates that the function returns an integer. Detailed rules for type annotations are covered formally in the next lesson. Understanding their basic role is sufficient for now. The signature establishes its input and output contract, accepting two integer parameters `total` and `spent` and declaring an integer return type. The body of the function consists of a single expression, `total - spent`, which evaluates to the final result of `25`. In Topaz, the evaluation of this body expression determines the return result. External context cannot alter function behavior, as all dependencies are passed explicitly through parameters. This property provides isolated reasoning. A computation that depends solely on explicit inputs can be evaluated independently, without requiring knowledge of global program state. When reading or writing functions, examining the declared inputs and the body expression is sufficient to predict what the function will return. ## Why the check step exists The workflow uses two commands to separate description analysis from execution. The check step analyzes the program description and identifies missing inputs before execution. If `minutesLeft(total)` is called without the second argument, the check reports the error location and halts execution before the run begins. This prevents the program from executing with missing arguments. Some environments detect missing arguments only during execution when that line is reached, whereas statically checked languages identify them earlier. Topaz exposes this validation as an explicit command prior to execution, which is why `check` precedes `run` throughout these guides. Checking the code first gives immediate feedback on structural errors before running the application. ## Try this Change `spent` to `50` while leaving other values unchanged. Predict the output prior to running the code. ### Answer The output becomes `study plan: -5 minutes left`. The subtraction expression is evaluated literally as written, as the program description contains no constraint requiring positive results. Calculating `45 - 50` yields `-5` because arithmetic rules are applied without evaluating whether negative time makes practical sense. If negative values are invalid for domain logic, that requirement needs to be explicitly defined in code. The engine executes the written instructions rather than intended outcomes. ## Ready to continue when You can identify a value, a name, a function signature, and its result expression, while explaining the operational difference between the check and run commands. Install the toolchain and write a first program in [First Program](/en/docs/getting-started). Developers familiar with another language can review [Coming From Other Languages](/en/docs/learn/coming-from-other-languages) to map existing knowledge to these concepts. --- route: https://topaz.ooo/v5.20/en/docs/getting-started markdown: https://topaz.ooo/docs/getting-started.md covers: §3, §4, §5, §16, §22 # First Program This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. **Outcome:** Create one `.tpz` file, check it before execution, run it, and repair an error identified by the checker's source span. **Prerequisite:** Choose a starting point on the [Learning path](/en/docs/learn). Run the commands below in a local terminal. ## Install the command On macOS or Linux: ```bash curl -fsSL https://topaz.ooo/install.sh | sh ``` On Windows PowerShell: ```powershell irm https://topaz.ooo/install.ps1 | iex ``` If Node is already installed: ```bash npm install -g topaz-lang ``` Confirm the installed product: ```bash topaz version ``` ```text output Topaz 5.20.2 ``` This first program does not require a repository clone, package manifest, Rust, or Python. If you use VS Code or an Open VSX-compatible editor, install the official Topaz extension from the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=studiohaze.topaz) or the [Open VSX Registry](https://open-vsx.org/extension/studiohaze/topaz). It recognizes `.tpz` files, displays the Topaz file icon, and provides syntax highlighting. It also launches the installed `topaz lsp` to display editor diagnostics. The extension does not bundle a compiler, so ensure the installed Topaz executable is available on `PATH` or set `topaz.executablePath`. You can also run `topaz check` directly from the terminal. ## Write the smallest study plan Create `first-program.tpz` with the following contents: ```topaz let topic = "Topaz basics" let minutes: int = 25 print("{topic}: {minutes} minutes") ``` Check the source file, then run it: ```bash topaz check first-program.tpz topaz run first-program.tpz ``` `check` validates the file without executing it: ```text output first-program.tpz: types-ok (1 module) first-program.tpz: resolve-ok (1 module) ``` `run` passes the same static gate and then prints: ```text output Topaz basics: 25 minutes ``` Read the program in execution order. The first `let` binding assigns a string and lets the checker infer its type. The second binding explicitly specifies `int`, requiring the checker to verify that `25` is an integer. `print` evaluates the interpolated expressions inside `{...}` and outputs a single line. ## Read and repair a diagnostic Create a separate file named `first-program-error.tpz`. This source code is intentionally invalid and is presented as plain text rather than a runnable Topaz example: ```text let topic = "Topaz basics" let minutes: int = "25" print("{topic}: {minutes} minutes") ``` Run the checker: ```bash topaz check first-program-error.tpz ``` The checker does not execute the file. It identifies the declared type, the type it found, and the exact source span: ```text output error[TPZ5001]: expected `int`, found `string` --> first-program-error.tpz:2:20 | 2 | let minutes: int = "25" | ^^^^ first-program-error.tpz: 1 type diagnostic ``` Repair the highlighted value by changing `"25"` to `25`. Running `topaz check first-program-error.tpz` now succeeds, and running `topaz run first-program-error.tpz` produces the same output as the working program above. ## Decision: check or run? Use `topaz check` while editing to verify source validity without starting the program. `topaz run` performs the same static check before executing the code to demonstrate behavior. Running `check` separately provides a fast, execution-free validation step rather than an additional check that `run` omits. ## Try this Change `topic` to `"Functions"` and `minutes` to `30`. What should the program output? ### Answer It outputs `Functions: 30 minutes`. Both edits preserve the inferred `string` and declared `int` types, allowing `topaz check first-program.tpz` to succeed before execution. ## Ready to continue when You can create a `.tpz` file, execute `topaz check` and `topaz run`, and use the expected and found types alongside the highlighted source span to repair invalid values. Continue to [Values and Functions](/en/docs/learn/values-functions). Refer to [Syntax at a Glance](/en/docs/language-tour) whenever you need a quick syntax reference. --- route: https://topaz.ooo/v5.20/en/docs/learn/values-functions markdown: https://topaz.ooo/docs/learn/values-functions.md covers: §3, §4, §5, §7 # Values and Functions This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. **Outcome:** Connect runtime values to checked types and place a repeated calculation inside a named function. **Prerequisite:** Complete [First Program](/en/docs/getting-started) and be able to run `topaz check` before `topaz run`. ## Extend the working file Create `values-functions.tpz`: ```topaz function minutesLeft(total: int, spent: int) -> int { total - spent } let topic = "Topaz basics" let total: int = 45 let spent = 20 let mut sessions = 0 sessions = sessions + 1 print("{topic}: {minutesLeft(total, spent)} minutes left") print("sessions: {sessions}") ``` Check and run the program: ```bash topaz check values-functions.tpz topaz run values-functions.tpz ``` The expected output is: ```text output Topaz basics: 25 minutes left sessions: 1 ``` ## Read from the result back to the code The previous lesson introduced type notations like `: int` and `-> int` briefly, promising to explain them in detail here. This section fulfills that promise by examining how Topaz handles types across function signatures and variable bindings. The first line of output shows the result of calling `minutesLeft(45, 20)`. Looking at the function definition, `function minutesLeft(total: int, spent: int) -> int` uses `: int` after each parameter name to declare that `total` and `spent` expect integer values. The `-> int` notation after the parameter list specifies the function return type. These explicit type annotations communicate the function contract clearly to anyone reading or calling the function. Readers can see what inputs the function requires and what value it produces without inspecting the inner function body. Inside `minutesLeft`, the final expression `total - spent` evaluates to an integer and yields that result without an explicit `return` statement. Topaz automatically returns the value of the last expression in a function block. Use `return` when an early exit improves control flow readability. The declared return type `-> int` applies to all potential exit paths. In the variable declarations, Topaz relies on type inference. Type inference means Topaz automatically determines the type of a binding from its initial assigned value. `topic` and `spent` require no explicit type annotations because their initial values, `"Topaz basics"` and `20`, make their string and integer types clear. When the initial value is obvious, omitting the type annotation keeps the code concise. By contrast, `total: int = 45` uses an explicit type annotation `: int`. Adding an explicit type annotation is helpful when defining an important type boundary or when you want to make the expected type explicit for documentation purposes. `topic`, `total`, and `spent` are all immutable bindings created with standard `let`. The program can read and use their values, but it cannot reassign them. `sessions` works differently. The `let mut` declaration indicates that reassignment is intended for this binding. The line `sessions = sessions + 1` updates the stored value of `sessions` from `0` to `1`. Reserve mutation for bindings that track changing state, rather than marking every value as mutable simply because it might change later. ## Decision: infer, annotate, or mutate? Choosing whether to infer types, write explicit annotations, or mark bindings mutable depends on how each value is used in your program. Use standard `let` with type inference for local values with obvious types. When an initial value makes the type clear, omitting the annotation reduces clutter while keeping the code easy to read. Add an explicit type annotation when defining an important boundary in your code. Explicit annotations like `total: int` make type expectations clear at a glance for anyone reviewing or maintaining the program. Keep function signatures explicit with parameter types like `(total: int, spent: int)` and return types like `-> int`. Callers depend on these parameter and return type contracts to know how to interact with the function without needing to read through its implementation details. Add `mut` only when subsequent reassignment is required by the design. Immutable bindings are easier to reason about because their values remain constant after initialization. ## Try this Change `total` to `60` and `spent` to `15`. Leave `sessions` unchanged. How does the output change? ### Answer The first line becomes `Topaz basics: 45 minutes left`. The second line remains `sessions: 1`. The function receives the updated arguments `60` and `15` to calculate `60 - 15`, while the separate mutable counter still increments once from `0` to `1`. ## Ready to continue when You can explain type inference, an explicit annotation, immutable bindings, mutable bindings, function parameters, and the expression that produces the function return value. For specific syntax, see [Types](/en/docs/language/types) and [Functions & Generics](/en/docs/language/functions-generics). Generics serve as reference material for later sections and are not a prerequisite. Proceed to [Data and Control](/en/docs/learn/data-control). --- route: https://topaz.ooo/v5.20/en/docs/learn/data-control markdown: https://topaz.ooo/docs/learn/data-control.md covers: §5, §6, §8, §9, §10 # Data and Control This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. **Outcome:** Model a well-defined domain using a record and a collection, then select `if`, `match`, and `for` based on the specific question each control structure answers. **Prerequisite:** Complete [Values and Functions](/en/docs/learn/values-functions) and understand bindings, type annotations, parameters, and function return values. ## Give each task a shape Create `data-control.tpz`: ```topaz record StudyTask { title: string, minutes: int, done: bool = false, } function pace(task: StudyTask) -> string { if task.done { return "done" } match task.minutes { case 1..15 => "quick" case 16..30 => "focused" case _ => "deep" } } let tasks: Array = [ StudyTask { title: "Run first program", minutes: 10, done: true }, StudyTask { title: "Build application", minutes: 25 }, ] let mut remaining = 0 for task in tasks { if !task.done { remaining = remaining + task.minutes } print("{task.title}: {pace(task)}") } print("remaining: {remaining} minutes") ``` Run the standard two-step workflow: ```bash topaz check data-control.tpz topaz run data-control.tpz ``` It prints: ```text output Run first program: done Build application: focused remaining: 25 minutes ``` ## Follow the three decisions A `record` is a named data structure that groups related fields into a single nominal type. Defining a record provides explicit names and types for each property instead of relying on loose values or unlabelled positional tuples. This choice keeps domain attributes bundled together under a clear name. In the example code, `record StudyTask` defines this shape with `title`, `minutes`, and `done` fields, where `done` defaults to `false` when omitted during construction. An `Array` is an ordered collection that holds multiple elements of the same type in a sequence. An array is chosen here because tasks have a specific order in a study plan, and keeping duplicate or sequential items intact is important for processing them in order. Alternative collection structures focus on key lookup or uniqueness rather than ordered traversal. In the example code, `let tasks: Array = [ ... ]` creates an ordered sequence holding two `StudyTask` record instances. While `record` and `Array` establish the shape and organization of data, control flow structures direct how that data is processed. The constructs `if`, `match`, and `for` handle distinct execution decisions to evaluate data elements. A `for` loop is a control structure that iterates over each item in a collection sequentially. A `for` loop is chosen here because every task in the array needs to be visited once in order to compute total minutes and display results. Other constructs would require managing index positions manually or checking conditions repeatedly without directly tracking the collection layout. In the example code, `for task in tasks { ... }` binds each element of `tasks` to the variable `task` during each step of the iteration. An `if` statement is a control structure that evaluates a single boolean expression to decide whether to execute a block of code. An `if` statement is chosen here because the decision depends on a binary true or false condition rather than multi-way branching or value matching. Using `if` keeps simple conditional checks clear without unnecessary syntax. In the example code, `if !task.done` inside the loop checks whether a task is unfinished before adding its minutes to `remaining`, while `if task.done` inside `pace` performs an early return for completed tasks. A `match` expression is a control structure that compares a value against pattern cases and produces a result from the matching arm. A `match` expression is chosen here because classifying numeric ranges into distinct category labels requires choosing among multiple possible outcomes. Using `match` handles multi-case classification directly instead of chaining multiple boolean conditional branches together. In the example code, `match task.minutes` evaluates `task.minutes` against range patterns and wildcards to return a descriptive pace string. Checking whether a `match` expression handles every possible value pattern is a key language behavior. When every case is covered, unexpected unhandled values cannot cause fallback errors or logic gaps. Including the wildcard pattern `case _ =>` accounts for any integer outside the specified numeric ranges, confirming that every potential input yields a valid outcome. ## Decision: Array, Map, or Set? Use `Array` when element order and duplicate values are meaningful, as demonstrated in this study plan. Use `Map` when key-based lookup is the primary operation. Use `Set` when element uniqueness takes precedence over position. Choose a collection based on the required observable behavior rather than current dataset size. ## Try this Add `StudyTask { title: "Review output", minutes: 5, done: true }` as the third element in the array. Which new line appears, and does `remaining` change? ### Answer The new output line is `Review output: done`. `remaining` stays at `25 minutes` because the added task is already marked complete, so the `if` body does not execute for it. ## Ready to continue when You can explain why `StudyTask` is defined as a record, why the collection uses `Array`, why `for` handles iteration, why `if` manages accumulation, and why `match` performs classification. See [Patterns & Control Flow](/en/docs/language/patterns-control-flow), [Records & Nominal Data](/en/docs/language/records-nominal-data), and [Collections & Comprehensions](/en/docs/language/collections-comprehensions) for detailed reference rules. Continue to [Failures and Resources](/en/docs/learn/failures-resources). --- route: https://topaz.ooo/v5.20/en/docs/learn/failures-resources markdown: https://topaz.ooo/docs/learn/failures-resources.md covers: §12, §13, §13a, §14 # Failures and Resources This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. **Outcome:** Represent expected absence and recoverable failure in values, guarantee scoped cleanup, and distinguish both from a runtime contract fault. **Prerequisite:** Complete [Data and Control](/en/docs/learn/data-control) and be able to trace a value through `if`, `match`, and `for`. ## Make bad input an expected result Create `failures-resources.tpz`: ```topaz record StudyTask { title: string, minutes: int, } function parseMinutes(text: string) -> Result { match toInt(text) { case Some(value) if value > 0 => Ok(value) case _ => Err("minutes must be a positive integer") } } function makeTask(title: string, minuteText: string) -> Result { let minutes = parseMinutes(minuteText)? Ok(StudyTask { title: title, minutes: minutes }) } function inspect(minuteText: string) -> () { print("input: {minuteText}") defer { print("inspection finished") } match makeTask("Build application", minuteText) { case Ok(task) => print("ready: {task.title}, {task.minutes} minutes") case Err(message) => print("cannot add task: {message}") } } inspect("25") inspect("later") ``` Check and run it: ```bash topaz check failures-resources.tpz topaz run failures-resources.tpz ``` The invalid text is handled as data, so the process completes normally: ```text output input: 25 ready: Build application, 25 minutes inspection finished input: later cannot add task: minutes must be a positive integer inspection finished ``` `toInt` returns an `Option`. `Some(value)` indicates that a parsed integer is present, while the alternative case covers absence. `parseMinutes` converts that absence and any non-positive input into a descriptive `Result`. `makeTask` also returns a `Result`. Postfix `?` unwraps `Ok` and immediately returns a compatible `Err`, so no invalid task is constructed. `inspect` handles both cases explicitly. The `defer` statement belongs to the scope that started the inspection. Its line appears after both successful and failed input, even though execution follows different branches. In a resource-owning function, deferred actions are where cleanup belongs. ## Inspect a broken contract Now create `intentional-fault.tpz`: ```topaz let tasks = ["Run first program", "Build application"] print(tasks[2]) ``` Running `topaz run intentional-fault.tpz` exits nonzero: ```text output error[TPZ4001]: index 2 is out of bounds for an array of length 2 --> intentional-fault.tpz:2:7 | 2 | print(tasks[2]) | ^^^^^^^^ ``` This is a `fault`, not an `Err` intended for recovery. The program violated array bounds. The diagnostic identifies the contract and source span instead of inventing a value. ## Decision: absence, recovery, or fault? Use `Option` when the absence of a value is an ordinary outcome. Use `Result` when the caller can respond to a meaningful error, and use `?` to propagate that error through another compatible `Result`. Use `fault` for a broken contract. Use `defer` independently whenever a scope must perform cleanup on every exit. ## Try this Add `inspect("0")` to the valid program. Which path runs, and does the deferred line still appear? ### Answer Zero fails the `value > 0` guard, so the fallback branch prints `cannot add task: minutes must be a positive integer`. The next line is still `inspection finished`, because `defer` runs when the `inspect` scope exits. ## Ready to continue when You can explain why invalid text becomes an `Err`, why `?` is valid inside `makeTask`, why the deferred line appears twice, and why out-of-bounds access is a fault rather than expected input. See [Null, Option, Result & Faults](/en/docs/language/null-option-result-faults) and [Defer & Resources](/en/docs/language/defer-resources) for exact rules. Continue to [First Application](/en/docs/learn/first-application). --- route: https://topaz.ooo/v5.20/en/docs/learn/first-application markdown: https://topaz.ooo/docs/learn/first-application.md covers: §17, §21, §22 # First Application This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. **Outcome:** Assemble a locked two-module package, run a selected test along with the package entry, and execute its Python artifact without the `.tpz` project or Topaz compiler. **Prerequisite:** Complete [Failures and Resources](/en/docs/learn/failures-resources), install `topaz`, and ensure Python 3.11 or newer is available. ## Start with the ordinary scaffold In the previous five lessons, every exercise ran out of a single source file without a project manifest. In this final guide, you build a complete multi-module package with locked dependencies and targeted build artifacts. That requires a project manifest configuration file named `topaz.toml`. `topaz.toml` serves as the project manifest configuration. It records package identity, specifies language versions, defines entry points, configures build targets, and declares dependencies. Single-file scripts do not need manifest files because they do not declare dependencies or build targets. Multi-module projects require `topaz.toml` so the compiler can resolve file trees and target settings. From the directory intended for your project, run: ```bash topaz init --root study-plan cd study-plan ``` Running `topaz init --root study-plan` creates a new project directory named `study-plan`. Inside that directory, `init` generates two initial files, `topaz.toml` and `src/main.tpz`. `init` refuses to overwrite existing projects. It creates `topaz.toml` and `src/main.tpz`, but does not create a lockfile or test file. Navigating into `study-plan` with `cd study-plan` positions your terminal inside the project root where subsequent Topaz commands expect to run. You must initialize the scaffold directory first and navigate into it before adding source files or running build tools. Replace the entry source code and add the module and selected test so the tree structure becomes: ```text tree study-plan/ ├── topaz.toml ├── src/ │ ├── main.tpz │ └── plan.tpz └── tests/ └── plan.tpz ``` Creating `src/plan.tpz` and `tests/plan.tpz` establishes a multi-file layout. The manifest `topaz.toml` sits at the project root to govern all files in `src/` and `tests/`. The scaffold manifest configuration is: ```toml [package] name = "study-plan" version = "0.1.0" language = "5.20" entry = "src/main.tpz" [build] target = "native" deterministic = true [dependencies] std = "5.20" ``` This manifest establishes project settings. `[package]` sets the package name to `"study-plan"`, the version to `"0.1.0"`, the language version to `"5.20"`, and designates `src/main.tpz` as the main application entry point. `[build]` selects the compilation target, while `[dependencies]` declares standard library requirements. ## Create the two modules Rather than keeping all code inside a single file, this project divides code across two separate modules, `src/plan.tpz` and `src/main.tpz`. Separating logic into two modules isolates reusable domain models and operations from application entry logic. This division keeps domain code decoupled from program initialization and command-line handling. `import` and `export` control visibility across module boundaries. `export` explicitly exposes selected records or functions to other modules. Symbols without `export` remain private internal implementation details. `import` pulls exposed symbols into another module scope so they can be referenced directly. `src/plan.tpz` defines the domain model and reusable operations: ```topaz export record StudyTask { title: string, minutes: int, done: bool = false, } function parseMinutes(text: string) -> Result { match toInt(text) { case Some(value) if value > 0 => Ok(value) case _ => Err("minutes must be a positive integer") } } export function makeTask( title: string, minuteText: string, done: bool = false, ) -> Result { let minutes = parseMinutes(minuteText)? Ok(StudyTask { title: title, minutes: minutes, done: done }) } export function summarize(tasks: Array) -> string { let mut remaining = 0 for task in tasks { if !task.done { remaining = remaining + task.minutes } } "tasks={tasks.length}, remaining={remaining} minutes" } ``` Only symbols marked with `export` are visible to other modules. `parseMinutes` remains an internal implementation detail. `src/main.tpz` imports those exported symbols and defines the package entry point: ```topaz import src.plan { StudyTask, makeTask, summarize } export function main(args: Array, stdin: string) -> Result { let tasks: Array = [ makeTask("Run first program", "10", true)?, makeTask("Build application", "25", false)?, ] print(summarize(tasks)) Ok(0) } ``` The explicit `main` function receives command-line arguments and standard input, returning an exit code or an error message. While this application requires neither input stream, maintaining this signature keeps the application interface clear. `src/main.tpz` imports `StudyTask`, `makeTask`, and `summarize` from `src.plan` to create sample tasks and output the summary string. With production modules established, create a dedicated test file in `tests/plan.tpz`. Adding `tests/plan.tpz`: ```topaz test-profile import src.plan { StudyTask, makeTask, summarize } match makeTask("Read syntax map", "15") { case Ok(task) => assert(task.minutes == 15, "valid minutes") case Err(message) => assert(false, message) } match makeTask("Read syntax map", "later") { case Ok(_) => assert(false, "invalid minutes were accepted") case Err(message) => assert( message == "minutes must be a positive integer", "invalid minutes", ) } let tasks: Array = [ StudyTask { title: "Run first program", minutes: 10, done: true }, StudyTask { title: "Build application", minutes: 25 }, ] assert( summarize(tasks) == "tasks=2, remaining=25 minutes", "summary", ) ``` The isolated TestHost environment provides the standard `assert(...)` function for testing. This test file validates correct input, handles invalid input, and verifies the final summary without altering production code. ## Lock, format, check, test, and run Topaz requires a resolved lockfile before running verification checks or executing code. You create the lockfile first so that all subsequent commands evaluate source code against explicit locked manifest state. Create and inspect the dependency lockfile: ```bash topaz lock --root . ``` Running `topaz lock --root .` reads `topaz.toml` in the current directory, resolves package settings, and generates a new file named `topaz.lock`. The generated `topaz.lock` file contains: ```toml [[package]] name = "study-plan" version = "0.1.0" source = "root" manifest_hash = "sha256:780ff549a9fb9b09d27c62df8ce48e70c1fc7b33d6d429e6bb826685f42e7615" ``` This file records package metadata and the calculated manifest hash. Now run the quality verification commands in sequence. You format code first to check formatting compliance, check types second to validate resolution against `topaz.lock`, and execute tests third to verify logic. Run the local quality verification loop in order: ```bash topaz fmt --check --root . topaz check --root . --locked topaz test tests/plan.tpz --root . --locked ``` `fmt --check` flags formatting differences without modifying files. `check` validates the resolved unit. The explicit path targets a single test entry point. Running these three commands verifies formatting compliance, resolves source dependencies against `topaz.lock`, and executes the assertions in `tests/plan.tpz`. The command produces the following test output: ```text output tests/plan.tpz: test-ok ``` This output confirms that all test assertions in `tests/plan.tpz` passed successfully. Once formatting, unit resolution, and tests have passed, execute the package entry point. Run the package entry point: ```bash topaz run --root . --locked ``` Running `topaz run --root . --locked` executes the entry point defined in `topaz.toml` (`src/main.tpz`) using `topaz.lock`. The command outputs: ```text output tasks=2, remaining=25 minutes ``` This output displays the evaluated task summary printed by `main`. ## Build and run without the Topaz project With local verification and package execution complete, compile the project into a target artifact. Building comes after testing and running to ensure that only verified source code is packaged into distribution files. The scaffold defaults to native compilation targets, but passing an explicit flag selects an alternative target. Build the Python distribution for this course: ```bash topaz build --target python --root . --locked --out-dir ../study-plan-product ``` Running `topaz build --target python --root . --locked --out-dir ../study-plan-product` creates the directory `../study-plan-product` and generates Python distribution files inside it. Python is selected intentionally for this step. It requires Python 3.11 or newer at runtime without requiring a Rust installation for new users. The managed artifact directory contains the following files: ```text tree study-plan-product/ ├── GENERATED-OUTPUT-NOTICE.txt ├── LICENSE ├── NOTICE ├── program.py ├── topaz-artifact.json └── topaz_py_rt.py ``` This file tree lists all generated build files in `study-plan-product/`. `program.py` contains the compiled program logic, `topaz_py_rt.py` provides runtime support modules, `topaz-artifact.json` stores artifact metadata, and legal notices complete the distribution directory. To verify that the artifact runs independently without project source code or compiler tools, move the Topaz project directory away before executing `program.py`. Move the Topaz project directory away, then execute the application from the artifact directory: ```bash cd .. mv study-plan source-unavailable cd study-plan-product python3 program.py ``` Executing `cd ..` steps out of the project directory. Running `mv study-plan source-unavailable` renames the source project directory so its files are unavailable. Entering `study-plan-product` with `cd study-plan-product` puts the shell into the artifact directory. Executing `python3 program.py` runs the compiled Python program directly using Python 3. In PowerShell, replace the move command with `Rename-Item study-plan source-unavailable` and run `python program.py`. The execution output matches the package output: ```text output tasks=2, remaining=25 minutes ``` This output matches the earlier result of `topaz run`, proving that the output artifact executes identically without source `.tpz` files. The artifact directory includes generated Python source code and runtime support modules. In this context, "without source" means the directory contains no `.tpz` files and operates independently of the Topaz project, compiler, registry, or build workspace. ## Keep the boundaries straight - A **file** refers to a single `.tpz` source file. - A **module** represents the namespace exposed by a file through `export`. - A **unit** is the set of Topaz source files resolved from an entry point and its imports. - A **package** defines product identity and locked dependencies inside `topaz.toml` and `topaz.lock`. - A **target** specifies the output compilation format, such as Python. - An **artifact** is the managed build result prepared for distribution and execution. ## Decision: source loop or product boundary? Use `check`, `test`, and `run` for fast source feedback. Use `build` for the distributable product, then execute that artifact outside the project directory. Source-tree execution validates program logic; external artifact execution validates the delivery boundary. ## Try this Modify the minute text for the second task in `src/main.tpz` from `"25"` to `"30"`. Which expectations must change before the full execution loop passes? ### Answer Executing the package entry point and the Python artifact will output `tasks=2, remaining=30 minutes`. The selected test defines its own 25-minute task fixture, so its existing summary remains valid and passes. This separation demonstrates that the test owns its test fixture rather than depending on `main`. ## Ready to continue when You can explain all six boundaries defined above, regenerate and enforce the lockfile, run `fmt --check`, `check`, the selected `test`, and `run`, and execute the Python artifact after the `.tpz` project source is moved. Use [Syntax at a Glance](/en/docs/language-tour) as your reference map, then deepen your understanding with [Modules & Visibility](/en/docs/language/modules-visibility), [Application Loop](/en/docs/guides/application-loop), and [Artifacts & Deployment](/en/docs/toolchain/artifacts-deployment). --- route: https://topaz.ooo/v5.20/en/docs/learn/reading-topaz markdown: https://topaz.ooo/docs/learn/reading-topaz.md covers: §7, §8, §13, §17 # Reading a Real File This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. **Outcome:** Open a file you did not write and locate its shape, contracts, and failure paths without reading line by line. **Prerequisite:** Complete [First Application](/en/docs/learn/first-application). Writing focused programs and reading large codebases are distinct skills, and this page focuses on the second. ## Reading is not writing in reverse Reading a large file from top to bottom is rarely the fastest way to answer a specific question. Experienced readers skim for key landmarks, construct a working mental map, and then read relevant sections closely. The same four landmark types provide guidance whenever they appear. Four passes provide quick orientation. Each pass takes seconds. ## Pass one, the imports The first lines reveal what the file depends on before you examine any other code. ```topaz import std.bytes { Bytes } import std.json ``` Imports reveal named module dependencies, while prelude operations and the selected product profile identify additional host interaction. Read all three together when mapping the host boundary; the import pass is the first map of the file. ## Pass two, the shapes Look for the nouns. A record declares a shape that values of that kind must have, often conveying the purpose of a file faster than any comment. ```topaz record StudySession { topic: string, minutes: int, finished: bool } function summarize(session: StudySession) -> string { if session.finished { return "{session.topic}: done" } "{session.topic}: {session.minutes} minutes left" } ``` Reading only the record, you already know this file handles study sessions with a topic, a duration, and a completion state. You learn the domain context without reading a single line of logic. ## Pass three, the signatures Now read function signatures and skip every body. A signature acts as a contract, and contracts compose into a cohesive narrative. In the example above, `summarize` takes a session and returns a string. The signature fixes its input and result shape. A `Result` return would also expose recoverable failure. It does not, by itself, encode every host capability. File and I/O authority also depend on the selected product profile. The signature pass provides the file's data and recoverable-failure outline. Read imports and the package profile alongside the signatures when evaluating host authority, then descend into the body relevant to your question. ## Pass four, the failure paths Last, find where the file admits things can go wrong. Look for `Result` in signatures and for the `?` mark in bodies. A function whose result type is a `Result` indicates that execution can fail and callers must handle it. The `?` mark in a body means failure at that point is passed up to the caller rather than handled here. Together, they outline the error path through the file without requiring detailed reading of error handling logic. A file with no `Result` exposes no recoverable failure through its signatures. Runtime faults and host operations are separate surfaces found in function bodies and the selected product profile. Keep the distinction between a returned error and a runtime fault. ## What you now do with a real file Run those four passes on the source of any example in this documentation, or on your own `first-application` project. Imports, shapes, signatures, failure paths. You should be able to describe the purpose of a file in one sentence before reading a single function body closely. When one of the four passes leaves you stuck, the reference explains the exact form. Shapes are in [Records & Nominal Data](/en/docs/language/records-nominal-data), signatures in [Functions & Generics](/en/docs/language/functions-generics), and failure paths in [Null, Option, Result & Faults](/en/docs/language/null-option-result-faults). ## Try this Read the `summarize` function again and answer without running it. Can code that receives only the returned string learn from its type whether the session was finished? ### Answer No. The signature promises a string in both cases, so code holding only that result must inspect the text, which is brittle. If downstream code needs the completion state, return it in a structured result rather than encoding it in a sentence. Noticing this from the signature alone, without reading the body, is the exact purpose of the third pass. ## Ready to continue when You can open an unfamiliar file, name its dependencies, its main shapes, two of its contracts, and one place it can fail, in under a minute and without reading every line. The reference material is organized the same way you just read. [Syntax at a Glance](/en/docs/language-tour) is the fastest map of the forms themselves. --- route: https://topaz.ooo/v5.20/en/docs/language-tour markdown: https://topaz.ooo/docs/language-tour.md covers: §1, §1a, §2, §3, §4, §5, §6, §7, §8, §9, §10, §11, §12, §13, §14, §15, §16, §17, §18, §19, §20, §22 # Syntax at a Glance This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. Use this page as a quick reference map rather than a full course or a condensed specification. Each card answers one practical question, presents the canonical form, and points to the manual page detailing the underlying mechanics. If you are writing your first Topaz program, start with the [Learning path](/en/docs/learn) and keep this page open for reference. ## The everyday working set Browse the cards to review syntax at a glance, or jump directly to the specific form you need. The examples are minimal by design. Complete programs and declarations stand on their own. The module card specifies the single file it assumes. ### Values, bindings, and types Values represent the data a program processes. Bind a value with `const` when it must be known at check time, `let` for an immutable local binding, or `let mut` when reassignment is part of the model. Add `: Type` when an explicit annotation clarifies the contract. Otherwise Topaz infers the type automatically. **Shape:** `let name: Type = expression` ```topaz const DEFAULT_MINUTES = 25 let title: string = "Check program" let mut completed: int = 0 completed = completed + 1 ``` **Manual:** [Types](/en/docs/language/types) ### Functions and lambdas A named function defines reusable behavior with an explicit parameter and return contract. A lambda provides a concise anonymous form for passing behavior as a value. A function body can use `return`, or evaluate its final expression as the result. **Shape:** `function name(value: Type) -> Result { expression }` and `value => expression` ```topaz function double(x: int) -> int { x * 2 } let increment: (int) -> int = x => x + 1 print("{double(increment(3))}") ``` This program prints `8`. **Manual:** [Functions & Generics](/en/docs/language/functions-generics) ### Operators and expression results Operators combine values, while blocks, `if`, `match`, and value-position `for` can produce values of their own. Inside a block, the final expression omits trailing assignments or return keywords and serves directly as the block result. **Shape:** `{ statements; finalExpression }` ```topaz let total = { let base = 20 base + 5 } print("{total}") ``` This program prints `25`. Assignment is a statement, not an expression. **Manual:** [Operators & Expressions](/en/docs/language/operators-expressions) ### `if`, `match`, and `for` Use `if` for Boolean branching, `match` when the shape or case of a value matters, and `for` to iterate over values in sequence. Here `for` appears in value position, collecting each score result into an `Array`. **Shape:** `if condition { value } else { value }`, `match value { case pattern => value }`, and `for item in values { value }` ```topaz let scores = [92, 74] let labels = for score in scores { match score { case 90..100 => "A" case _ => if score >= 70 { "pass" } else { "retry" } } } ``` `labels` evaluates to `["A", "pass"]`. **Manual:** [Patterns & Control Flow](/en/docs/language/patterns-control-flow) ### Arrays, maps, and sets Choose `Array` for ordered values, `Map` for key-to-value lookups, and `Set` for unique elements. Array element ordering is index-based. Maps and sets retain their specified insertion order. **Shape:** `[a, b]`, `map { key: value }`, and `set { a, b }` ```topaz let scores: Array = [80, 90] let ids = set { 3, 1, 3 } let labels = map { 1: "one", 2: "two" } print("{scores}:{ids.toArray()}:{labels.keys}") ``` The duplicate `3` occupies a single set slot. A map key appears only once in a literal. **Manual:** [Collections & Comprehensions](/en/docs/language/collections-comprehensions) ### Records, enums, and unions A `record` defines a product type with named fields. An `enum` defines a closed set of variants. A union written with `|` specifies that a value can match any listed type. Including `null` handles nullable data boundaries. **Shape:** `record Name { fields }`, `enum Name { variants }`, and `type Choice = A | B` ```topaz record Task { title: string, done: bool = false, } enum Filter { All, Open, } type Selection = Task | null let selected: Selection = null ``` Records and enums are nominal. Their declared identity matters, not just their visible structure. **Manual:** [Records & Nominal Data](/en/docs/language/records-nominal-data) ### Absence, recoverable failure, faults, and cleanup Use `Option` for expected absence and `Result` for recoverable errors that callers handle. Postfix `?` unwraps `Ok` or returns the underlying `Err`. `defer` ties resource cleanup to the current lexical scope. A fault is neither a keyword nor a value. It aborts execution and cannot be caught by `?`. **Shape:** `Some(value)` / `None`, `Ok(value)` / `Err(error)`, `result?`, and `defer { cleanup }` ```topaz function readPlan(path: string) -> Result { let file = open(path)? defer { file.close() } file.read() } ``` An open or read failure yields a `Result`. An invalid array index produces a runtime fault instead. **Manual:** [Null, Option, Result & Faults](/en/docs/language/null-option-result-faults) ### Strings and registry-tag templates Double-quoted string literals interpolate expressions via `{expression}`. Built-in registry tags `p`, `r`, `sh`, and `sql` preserve domain structure for paths, regular expressions, shell commands, and parameterized SQL. A tagged value does not automatically trigger its host action. **Shape:** `"text {value}"` and `tag"text {value}"` ```topaz let name = "Topaz" let greeting = "Hello, {name}!" let path = p"notes/{name}.txt" ``` Backtick templates, dollar-brace interpolation, HTML tags, and user-defined tags are not part of current Topaz syntax. **Manual:** [Strings & Templates](/en/docs/language/strings-templates) ### Modules and visibility Each `.tpz` file defines a module. A dotted module path resolves relative to the compilation root. Imports form the file prologue, and only declarations marked `export` are visible to external modules. **Shape:** `import module.path { Name }` and `export function name(...)` Assume `src/plan.tpz` exports `StudyTask` and `summarize`: ```topaz import src.plan { StudyTask, summarize } export function summary(tasks: Array) -> string { summarize(tasks) } ``` The explicit import exposes only the named declarations. It does not expose private items of the module. **Manual:** [Modules & Visibility](/en/docs/language/modules-visibility) ### Format, check, test, run, and build The toolchain applies a consistent source and module model across development and distribution. Format source code first, verify that static checks pass, run tests, observe execution via the interpreter, and build the target binary or package. **Shape:** `topaz [inputs]` ```bash topaz fmt --check --root . topaz check --root . --locked topaz test tests/plan.tpz --root . --locked topaz run --root . --locked topaz build --target python --root . --locked --out-dir build ``` `check` produces no artifact. `run` executes immediately. `build` generates a managed build target and may require the selected target toolchain. **Manual:** [CLI & Diagnostics](/en/docs/toolchain/cli) ## Advanced routes These forms become relevant once you are comfortable with the core working set. They are intentionally presented with less prominence than the primary cards. Follow only the paths your application requires. - **Pipelines** — trace left-to-right value flow in [Pipelines & Placeholders](/en/docs/language/pipelines-placeholders). - **Comprehensions** — build collections with nested selection in [Collections & Comprehensions](/en/docs/language/collections-comprehensions). - **Protocols** — learn static conformance alongside nominal declarations in [Records & Nominal Data](/en/docs/language/records-nominal-data). - **Generics** — add one rank-1 type parameter when ordinary functions are not sufficient in [Functions & Generics](/en/docs/language/functions-generics). - **Concurrency** — join named work with a declared timeout in [Concurrency](/en/docs/language/concurrency). - **Files and resources** — cross an explicit host boundary in [Files & Resources](/en/docs/stdlib/files-resources). - **Web and service products** — choose a browser, offline, or managed HTTP service path in [Application Loop](/en/docs/guides/application-loop). - **Bytes and media** — work with immutable `Bytes`, mutable `ByteBuffer`, hashes, and codecs in [Core Library](/en/docs/stdlib/core). This page serves as an unnumbered reference guide. Return to the [Learning path](/en/docs/learn) when you want concepts, exercises, and your first complete application presented in dependency order. --- route: https://topaz.ooo/v5.20/en/docs/guides/application-loop markdown: https://topaz.ooo/docs/guides/application-loop.md covers: §13a, §17, §21, §22 # Application Loop This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. Use this page after [First Application](/en/docs/learn/first-application). The authoring loop is shared, but the delivery loop is not. Choose a single product target instead of building every target sequentially. - Choose **native** for a single platform-specific executable with no language runtime at deployment. - Choose **Python** when Python 3.11 or newer is already present in the deployment environment. - Choose **Web Application** when Topaz manages the browser model, messages, update function, and view. - Choose **HTTP service** when a checked request handler runs behind a process supervisor and, for public traffic, a reverse proxy. Raw Web and Worker packages serve as integration products for an existing JavaScript host. They are covered in [WASM & Playground](/en/docs/toolchain/wasm-playground). ## Shared authoring loop Start from a checked two-module package. A current native package manifest uses the following layout. Save it as `topaz.toml`: ```toml [package] name = "task_summary" version = "0.1.0" language = "5.20" entry = "src/main.tpz" [build] target = "native" deterministic = true [dependencies] std = "5.20" ``` The helper module handles the application calculation: ```topaz export function remainingMinutes(values: Array) -> int { let mut total = 0 for value in values { total += value } total } ``` The entry module converts the result into an observable command: ```topaz import src.summary { remainingMinutes } export function main(args: Array, stdin: string) -> Result { let minutes = remainingMinutes([10, 25]) print("remaining={minutes}") Ok(0) } ``` Generate `topaz.lock` once, then use the same sequence for routine work: ```bash topaz lock --root task-summary topaz fmt --check --root task-summary topaz check --root task-summary --locked topaz test --root task-summary --locked topaz run --root task-summary --locked ``` `fmt --check` reports formatting drift without modifying files. A failed `check` or `test` halts before a product artifact is written. Fix the first Topaz diagnostic and repeat the command. Use `topaz explain TPZ####` when a diagnostic code requires a detailed explanation. ## Path A: native or Python command application Native builds require a Rust toolchain for the target operating system and architecture. Python builds do not require Rust, but the resulting product requires Python 3.11 or newer. Build only the product intended for delivery: ```bash topaz build --release --root task-summary --locked --out-dir native-product topaz build --target python --root task-summary --locked --out-dir python-product ``` The release tree structures are: ```text output tree native-product/ ├── GENERATED-OUTPUT-NOTICE.txt ├── LICENSE ├── NOTICE ├── target │ └── release │ └── program[.exe] └── topaz-artifact.json python-product/ ├── GENERATED-OUTPUT-NOTICE.txt ├── LICENSE ├── NOTICE ├── program.py ├── topaz-artifact.json └── topaz_py_rt.py ``` `program[.exe]` specifies `program` on Unix and `program.exe` on Windows. Copy the selected directory completely to a new location and run from its root: ```bash ./target/release/program python3 program.py ``` Use the first command for native products and the second for Python products. They are alternative paths. If the application declares `[capabilities.fs]`, place its `data/` and writable output directories alongside the product, using that directory as the working directory. Application data is not added to native or Python products automatically. If a build reports a missing Rust toolchain, either install the matching Rust toolchain or choose Python. If an output directory contains another target or a modified managed file, preserve it for inspection and build into a new empty directory. ## Path B: managed Web Application A Web Application build requires Rust configured with the `wasm32-unknown-unknown` target. Create the checked scaffold instead of converting the command entry point: ```bash topaz init --target web-app --root hello-web topaz lock --root hello-web topaz fmt --check --root hello-web topaz check --root hello-web --locked topaz test hello-web/tests/app.tpz --root hello-web --locked topaz dev --root hello-web --port 8000 topaz build --root hello-web --locked --release --out-dir web-product ``` The generated entry point uses the checked lifecycle: ```topaz import std.dom { Html, WebAppEvent, WebAppStep, text } export record Model { message: string, } export enum Msg { Ready, } export function init() -> WebAppStep { WebAppStep { model: Model { message: "Hello from Topaz" }, commands: [] } } export function update(model: Model, message: Msg, event: WebAppEvent) -> WebAppStep { WebAppStep { model: model, commands: [] } } export function view(model: Model) -> Html { text(model.message) } ``` `topaz dev` runs a loopback development server and retains the last valid product after a failed edit. It is not a production server. The final directory contains HTML, the checked application host, the raw Web facade, WASM, capability metadata, declared styles and assets, and the standard license and artifact files. Copy the complete directory to a static host and serve it over HTTP. Opening ES modules through `file://` is not a valid deployment path. [Data Lens](/en/docs/guides/data-lens) is the maintained multi-module example for this path. ## Path C: HTTP service Create a service scaffold when the product is a request handler rather than a command or browser UI: ```bash topaz init --target http-service --root hello-service topaz lock --root hello-service topaz fmt --check --root hello-service topaz check --root hello-service --locked topaz test --root hello-service --locked topaz dev --root hello-service --port 8080 topaz build --root hello-service --locked --release --out-dir service-product ``` The handler boundary is explicit and synchronous: ```topaz import std.http { HttpRequest, HttpResponse, text } export function handle(req: HttpRequest) -> HttpResponse { if req.url.path() == "/health" { return text(200, "ok") } text(404, "not found") } ``` The service product contains one native executable, embedded fixed settings, third-party notices, and standard artifact files. It binds to loopback by default. A non-loopback bind requires an explicit process argument. TLS, HTTP/2, outbound networking, and a general Web framework are outside this product. [HTTP Service](/en/docs/guides/http-service) completes this path, including budgets, observable failures, source-free execution, and deployment recovery. ## Related documentation - [First Application](/en/docs/learn/first-application) - [Data Lens](/en/docs/guides/data-lens) - [HTTP Service](/en/docs/guides/http-service) - [Packages & Distribution](/en/docs/toolchain/packages) - [Artifacts & Deployment](/en/docs/toolchain/artifacts-deployment) --- route: https://topaz.ooo/v5.20/en/docs/guides/lispex-application markdown: https://topaz.ooo/docs/guides/lispex-application.md covers: §0, §20, §21 # Run Lispex Rules This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. Some decisions do not belong directly in your source code. Business logic such as request approvals, customer tier classifications, or claim scoring change on their own schedule and are often written by external teams. This guide demonstrates how to isolate such a rule in its own file, supply an input, and receive a decision without allowing the rule to access any other system resources. The rules are written in [Lispex](/en/docs/interop/lit), a deterministic Lisp designed for decision logic where a given rule and input always yield the identical result. Topaz reads each rule during package compilation and follows a prepare-once/evaluate-many runtime model. A prepared rule cannot access files, read the system clock, or connect to the network, and it terminates once it reaches the allocated execution limit. A package built in this manner constitutes a complete-current-profile Lispex decision application, which this page constructs step by step. This page assumes prior experience building a Topaz package. If you are new to Topaz, start with [First Application](/en/docs/learn/first-application). ## Write the rule A rule is a standard Lispex source file. The following example compares two numbers and returns one of two string values. Save it as `rules/approve.lspx`: ```lispex (if (< 10 15) "allow" "deny") ``` Three additional rules complete the application package below. The file `rules/classify.lspx` returns a numeric value rather than a string: ```lispex (+ 20 22) ``` The rule `rules/deadline-probe.lspx` executes indefinitely on its own, providing a workload for the application deadline mechanism to terminate: ```lispex (letrec ((loop (lambda () (loop)))) (loop)) ``` The fourth rule reuses `rules/approve.lspx` under a much lower limit, requiring no separate source file. ## Set two kinds of limit Two distinct documents define resource ceilings for execution. A limits document applies to an individual rule, while a quotas document applies to the entire application. The file `rules/approve.limits.json` defines the resource ceilings for preparing and evaluating a single rule. A complete document must retain every field, and while lower values may be specified, fields cannot be omitted or added. The following snippet illustrates the two work ceilings: ```json { "schema": "topaz.lispex-embed-limits/v1", "prepare": { "prepare_work": 1000000 }, "evaluate": { "eval_work": 10000 } } ``` The `prepare_work` and `eval_work` fields measure evaluator computational effort deterministically rather than tracking elapsed wall-clock time, ensuring that identical rules and inputs consume identical resource units across different host environments. Every rule requires a dedicated limits document. Both `rules/classify.limits.json` and `rules/deadline-probe.limits.json` share this structural format. The document `rules/semantic-limit-probe.limits.json` uses the same structure with `eval_work` set to `1`, an intentionally insufficient threshold that prevents the rule from completing. This setup allows the application to demonstrate how resource exhaustion is handled. The file `rules/application.quotas.json` imposes constraints on the host runtime rather than an individual rule. It permits up to two concurrent evaluations, two queued evaluations, 64 total evaluations, and a finite wall-clock timeout. The distinction between limits and quotas becomes critical when execution is interrupted. A rule exceeding its `eval_work` ceiling reaches a semantic limit failure, returning a result type that application code can inspect and pattern-match. Conversely, an application exceeding its global quota rejects the invocation entirely. Because these represent distinct typed outcomes, application logic must distinguish between them. ## Declare the package The manifest configuration connects all components of the application. It selects the Topaz language version and standard library, specifies the native delivery target, declares the complete current Lispex profile and application contract, references the quotas document, and registers each rule. Save it as `topaz.toml`: ```toml [package] name = "complete_lispex_decision_application" version = "0.1.0" language = "5.20" entry = "src/main.tpz" [build] target = "native" deterministic = true [dependencies] std = "5.20" [lispex] profile = "lispex/r7rs-rule-current-profile-bounded/1" application = "topaz/lispex-decision-application/2" application_quotas = "rules/application.quotas.json" [[lispex.rule]] name = "approve" source = "rules/approve.lspx" limits = "rules/approve.limits.json" [[lispex.rule]] name = "classify" source = "rules/classify.lspx" limits = "rules/classify.limits.json" [[lispex.rule]] name = "deadline_probe" source = "rules/deadline-probe.lspx" limits = "rules/deadline-probe.limits.json" [[lispex.rule]] name = "semantic_limit_probe" source = "rules/approve.lspx" limits = "rules/semantic-limit-probe.limits.json" ``` The `language` and `std` settings are prescribed by the application contract rather than user-selected, causing toolchains operating in a different language mode to reject the package during preparation. Refer to [Toolchain Status](/en/docs/toolchain/status) for details on the compiler, language mode, and runtime included in an installation. The `profile` key selects the complete-current-profile evaluator identified by `lispex/r7rs-rule-current-profile-bounded/1`. In this provider-owned identifier, `bounded` names the fixed resource envelope. The embedded-core compatibility profile uses its own component and contract identity. The `application` key selects the 5.20 delivery contract. Both values are fixed identifiers. Each `[[lispex.rule]]` entry defines three standard fields. The `name` field maps to a generated function within `std.lispex.rules`, turning `approve` into `rules.approve()`. This identifier is neither a runtime file path nor a component selector. Multiple rule entries can reference the same source file—as demonstrated by `approve` and `semantic_limit_probe`—while applying distinct execution limits. ## Lock the package Locking prepares each rule once and records the exact compilation artifacts. Execute the command from the package root: ```bash topaz lock --root . ``` Never enter a component or prepared-artifact digest manually. Inspect `topaz.lock` prior to committing it to source control. Its `[lispex]` section locks the profile, application contract, component, evaluator, ABI, value codec, meter, artifact contract, adapter, quotas, target disposition, and catalog of generated handles. Each `[[lispex.rule]]` row locks the corresponding source, limits, preparation request, submission, and compiled artifact. The component represents the evaluator itself. The lock pins its state via a cryptographic digest to prevent subsequent builds from silently executing a modified component. Modifying the manifest, a rule source, a limits document, application quotas, the component, or the profile requires regenerating the lock file. Running a build with the `--locked` flag rejects any configuration drift instead of silently preparing modified rules. ## Call a rule from Topaz code The lock operation generates the core API within `std.lispex` alongside a dedicated function for each declared rule inside `std.lispex.rules`. ```topaz let settlement = evaluate(rules.approve(), input, defaultLimits(rules.approve())) let recorded = evaluateWithEvidence( rules.approve(), input, defaultLimits(rules.approve()), ) ``` The `evaluate` function returns a settlement value representing the typed result of an evaluation. This settlement indicates whether the rule executed to completion, encountered an internal evaluation failure, or exhausted a resource limit. The `defaultLimits` function retrieves the execution ceilings recorded in the lock file, preventing calls from requesting higher resource limits than declared by the package. The `input` parameter must be a canonical value using a shared byte encoding scheme between the host and evaluator to ensure consistent semantics. Each evaluation initializes a clean guest memory state, global variables, execution meters, and transcript data, ensuring that state is not shared between consecutive rule evaluations. Use `evaluate` when only the evaluation result is required. Use `evaluateWithEvidence` when execution must produce a verifiable audit record alongside the deterministic result. ## Store, verify, and replay the evidence When `evaluateWithEvidence` resolves to an eligible outcome, it generates a consumer artifact. This artifact encapsulates the evaluation execution, allowing it to be stored, shared, and independently validated without relying on the host application that generated it. The complete lifecycle consists of six calls: 1. `consumerArtifactBytes` serializes the artifact into a byte payload suitable for storage. 2. `consumerArtifactFromBytes` deserializes stored bytes while performing validation checks. 3. `inspectConsumerArtifact` inspects artifact metadata and identities without performing code execution. 4. `verifyConsumerArtifact` validates internal structure, cryptographic digests, and parameter bindings. 5. `portableCoreBytes` extracts the embedded Lispex-formatted core from the artifact if present. 6. `freshReplay` re-evaluates the locked rule with the identical input in an isolated guest environment, validating that it produces a matching artifact. Be careful when evaluating assertions derived from an artifact. The artifact and its portable core are produced client-side without cryptographic authentication. They do not specify an issuing identity, provider authorization, signing authority, component admission, or execution permissions for external actions. Furthermore, operational refusals, cancellations, safety preemptions, and engine failures do not generate a portable core. ## Check and run the package Validate the package before running execution tests, executing both operations against the locked configuration: ```bash topaz check --root . --locked topaz run --root . --locked -- all ``` Passing the `all` argument runs a test scenario that exercises the entire application boundary in a single pass. It processes 24 standard inputs, verifies state isolation between rules, triggers semantic resource exhaustion in one rule, and confirms that the application rejects the 65th evaluation attempt after reaching its quota of 64. The reference package implementation produces the following single-line output for `all`: ```text all:default:24:evidence:verified:replayed:isolation:2:complete:semantic-limit:exhausted:aggregate-quota:64:refused ``` Any differing output text or a non-zero exit code signifies a test failure. The `deadline_probe` rule is intentionally excluded from the `all` test suite. Release validation tests handle timeout behaviors separately by duplicating the package, reducing the wall-clock quota from 100 milliseconds to 1 millisecond, updating the lock file, and asserting a `DeadlineExceeded` result across both interpreter and native release builds. The standard 100-millisecond configuration must then execute successfully. Isolating this test ensures that the output line above remains independent of host CPU performance. ## Build the native product Compile an optimized native executable into an output directory located outside the package root: ```bash topaz build --root . --locked --release --out-dir ../complete-product ``` Execute the binary on the same operating system and hardware architecture used for the build: ```bash ../complete-product/target/release/program all ``` On Windows platforms, execute: ```powershell ..\complete-product\target\release\program.exe all ``` The native executable must produce the identical output line. A successful build does not make the generated executable portable across different target architectures. ## Where the finished application can run The application contract specifies a fixed set of supported deployment target routes. Any rejected route is denied prior to code generation or execution, without falling back to the interpreter or native target. | Route | Disposition | | ------------------------- | ----------------------------------------------------- | | `interpreter` | Supported for the locked complete-profile application | | `native` | Supported only on admitted native release targets | | `generated-python` | Refused before artifact output | | `raw-web` | Refused before artifact output | | `worker-web` | Refused before artifact output | | `managed-web` | Refused before artifact output | | `http-service` | Refused before artifact output | | `no-capability` | Refused before execution or artifact output | | `mcp-empty-component-set` | Refused before execution | The Topaz 5.18 route remains available for existing packages through the embedded-core compatibility profile. It retains its own component, profile, contract, and prepared-artifact identity. The complete-current-profile application uses the identifiers shown in this guide, and selectors never substitute one route for the other. ## Related documentation - [Lispex Evaluator and LIT](/en/docs/interop/lit) - [Interop Boundaries](/en/docs/interop/overview) - [Rust Backend](/en/docs/interop/rust) - [Python Backend](/en/docs/interop/python) - [Packages & Distribution](/en/docs/toolchain/packages) --- route: https://topaz.ooo/v5.20/en/docs/guides/data-lens markdown: https://topaz.ooo/docs/guides/data-lens.md covers: §3, §7, §13a, §17, §21, §22 # Data Lens This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. Data Lens is a maintained multi-module web application example. It opens user-selected CSV or JSON files, parses and filters rows locally, displays typed summaries, exports text, and persists only view preferences. Imported documents and parsed rows are neither sent to a server nor stored in durable browser state. Complete [First Application](/en/docs/learn/first-application) before reading this guide. You should already be comfortable with modules, `Result`, tests, `topaz.lock`, and the `--locked` flag. Building the final browser product requires Rust with the `wasm32-unknown-unknown` target. ## Why this is a Web Application Choose `web-app` when Topaz owns the model, messages, update function, and view. Choose raw `web` or `web-worker` only when an existing JavaScript host owns the UI or worker protocol. The Playground is designed for quick source experiments, not for deploying Data Lens. The maintained source is organized by responsibility rather than contained in a single entry file: ```text output data-lens/ topaz.toml topaz.lock src/main.tpz src/model.tpz src/session.tpz src/view.tpz tests/application.tpz tests/exporting.tpz tests/parsing.tpz tests/transform.tpz styles/app.css registry/lens_labels/1.0.0/... vendor/lens_labels/1.0.0/... data-lens-core/ topaz.toml src/lib.tpz ``` `data-lens-core` handles CSV and JSON parsing, filtering, sorting, summaries, and text export. The Web package manages browser messages, local-state encoding, and the view. `lens_labels` is a locked registry dependency copied into `vendor`. ## Model the data before the UI The application model keeps source text, controls, typed rows, summary state, and visible errors separate: ```topaz import data_lens_core { DataFormat, DataRow, SortMode, Summary } export record Model { source: string, query: string, activeOnly: bool, format: DataFormat, sortMode: SortMode, rows: Array, summary: Summary, status: string, error: Option, } ``` Parsing returns a `Result` containing a stable error location such as `JSON $[0].category: missing field`. The update function stores the message in `error`, which the view renders as plain text. Source content is never inserted with `innerHTML`. The lifecycle initializes with an in-memory sample and requests only saved view preferences: ```topaz export function init() -> WebAppStep { let base = Model { source: CSV_SAMPLE, query: "", activeOnly: false, format: DataFormat.Csv, sortMode: SortMode.ByName, rows: [], summary: emptySummary(), status: "Ready", error: None, } WebAppStep { model: applyModel(base), commands: [loadState("data-lens-session-load", "session", Msg.LocalCompleted)], } } ``` Explicit user selection supplies the document as UTF-8 text. Saving a session writes the query, active-row filter, and sort mode. User cancellation, malformed UTF-8, unavailable storage, quota exhaustion, and corrupt saved state remain typed application outcomes. ## Shortest maintained task path Create the registry input and sibling core package while authoring the application, then run the vendor workflow once. `vendor` verifies and copies the registry dependency while writing the lock file. Do not run `lock` first for a registry dependency that has not been vendored. ```bash topaz vendor --root data-lens --from data-lens/registry topaz fmt --check --root data-lens topaz check --root data-lens --locked topaz test data-lens/tests/parsing.tpz --root data-lens --locked topaz test data-lens/tests/transform.tpz --root data-lens --locked topaz test data-lens/tests/application.tpz --root data-lens --locked topaz test data-lens/tests/exporting.tpz --root data-lens --locked topaz dev --root data-lens --port 8000 topaz build --root data-lens --locked --release --out-dir data-lens-product ``` `topaz run` is the direct execution path for command entries containing `main`. Because Data Lens follows a Web lifecycle, its observable development command is `topaz dev`. In the browser, verify CSV and JSON format handling, Unicode labels and filenames, filtering, both sort modes, text export, preference state management (saving, reloading, and resetting), cancellation, malformed document handling, and recovery after a failed edit. `topaz dev` continues serving the last successful product until the source is valid again. ## Product and run without the Topaz package source The managed product directory contains: ```text output tree data-lens-product/ ├── GENERATED-OUTPUT-NOTICE.txt ├── LICENSE ├── NOTICE ├── index.html ├── styles │ └── app.css ├── topaz-app.js ├── topaz-artifact.json ├── topaz-web-capabilities.json ├── topaz-web.d.ts ├── topaz-web.js └── topaz-web.wasm ``` Copy the entire directory to a fresh static root and serve it over HTTP. The product does not require the package source, Topaz CLI, registry, npm runtime, CDN, or a data service. If ES modules or WASM fail under `file://`, use a local static HTTP server. If the build rejects the output directory because a managed file changed or another target owns it, keep the directory intact and choose a new empty `--out-dir`. ## Related documentation - [First Application](/en/docs/learn/first-application) - [Application Loop](/en/docs/guides/application-loop) - [Files & Resources](/en/docs/stdlib/files-resources) - [Packages & Distribution](/en/docs/toolchain/packages) - [Artifacts & Deployment](/en/docs/toolchain/artifacts-deployment) --- route: https://topaz.ooo/v5.20/en/docs/guides/http-service markdown: https://topaz.ooo/docs/guides/http-service.md covers: §3, §7, §13a, §17, §21, §22 # HTTP Service This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. Choose `http-service` when a checked Topaz function receives an `HttpRequest` and returns an `HttpResponse`. The generated native host owns the listener, HTTP/1.1 framing, finite request budgets, overload responses, logging, and shutdown. This is not a general web server framework. It does not grant outbound networking, raw sockets, ambient environment access, shared mutable Topaz state, TLS, HTTP/2, WebSockets, or sessions. Place a standard reverse proxy in front of the process for public TLS and Internet-edge policy. Treat this explicit `Capability` boundary as part of the product design. Complete [First Application](/en/docs/learn/first-application) first. Building the service requires a Rust toolchain for the target deployment platform. ## Create, lock, and test The scaffold supplies the package manifest, service defaults, and a concrete handler: ```bash topaz init --target http-service --root hello-service topaz lock --root hello-service topaz fmt --check --root hello-service topaz check --root hello-service --locked topaz test --root hello-service --locked ``` The entry module exports exactly one handler with this shape: ```topaz import std.http { HttpRequest, HttpResponse, text } export function handle(req: HttpRequest) -> HttpResponse { if req.method == "GET" && req.url.path() == "/health" { return text(200, "ok") } text(404, "not found") } ``` The checker rejects missing, generic, variadic, defaulted, or differently typed handlers before the listener can start. ## Develop through loopback Run the generated service on loopback and request its health route: ```bash topaz dev --root hello-service --port 8080 curl --fail-with-body http://127.0.0.1:8080/health ``` `topaz dev` forces the bind address to `127.0.0.1`, rebuilds the managed service, and forwards interruption signals to it. If port 8080 is occupied, choose another loopback port with `--port`. ## Keep finite service budgets The scaffold records fixed service settings under `[service]`: ```toml [service] bind = "127.0.0.1" port = 8080 workers = 1 max_connections = 64 queue_capacity = 32 max_target_bytes = 8192 max_header_bytes = 16384 max_headers = 64 max_body_bytes = 1048576 header_timeout_ms = 5000 body_timeout_ms = 5000 handler_timeout_ms = 1000 shutdown_grace_ms = 5000 log_format = "text" ``` Unknown keys and out-of-range values fail during package loading. Each request receives a fresh runtime context and module graph. Requests use separate Topaz heaps. Queue saturation, oversized inputs, handler deadlines, and invalid responses produce the declared host outcomes. ## Build and inspect the product ```bash topaz build --root hello-service --locked --release --out-dir hello-service-product cd hello-service-product ./target/release/program --print-config ./target/release/program ``` On Windows, run `target\release\program.exe`. The product tree is: ```text output tree hello-service-product/ ├── GENERATED-OUTPUT-NOTICE.txt ├── LICENSE ├── NOTICE ├── THIRD-PARTY-NOTICES.txt ├── target │ └── release │ └── program[.exe] ├── topaz-artifact.json └── topaz-service-config.json ``` `program[.exe]` denotes `program` on Unix and `program.exe` on Windows. `--print-config` validates and prints effective settings without opening a listener. The managed JSON file records embedded defaults. Runtime overrides require explicit command-line flags. An explicit non-loopback bind is configured as follows: ```bash ./target/release/program --bind 0.0.0.0 --port 8080 ``` Use that flag only behind the intended network boundary. The manifest itself may specify only a loopback address. ## Observable failures - Oversized bodies, targets, or header sets produce the declared HTTP errors where framing permits a response. - A full handler queue returns `503`. A handler deadline returns `504`. - A runtime fault or invalid application response returns a generic `500` without exposing source paths, request content, or Topaz fault text. - Responses and host logs share a request ID. Logs omit targets, bodies, and header values. - `SIGINT` and, on Unix, `SIGTERM` stop admission and drain existing work until `shutdown_grace_ms`. If `check` or `test` fails, resolve the Topaz diagnostic and repeat the command. If a build detects a modified managed file or another target in the output directory, retain that directory for inspection and choose a new empty `--out-dir`. ## Source-free deployment Copy the complete `hello-service-product/` directory so the executable, configuration record, integrity manifest, licenses, and notices stay together. The copied product requires neither package source files, the Topaz CLI, a registry, nor the temporary Cargo workspace. Run it under a process supervisor, retain the generated budgets, and terminate public TLS at a reverse proxy. ## Related documentation - [First Application](/en/docs/learn/first-application) - [Application Loop](/en/docs/guides/application-loop) - [Packages & Distribution](/en/docs/toolchain/packages) - [Artifacts & Deployment](/en/docs/toolchain/artifacts-deployment) --- route: https://topaz.ooo/v5.20/en/docs/language/lexical-layout markdown: https://topaz.ooo/docs/language/lexical-layout.md covers: §1, §1a # Lexical Structure & Layout This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. Consult this page when a `.tpz` file appears syntactically valid line by line, but item boundaries or identifier and literal spellings remain uncertain. Topaz accepts Unicode names and evaluates completed lines as finished statements. Parentheses, brackets, and continuation tokens permit expressions to span multiple lines without an explicit line-continuation character. ## Start with a complete source file Save this as `lexical-layout.tpz`: ```topaz let ready = true let archived = false let missing = null let 상태 = "ready" let total = 40 + 2 print("{상태}: {ready}, {archived}, {missing}, {total}") ``` Check and run it: ```bash topaz check lexical-layout.tpz topaz run lexical-layout.tpz ``` The output is: ```text output ready: true, false, null, 42 ``` Across these five bindings, the program demonstrates four literal categories: boolean values `true` and `false`, the absence value `null`, a string, and decimal numbers. `상태` functions as a standard identifier. Keywords such as `let`, `true`, `false`, and `null` retain their English spellings, whereas custom identifiers can incorporate Unicode letters, underscores, trailing digits, and emoji. ## Read the line breaks from what the parser can see The first four bindings each complete on a single line, allowing each newline to separate distinct top-level items. The `total` expression continues because `=` and `+` appear at line endings. A trailing operator signals to the parser that another operand follows. Within parentheses, brackets, record braces, parameter lists, and other delimited constructs, newlines function as whitespace rather than item separators. Blocks, `match` bodies, and `concurrent` bodies follow different rules. Because their contents consist of statement lists, a completed line can conclude an item. Wrap complex expressions in parentheses to resolve potential ambiguity rather than relying on indentation. Indentation aids readability, whereas delimiter and continuation rules govern structural parsing. | You want to… | Prefer | | ------------------------------------------------ | -------------------------------------------------- | | End a complete top-level item or block statement | Put the next item on a new line | | Continue arithmetic or a pipeline | Leave the operator at the end of the previous line | | Break an argument or collection list | Keep the items inside its delimiters | | Remove any doubt about grouping | Add parentheses | Semicolons serve as valid statement separators where statements are permitted, though standard Topaz code omits them. A newline preceding `else` remains part of the surrounding `if` or timed `concurrent` expression. ## Names, comments, and strings An identifier begins with a Unicode letter, `_`, or emoji, followed by optional Unicode digits. The standalone `_` is reserved. It functions as a pattern wildcard and a pipeline argument placeholder rather than a readable value or bindable name. Identifiers such as `_internal` operate as standard names. Use `//` for line comments and `/* ... */` for block comments. Block comments do not nest. Integer and floating-point literals use decimal notation. Exponent notation such as `1e21` is not part of the surface, so write the digits in full or compute the value. Alternate numeric bases and underscore digit separators are not canonical. Double-quoted strings support escape sequences such as `\n` alongside `{expression}` interpolation. Triple-quoted strings provide multiline formatting. Their closing delimiter sets the exact space-and-tab prefix removed from nonblank lines. Topaz preserves Unicode scalar sequences without implicit normalization. ## Common correction Do not place a binary operator at the start of a statement line when it could be parsed as a unary `+` or `-`. Keep `+` or `-` at the end of the preceding line, or enclose the entire expression in parentheses. Avoid using `s[i]`, `s.length`, or slice syntax for text manipulation. Inspect strings using `s.scalars()`. The [Strings & Templates](/en/docs/language/strings-templates) page details this design approach. Token recognition follows the longest valid match rule. Consequently, `...`, `..`, `..<`, `?.`, and postfix `?` remain distinct. Single-quoted strings, character literals, nested block comments, string slicing, grapheme-cluster APIs, and implicit Unicode normalization are excluded from the current surface. Consult [Operators & Expressions](/en/docs/language/operators-expressions) for value-producing expressions and [Types](/en/docs/language/types) for annotations and inference. Refer to [Forbidden & Deferred Forms](/en/docs/language/forbidden-deferred) when a familiar syntax pattern is rejected. --- route: https://topaz.ooo/v5.20/en/docs/language/operators-expressions markdown: https://topaz.ooo/docs/language/operators-expressions.md covers: §2, §5, §11, §12, §13 # Operators & Expressions This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. Use an expression when you need a value. Arithmetic, comparison, function calls, and `if` or `match` constructs can all produce values. Assignment serves a different role. It mutates an existing location and is a statement, not a value embedded within another expression. ## Start with a value you can observe Save this as `operators-expressions.tpz`: ```topaz function score(base: int, bonus: int) -> int { let doubled = base * 2 if bonus > 0 { doubled + bonus } else { doubled } } let result = score(20, 2) let exact = result == 42 let changed = result != 41 print("{result} {exact} {changed}") ``` Check and run it: ```bash topaz check operators-expressions.tpz topaz run operators-expressions.tpz ``` The output is: ```text output 42 true true ``` `base * 2` evaluates before `+ bonus`, and the selected `if` branch becomes the function result. `==` determines whether two comparable values are equal, while `!=` determines whether they are unequal. Neither operator mutates a binding. ## Choose an operator by its job | Need | Form | Important distinction | | -------------------- | ---------------------------------- | ----------------------------------------------------------- | | Arithmetic | `+`, `-`, `*`, `/`, `%`, `**` | Numeric operands remain in the same `int` or `float` domain | | Ordering or equality | `<`, `<=`, `>`, `>=`, `==`, `!=` | Only comparable types are permitted | | Membership | `value in collection` | A Map lookup uses its keys, not `value in map` | | Conditional work | `left && right`, `left \|\| right` | The right side executes only when needed | | Default for absence | `value ?? fallback` | Coalesces a nullable or optional value | | Result propagation | postfix `value?` | Returns an `Err` from the enclosing function, unlike `??` | | Left-to-right flow | `value \|> next` | Uses pipeline rules rather than ordinary binary arithmetic | | Mutation | `target = value`, `+=`, `??=` | A statement that does not produce a value | `&&` evaluates its right side only when the left side is `true`. `||` evaluates its right side only when the left side is `false`. This short-circuit behavior guards work that is valid only after a preceding condition succeeds. `??` likewise skips its fallback when the left side already contains a value. Postfix `?` differs as well. The [Null, Option, Result & Faults](/en/docs/language/null-option-result-faults) page explains its enclosing-function boundary. ## Numeric and equality boundaries `int` is a signed 64-bit integer. Integer division truncates toward zero, and the remainder has the sign of the dividend. Integer overflow, division by zero, and a dynamically negative integer exponent produce runtime faults. The same invalid operation in a constant expression produces a static error. `float` uses IEEE-754 binary64, so floating division by zero and NaN follow that model. Topaz does not implicitly convert between `int` and `float`. Primitive values, compatible records, Arrays, Options, Results, and nominal values are comparable only when their contained types are comparable. Array equality observes length and element order. Map and Set values, functions, resources, and template values are not comparable. Nominal equality requires matching enum, record, or newtype identities. Precedence places calls and member access above exponentiation, unary operators, multiplication, addition, ranges, comparisons, `&&`, `||`, `??`, composition, and finally `|>`. Memorizing the entire hierarchy is rarely necessary. Use parentheses whenever grouping clarifies intent. ## Common correction Do not write `count++`, `count--`, or `count **= 2`. These forms are not valid in Topaz. For a mutable binding, write an explicit statement such as `count = count + 1`. Do not use `=` when evaluating equality. `=` mutates a target, whereas `==` evaluates to a boolean value. Bitwise operators, implicit numeric promotion, floating remainder as an operator, operator overloading, and user-defined operators are outside the current surface. Function composition exists only at the specification level for this documentation profile. Current examples use named functions or `|>`. Continue with [Types](/en/docs/language/types) for the domains operators accept, [Patterns & Control Flow](/en/docs/language/patterns-control-flow) for value-producing branches, and [Pipelines & Placeholders](/en/docs/language/pipelines-placeholders) for `|>`. --- route: https://topaz.ooo/v5.20/en/docs/language/types markdown: https://topaz.ooo/docs/language/types.md covers: §3, §4, §5, §7, §22 # Types This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. Types allow the checker to answer a practical question before program execution. Which values may cross a given boundary? Most local values do not require an explicit type annotation. Add one when it clarifies an important input, return value, or domain choice. ## Start with one checked program Save this file as `types.tpz`: ```topaz type Stage = "draft" | "ready" function describe(stage: Stage, checks: int) -> string { "{stage} after {checks} checks" } let checks = 3 let current: Stage = "ready" let render: (Stage, int) -> string = describe print(render(current, checks)) ``` Check the boundaries, then run the program: ```bash topaz check types.tpz topaz run types.tpz ``` The result is: ```text output ready after 3 checks ``` ## Read the types from the result `checks` lacks a type annotation. Because its initializer is an integer, Topaz infers `int`. `current` includes an annotation because the program requires a `Stage` rather than an arbitrary `string`. `Stage` is a transparent alias for the literal union `"draft" | "ready"`. This union specifies that only the listed values are valid and rejects all other strings. The alias provides a clear name for this choice without creating a new runtime identity. `render` demonstrates a callable type. `(Stage, int) -> string` specifies a function that accepts a `Stage` and an `int`, returning a `string`. Because the named function `describe` matches this signature, it can be assigned to the binding and invoked later. Primitive type names are lowercase: `int`, `float`, `bool`, and `string`. The unit type has the sole source spelling `()`; the identifier `unit` is not an alias for it. Common containers use named constructors such as `Array`, `Map`, and `Set`. Expected absence and recoverable failure are represented by `Option` and `Result`. ## Choose the shape that states your intent | Need | Prefer | Why | | --------------------------------------------------- | --------------------------------------- | ------------------------------------------------------------------------- | | An obvious local value | inference | The initializer already conveys the type. | | A public input, result, or important local boundary | an annotation | The checker and reader directly see the intended contract. | | One value from a closed set of alternatives | a union or literal-union alias | Each admitted alternative remains explicit. | | A value that can be called later | a callable type such as `(int) -> bool` | Parameter and return types stay attached to the value. | | Domain identity, fields, or named cases | a `record`, `enum`, or `newtype` | Nominal declarations remain distinct even when their contents look alike. | A `type` alias improves domain vocabulary while remaining transparent. Choose nominal data when mixing two structurally similar values would be an actual error. The [Records & Nominal Data](/en/docs/language/records-nominal-data) page compares `record`, `enum`, and `newtype` construction and matching. ## Give empty values enough context Some values do not carry sufficient type information on their own. An empty array cannot determine its element type, and `None` cannot reveal the missing value type. Supply context through an annotation, a function parameter, a declared result, or another surrounding type: ```text let names: Array = [] let selected: Option = None ``` These canonical shapes are presented as text here. The runnable example above remains the page’s verified program. ## Common mistake: writing the target language’s type spelling Topaz collection types use `Array`, `Map`, and `Set`. Callable types enclose parameter types in parentheses, followed by `->` and the result type. Keeping these spellings explicit ensures that checker diagnostics and generated targets agree on the same source contract. ## Exact limits - There is no implicit conversion between `int` and `float`. - Every use of a type parameter, primitive named type, builtin generic constructor, user `record`, `enum`, or `newtype`, or type alias supplies exactly its declared number of type arguments. - The closed source-spellable standard-library types `template`, `JSONValue`, `File`, `Bytes`, `ByteBuffer`, `Path`, `Regex`, `Match`, `TOMLValue`, `URL`, `Date`, `BigInt`, `Decimal`, and `RoundingMode` take no type arguments. - `File` in a source annotation denotes the same opaque resource type returned by `open`. - Generic type parameters are invariant. Compatibility follows declared type arguments, not content similarity. - Generic bounds belong only to named function type parameters. [Functions & Generics](/en/docs/language/functions-generics) explains the admitted rank-1 form. - Recursive aliases, variance annotations, higher-rank polymorphism, and broader reflection remain deferred. - A generic empty value that still has unsolved type parameters is a static error rather than a runtime guess. If inference, annotations, and mutation are still new, revisit [Values and Functions](/en/docs/learn/values-functions). For choosing among absence, recoverable failure, and faults, continue to [Null, Option, Result & Faults](/en/docs/language/null-option-result-faults). --- route: https://topaz.ooo/v5.20/en/docs/language/bindings-scope-closures markdown: https://topaz.ooo/docs/language/bindings-scope-closures.md covers: §3, §4, §5, §7, §18, §22 # Bindings, Scope & Closures This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. A binding associates a name with a value. Its declaration answers two essential questions: can the name be reassigned, and where is it visible? A closure can keep that binding alive even after the enclosing function returns. ## Watch one captured binding change Save this as `bindings-scope-closures.tpz`: ```topaz function makeCounter(start: int) -> () -> int { let mut current = start () => { current = current + 1 current } } let label = "outer" let next = makeCounter(10) { let label = "inner" print("{label}: {next()}, {next()}") } print(label) ``` Check and run it: ```bash topaz check bindings-scope-closures.tpz topaz run bindings-scope-closures.tpz ``` Both calls share a single counter, while the inner `label` disappears when its block ends: ```text output inner: 11, 12 outer ``` ## Follow the binding, not a copied value `makeCounter` creates the mutable binding `current` and returns a lambda. The lambda references `current`, capturing that binding directly. Because the original declaration used `let mut`, reassignment inside the lambda is valid. The closure does not receive a frozen copy of `10`. It retains the same mutable cell. The first call updates the cell to `11`. The second call reads that new value and updates it to `12`. The cell remains alive because `next` still refers to it after `makeCounter` returns. `label` demonstrates lexical scope. The inner block may shadow the outer name with its own binding. Inside the block, `label` evaluates to `"inner"`. Once the block ends, that binding goes out of scope and the outer `"outer"` binding becomes visible again. Redeclaring the same name twice in one scope is a static error. ## Choose the binding deliberately | Form | Use it for | Reassignment | | --------- | -------------------------------------------------------------------------- | ------------ | | `const` | A compile-time value built from the documented constant-expression grammar | Not allowed | | `let` | An ordinary runtime value that keeps the same binding | Not allowed | | `let mut` | State whose reassignment is part of the model | Allowed | Prefer `let`. Add `mut` only when a subsequent assignment is meaningful. This keeps changing state visible at the declaration instead of making every name potentially mutable. Parameters and names introduced by patterns are immutable. If a function needs changing local state, copy the parameter into a new `let mut` binding with a name that describes that state. ## Common mistake: treating capture as a snapshot Capturing an immutable binding is read-only. Capturing a mutable binding preserves its live cell, so writes made through one closure are visible to later reads through the same binding. If two closures capture the same mutable binding, they share that cell rather than receiving independent counters. Scope still applies when a closure is created. It may capture names that are lexically visible at that point, but it cannot reach a name declared later or a sibling block’s private binding. ## Exact limits - Inner scopes may shadow an outer name. One scope may not declare the same name twice. - An immutable `let`, a `const`, a parameter, or a pattern binding cannot be assigned. - Mutable declarations accept a bare identifier rather than a destructuring pattern. - Topaz closures have no explicit capture list, move-capture syntax, Rust closure traits, or borrow-receiver syntax. - Resource lifetime is a separate decision. The File-only `using` form and deterministic cleanup belong in [Defer & Resources](/en/docs/language/defer-resources). Revisit [Values and Functions](/en/docs/learn/values-functions) for the initial immutable and mutable bindings. Continue with [Functions & Generics](/en/docs/language/functions-generics) to choose between named functions, lambdas, and generic functions. --- route: https://topaz.ooo/v5.20/en/docs/language/patterns-control-flow markdown: https://topaz.ooo/docs/language/patterns-control-flow.md covers: §5, §6, §10 # Patterns & Control Flow This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. Use `if` for a single yes-or-no condition, `match` when the shape or category of a value determines the result, and `for` when performing the same work on each item. Patterns are constructs inside `match` and `for` that test data and bind names to required parts. ## Run one example Save this as `data-control.tpz`: ```topaz record StudyTask { title: string, minutes: int, done: bool = false, } function pace(task: StudyTask) -> string { if task.done { return "done" } match task.minutes { case 1..15 => "quick" case 16..30 => "focused" case _ => "deep" } } let tasks: Array = [ StudyTask { title: "Run first program", minutes: 10, done: true }, StudyTask { title: "Build application", minutes: 25 }, ] let mut remaining = 0 for task in tasks { if !task.done { remaining = remaining + task.minutes } print("{task.title}: {pace(task)}") } print("remaining: {remaining} minutes") ``` Check it before running it: ```bash topaz check data-control.tpz topaz run data-control.tpz ``` The output follows array order: ```text output Run first program: done Build application: focused remaining: 25 minutes ``` ## Follow the decisions The loop binds each array element to `task`. The inner `if` evaluates a single boolean condition to check whether the task is unfinished. It neither classifies multiple cases nor destructures the value. When both outcomes require handling, write `if condition { ... } else { ... }`. Compatible branch values allow the entire `if` expression to yield a value. `pace` first uses `if` for the completed task case. Its `match` expression then evaluates `task.minutes` once and tests the `case` arms from top to bottom. `1..15` and `16..30` are range patterns. `_` serves as the catch-all pattern, ensuring every other integer produces a result. | Choose | When it fits | What it produces | | ------- | --------------------------------------------------- | ------------------------------------------------------------------------------------------- | | `if` | A single condition selects between branches | A value when compatible branches yield values, or an effect-only statement otherwise | | `match` | A value has several meaningful shapes or categories | The selected arm's value, along with any pattern bindings in that arm | | `for` | Every item must be visited in iteration order | Statements executing side effects, or an `Array` of body results when used as an expression | ## Patterns select and bind A literal or range pattern tests a value. An identifier such as `task` binds the matched value. Constructor patterns such as `Some(value)` and `Err(message)` destructure `Option` and `Result` values. Record patterns extract named fields. Array patterns use `..` rather than call spread syntax. `case [head, ..tail] =>` binds the first element and assigns the remaining elements to `tail` as an `Array`. Use `_` when a fallback is required but the value is not needed. `if let pattern = value { ... }` is useful for matching a single successful shape with an optional fallback. `while let` repeats the test until pattern matching fails. Their bindings are immutable and scoped exclusively to the successful block. ## Common mistake Do not write `[head, ...tail]` inside a pattern. The `...` syntax is reserved for array and call spreads. The standard pattern for capturing remaining elements is `[head, ..tail]`. Enum construction and enum pattern matching are intentionally distinct. Construct variants using the qualified enum name, such as `Status.Paused("review")`, but match variants without qualification, such as `case Paused(reason) =>`. ## Exact behavior that affects a choice - A value-producing `if` or `match` requires compatible branch results. - A closed enum match is verified for complete variant coverage. A guarded arm does not count toward that coverage. - When complete match coverage cannot be established statically, an explicit `_` arm is required. - Patterns evaluate from left to right. Bindings from a failed alternative do not leak into subsequent alternatives. - A refutable pattern failure inside a collection comprehension produces an error rather than filtering items. Add an explicit comprehension `if` clause to skip items. - `Array`, integer ranges, `Set`, and `map.keys` have a defined iteration order. Strings are not directly iterable. - `while` loops and statement-position `for` loops produce no value. A `for` loop evaluated in expression position collects its body results in source order. User-defined patterns, generators, user-defined iteration, exception-handling control structures, and labels on `while` or `for` loops are not supported in the current language. Continue from the course example in [Data and Control](/en/docs/learn/data-control). See [Records & Nominal Data](/en/docs/language/records-nominal-data) for the shapes patterns can destructure, and [Collections & Comprehensions](/en/docs/language/collections-comprehensions) for iteration and filtering. --- route: https://topaz.ooo/v5.20/en/docs/language/functions-generics markdown: https://topaz.ooo/docs/language/functions-generics.md covers: §3, §4, §5, §6, §7, §22 # Functions & Generics This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. A function defines a reusable transformation and specifies the types crossing its boundary. Start with a concrete named function. Use a lambda for concise behavior passed as a value, and introduce a type parameter only when a single relationship applies across multiple types. ## Run the progression in one program Save this as `functions-generics.tpz`: ```topaz record Label derives Show { text: string, } function applyTwice(value: int, step: (int) -> int) -> int { step(step(value)) } function firstOr(values: Array, fallback: T) -> T { match values { case [] => fallback case [first, ..] => first } } function showValue(value: T) -> string { Show.show(value) } let increment: (int) -> int = value => value + 1 let first: string = firstOr(["Topaz", "Classic"], "none") let shown = showValue(Label { text: "docs" }) print("{applyTwice(3, increment)}") print(first) print(shown) ``` Check it before running: ```bash topaz check functions-generics.tpz topaz run functions-generics.tpz ``` The result is: ```text output 5 Topaz Label { text: docs } ``` ## Begin with a named function `applyTwice` specifies two parameters and an `int` result. Its final expression provides that result, making an explicit `return` statement unnecessary. Use `return` when an early exit clarifies control flow. The `step` parameter has type `(int) -> int`. Callers must provide a callable that accepts and returns an `int`. `increment` is a lambda matching that shape. A lambda uses `=>`, can be stored like any other value, and captures visible bindings under closure rules. ## Add a type parameter when the relationship repeats `firstOr` does not need to know the concrete type of the array elements; it handles strings, integers, or any other type uniformly. It defines the relationship where the array elements, fallback value, and result all share the same type `T`. Because this call supplies a string array, type inference resolves `T = string`, so the caller does not need to specify a type argument explicitly. This demonstrates rank-1 polymorphism. The named function is generic, and each invocation selects a concrete instantiation. Choose generics when the algorithm preserves a true type relationship; otherwise, choose a concrete type. ## Add a bound only when the body needs an operation `showValue` calls `Show.show`, so its body requires proof that `T` conforms to the static `Show` protocol. `Label derives Show` provides that nominal conformance. The bound does not apply to arbitrary printable values. Structural values and primitives do not gain protocol conformance automatically. Bounds represent an escalation beyond standard generics rather than the default. A named generic function may require an order-independent conjunction such as `T: Show + JSON` when its body genuinely uses both protocols. ## Choose the smallest function form | Need | Choose | Signal to the reader | | ----------------------------------------------------- | ------------------------------------ | ---------------------------------------------------------- | | Reusable behavior with a stable name | A concrete named function | Parameters and result form a public contract. | | Short behavior passed or stored as a value | A lambda | The surrounding context supplies concrete parameter types. | | One algorithm preserving a relationship across types | A named generic without bounds | The same `T` connects inputs and result. | | A generic body that calls a static protocol operation | A named generic with declared bounds | The bounds list the operations the body may require. | ## Calls, defaults, and variadics Parameters are immutable. A default value is evaluated when its argument is omitted and must be a literal or `const` expression. It cannot reference another parameter. A variadic tail is written `...args: T`, must be final, and appears inside the function as `Array`. A call evaluates its callee once and then evaluates positional and spread arguments from left to right. Named arguments follow positional and spread arguments. Array spread contributes only to a variadic tail. It does not fill an arbitrary fixed parameter. Type inference is the standard generic call path. Where explicit type arguments are permitted, the list must be complete and agree with the value arguments and expected result. Partial decoration is a static error. ## Common mistake: reaching for a generic lambda Anonymous behavior is a lambda whose parameter types become concrete from context. Type parameter bindings and protocol bounds belong to named function declarations. If behavior requires its own reusable generic contract, give it a name and declare that contract once. ## Exact limits - Generic functions are rank-1. Higher-rank function values remain deferred. - Lambdas, callable types, aliases, nominal declarations, receiver methods, and protocol methods do not declare bounds. - Generic lambdas, default type parameters, variance annotations, dynamic protocol dispatch, and arbitrary trait constraints remain deferred. - A generic body has exactly its declared bounds and cannot silently use a stronger operation. For the initial function and lambda lesson, revisit [Values and Functions](/en/docs/learn/values-functions). Use [Types](/en/docs/language/types) for callable and generic type shapes, and [Bindings, Scope & Closures](/en/docs/language/bindings-scope-closures) for capture and lifetime. --- route: https://topaz.ooo/v5.20/en/docs/language/records-nominal-data markdown: https://topaz.ooo/docs/language/records-nominal-data.md covers: §3, §5, §6, §8 # Records & Nominal Data This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. Topaz offers several data forms because “these values have the same fields” and “these values represent the same domain concept” are different promises. Start with the distinction your program needs, then choose the smallest form that preserves it. ## Run one example Save this as `records-nominal-data.tpz`: ```topaz type DisplayValue = int | string newtype UserId = int enum Status { Active, Paused(string), } record User { id: UserId, name: string, status: Status = Status.Active, } function statusLabel(status: Status) -> string { match status { case Active => "active" case Paused(reason) => reason } } function display(value: DisplayValue) -> string { match value { case number: int => "number {number}" case text: string => text } } let before: User = User { id: UserId(7), name: "Ada" } let after: User = User { ...before, status: Status.Paused("review") } let preview = { name: "Ada", age: 36 } let older = preview{ age: 37 } let ready = display("ready") print("{before.id.value()}:{before.name}:{statusLabel(before.status)}") print("{after.name}:{statusLabel(after.status)}") print("{preview.age}:{older.age}") print("{display(7)}:{ready}") ``` ```bash topaz check records-nominal-data.tpz topaz run records-nominal-data.tpz ``` It prints: ```text output 7:Ada:active Ada:review 36:37 number 7:ready ``` ## Choose the data form | Form | Use it when | Construction and observation | | ----------------- | ----------------------------------------------- | ------------------------------------------------------------------- | | Structural record | A local value only needs a known set of fields | `{ name: "Ada", age: 36 }`, then `value.field` | | Nominal `record` | The domain name and declaration identity matter | `User { ... }`, fields, defaults, named update, and record patterns | | `enum` | Exactly one member of a closed set is present | `Status.Active` or `Status.Paused(reason)`, select with `match` | | Union `A \| B` | An existing value may have one of several types | Narrow it with literal, constructor, or type patterns | | `newtype` | One base value needs a distinct domain identity | `UserId(7)`, then unwrap one layer with `.value()` | `User` is a nominal record. Omitting `status` runs its default once, so `before` is active. `User { ...before, status: ... }` creates a fresh outer `User`, retains the other fields, and does not mutate `before` or rerun defaults. `Status` is a closed enum. Construction uses the namespace (`Status.Paused`), while a match arm uses the unqualified variant (`case Paused(reason)`). The checker can therefore require that `statusLabel` handles every variant. `DisplayValue` is a union alias, not a new runtime wrapper. The type patterns in `display` narrow the existing `int` or `string` and bind it. By contrast, `UserId` is a new nominal wrapper even though its base is `int`. `preview` is structural. Its identity is its compatible field shape. Its update uses `preview{ age: 37 }`. This is a different operation from the nominal update used for `User`. ## Construct, update, and match Nominal record fields are checked by name and type. Explicit fields evaluate from left to right. Missing defaults run afterward in declaration order. A nominal update allows one leading `...source`, then replacements. It copies only the outer record, so mutable values stored in fields remain shared. The spread expression evaluates first and must supply the defining record identity and exact declared field inventory. Missing or duplicate fields produce runtime fault `GUARD_ARITY` (`TPZ5004`), while unknown fields produce `GUARD_NO_FIELD` (`TPZ5006`). These shape guards run immediately after the spread expression evaluates and before any replacement expressions evaluate. Explicit replacement fields then evaluate from left to right and take precedence over the base record's fields without re-evaluating declaration defaults. A nominal record pattern such as `case User { name } =>` first checks that the value came from the `User` declaration, then reads the selected fields. Omitted fields are ignored, but the pattern must name at least one field. A structural record with the same fields is not a `User`. ## Common mistake JavaScript-style `{ ...value }` is not a Topaz structural record update. Use `value{ field: replacement }` for a structural record and `Name { ...value, field: replacement }` for a nominal record. Methods use a separate module-level `impl Name` block with immutable by-value `self`. Record bodies have no implicit `this`, `&self`, or `&mut self`. ## Exact boundaries - Record, enum, and newtype declarations are module-top-level nominal declarations. - Generic nominal parameters are invariant and need an expected type or another accepted inference context at construction. - Nominal equality, ordering, and collection keys preserve declaration identity as well as compatible contents. - Enum matches use bare variant names and are checked for complete coverage. Guarded arms do not establish coverage. - Nominal pattern heads are bare names, not namespace-qualified names, and empty nominal patterns are invalid. - A `newtype` constructor takes exactly one base value, and `.value()` unwraps exactly one layer. Reflection, inheritance, row polymorphism, open nominal patterns, qualified construction, dynamic protocol dispatch, and Rust ownership-style receivers are not part of the current language. Start with the course’s [Data and Control](/en/docs/learn/data-control) example. Use [Types](/en/docs/language/types) for aliases and unions, and [Patterns & Control Flow](/en/docs/language/patterns-control-flow) for exhaustive matching and bindings. --- route: https://topaz.ooo/v5.20/en/docs/language/collections-comprehensions markdown: https://topaz.ooo/docs/language/collections-comprehensions.md covers: §5, §6, §9, §10 # Collections & Comprehensions This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. Select a collection based on the behavior your application must preserve: positional order with duplicates, key-based lookup, or element uniqueness. Comprehensions provide a concise syntax to filter and transform these collections while preserving their ordering rules. ## Choose the collection first | Collection | Choose it for | Observable behavior | | ----------- | ----------------------------------------------------- | -------------------------------------------------------------------------- | | `Array` | Ordered items, positional access, and repeated values | Iterates by increasing index and preserves duplicate values | | `Map` | Values selected by key | Keys maintain insertion order; updating an existing key preserves its slot | | `Set` | Unique values | The first matching value preserves the insertion slot | ## Run one example Save this as `collections-comprehensions.tpz`: ```topaz let scores = [10, 25, 10] let labels = map { "Ada": 10, "Lin": 25 } let unique = set { 10, 25, 10 } let focused = [ for score in scores if score >= 20 => score * 2 ] let first = scores[0] let missing = scores.get(9) let hasAda = "Ada" in labels.keys let missingLabel = match missing { case Some(value) => "found {value}" case None => "missing" } print("{scores}:{unique.toArray()}:{labels.keys}") print("{focused}:{first}:{hasAda}") print(missingLabel) ``` ```bash topaz check collections-comprehensions.tpz topaz run collections-comprehensions.tpz ``` It prints: ```text output [10, 25, 10]:[10, 25]:[Ada, Lin] [50]:10:true missing ``` The array retains both `10` values. The set deduplicates the second `10` and preserves the initial insertion order. Because `labels.keys` returns an array ordered by insertion, membership checks are written as `"Ada" in labels.keys`. ## Read and change collections deliberately `scores[0]` performs a direct indexed read. Passing a negative or out-of-range index triggers a runtime fault. `scores.get(9)` avoids runtime faults by returning an `Option`, which the example handles using `match`. In-place mutation requires a mutable root binding. Declare `let mut values = [...]` before invoking a mutating operation such as `values.push(...)`. Conversely, a non-mutating expression can derive a new collection from an immutable binding. Map duplicate handling differs from Set handling. Defining duplicate literal keys produces a static error. If two keys evaluate to equal values at runtime, map construction evaluates the operands and raises a fault rather than overwriting. `Map.insert` serves as the explicit operation for inserting or updating key-value pairs. ## Literal, loop, or comprehension? - Use `[a, b]`, `set { a, b }`, or `map { key: value }` when elements are specified directly as literals. - Use `for item in source { ... }` when expressing the logic as explicit sequential steps improves readability. In statement position, it executes side effects. In expression position, it collects body evaluation results into an `Array`. - Use `[ for item in source if condition => result ]` when filtering and transformation fit naturally into a single expression. Replace the outer brackets with `set { ... }` for unique set elements or `map { ... }` for key-value entries. Comprehension clauses start with `for`, continue with subsequent `for` or `if` keywords without commas, and conclude with `=>`. Clauses nest sequentially from left to right. The resulting collection is constructed only after every evaluated clause and body expression succeeds. ## Common mistakes The collection types are `Array`, `Map`, and `Set`. `[T]` is not a valid collection type syntax. Set and map literals require explicit `set` and `map` keywords. `#{...}` and bare `{key: value}` syntax are invalid. `key in map` is invalid. Use `key in map.keys` instead. A refutable pattern mismatch inside a comprehension causes a runtime fault rather than filtering items automatically. Write an explicit `if` clause to skip non-matching elements. ## Exact boundaries - Array spread operations accept only another `Array`. Set and map literals and comprehensions do not support spread syntax. - Empty `set {}` and `map {}` literals require explicit type context for `Set` or `Map`. - Map keys and set elements must be recursively keyable. - Removing an entry removes its insertion slot. Reinserting the entry appends it to the end. Updating an existing map value preserves key order. - `for` loops iterate over `Array`, integer ranges, `Set`, and `map.keys`. Iterating over strings requires explicit scalar operations. User-defined iteration, generators, lazy or parallel comprehensions, iterable spread, and unordered collection semantics are not supported in the current language specification. Return to the course example in [Data and Control](/en/docs/learn/data-control). Refer to [Patterns & Control Flow](/en/docs/language/patterns-control-flow) for comprehension bindings, and consult [Collection Operations](/en/docs/stdlib/collections) for available standard methods. --- route: https://topaz.ooo/v5.20/en/docs/language/pipelines-placeholders markdown: https://topaz.ooo/docs/language/pipelines-placeholders.md covers: §11 # Pipelines & Placeholders This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. Use a pipeline when a result should visibly flow into the next operation. `left |> right` evaluates `left` once, saves that value, and determines a single well-defined way to supply it to `right`. A placeholder `_` marks the exact call-argument position that receives the saved value. ## Start with a self-contained pipeline Save this as `pipelines-placeholders.tpz`: ```topaz function add(value: int, amount: int) -> int { value + amount } function multiply(left: int, right: int) -> int { left * right } function equal(left: int, right: int) -> bool { left == right } let result = 5 |> add(2) |> multiply(_, 3) let repeated = result |> equal(_, _) print("{result} {repeated}") ``` Check and run it: ```bash topaz check pipelines-placeholders.tpz topaz run pipelines-placeholders.tpz ``` The output is: ```text output 21 true ``` The first stage has no placeholder, inserting the saved `5` as the first argument: `add(5, 2)`. The second stage explicitly specifies where the saved `7` goes: `multiply(7, 3)`. In the final pipeline, both placeholders receive the same saved `21`, making the call equivalent to `equal(21, 21)`. The left side is not evaluated again for the second placeholder. ## Choose the right-hand shape | Right side | Meaning | | -------------------------- | --------------------------------------------------------------------------------------- | | `value \|> call(other)` | Insert `value` before all explicit positional, spread, and named arguments | | `value \|> call(_, other)` | Replace every valid `_` argument with `value` without inserting an extra first argument | | `value \|> callable` | Call the callable with `value` as its single argument | | `value \|> .field` | Read `field` from `value` | Pipelines associate from left to right, passing the evaluated value of each stage to the next. The placeholder belongs to its nearest enclosing call. A `_` inside a pattern is instead a wildcard that discards a matched value. The two roles share a token but operate in separate scopes. Multiple placeholders are valid when each appears in a call-argument position. They all receive the same saved left value. Explicit placeholder replacement takes priority. Once a valid `_` exists on the right, Topaz does not insert an extra first argument. ## Common correction A bare `_` is not a value. It cannot be the entire pipeline right side, a callee, a member name, or a field label. Place it inside the argument list of the call that should receive the piped value. Optional-property pipe sugar such as `value |> ?.field` is not supported. Use ordinary optional chaining before or inside an explicit lambda. Named arguments must still follow positional and spread arguments after pipeline binding. A right side that is neither a call, a callable value, nor `.field` is a static error. Pipelines connect data flow directly. Use postfix `?` explicitly for Result propagation and `concurrent` with a declared timeout for concurrent work. Partial application, asynchronous lifting, and user-defined pipe operators remain separate language choices. See [Operators & Expressions](/en/docs/language/operators-expressions) for precedence, [Functions & Generics](/en/docs/language/functions-generics) for callable values, and [Collections & Comprehensions](/en/docs/language/collections-comprehensions) for collection-producing stages. --- route: https://topaz.ooo/v5.20/en/docs/language/null-option-result-faults markdown: https://topaz.ooo/docs/language/null-option-result-faults.md covers: §12, §13, §13a # Null, Option, Result & Faults This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. Use this guide when a value may be absent or an operation may fail. The primary question is not “which operator is shortest?” but “can the caller continue execution?” ## Choose by meaning | Situation | Use | | ------------------------------------------------- | ------------------------------------------------------- | | No value is an ordinary outcome inside Topaz | `Option` with `Some(value)` or `None` | | External data explicitly carries a nullable field | `T \| null` | | The caller can respond to a meaningful error | `Result` with `Ok(value)` or `Err(error)` | | The program violated a runtime contract | A fault; evaluation aborts instead of producing a value | Prefer `Option` for standard absence in Topaz APIs. Reserve `null` for data and interop boundaries that explicitly contain it. Use `Result` when a failure requires diagnostic details or a recovery decision. ## Turn bad input into a recoverable result This runnable example converts the absence returned by `toInt` into a descriptive `Result`: ```topaz record StudyTask { title: string, minutes: int, } function parseMinutes(text: string) -> Result { match toInt(text) { case Some(value) if value > 0 => Ok(value) case _ => Err("minutes must be a positive integer") } } function makeTask(title: string, minuteText: string) -> Result { let minutes = parseMinutes(minuteText)? Ok(StudyTask { title: title, minutes: minutes }) } function inspect(minuteText: string) -> () { print("input: {minuteText}") defer { print("inspection finished") } match makeTask("Build application", minuteText) { case Ok(task) => print("ready: {task.title}, {task.minutes} minutes") case Err(message) => print("cannot add task: {message}") } } inspect("25") inspect("later") ``` Check and run the program: ```bash topaz check failures-resources.tpz topaz run failures-resources.tpz ``` ```text output input: 25 ready: Build application, 25 minutes inspection finished input: later cannot add task: minutes must be a positive integer inspection finished ``` `toInt` returns `Option` because non-integer text yields no parsed value. `parseMinutes` determines that this absence is significant to its caller and converts it into `Err`. In `makeTask`, the postfix `?` operator unwraps `Ok`. Upon encountering `Err`, it propagates that error immediately from the returning `Result` function. The final `match` expression handles both potential outcomes. ## Work with optional and nullable values Optional access and fallback operators maintain this distinction: - For `maybeUser: Option<{ name: string }>`, `maybeUser?.name` evaluates to an `Option` result, and `maybeUser?.name ?? "guest"` evaluates to a `string`. - For `configPath: string | null`, `configPath ?? "default.toml"` evaluates the fallback only when the value is `null`. - `target ??= value` is a statement that performs assignment only when the target is empty. An `Option` target requires `Some(42)` rather than a bare `42`. Topaz does not wrap values in `Some` automatically. Both `?.` and `??` evaluate their left operand once. `?.` also skips evaluating method arguments when the receiver is empty. Each operator unwraps or flattens a single optional layer without converting `Option` into `null`. **Common correction:** Use standard `.` property access for non-optional and non-nullable values. If a type combines `Option` and `null`, handle cases explicitly with pattern matching rather than chaining optional operators. ## A fault is not an error value This example requests an out-of-bounds array element: ```topaz let tasks = ["Run first program", "Build application"] print(tasks[2]) ``` Running `topaz run intentional-fault.tpz` exits with a non-zero status: ```text output error[TPZ4001]: index 2 is out of bounds for an array of length 2 --> intentional-fault.tpz:2:7 | 2 | print(tasks[2]) | ^^^^^^^^ ``` The bounds violation constitutes a fault. It halts evaluation immediately and is neither `None` nor `Err`. Postfix `?`, `??`, optional chaining, and `concurrent` cannot catch or convert faults. Constant expressions containing invalid arithmetic may instead be rejected during static checking prior to execution. ## Exact boundaries - Postfix `?` is valid only within functions or closures whose return type can represent the corresponding error value. - Traditional `try` blocks, exception handling, fault catching, panic keywords, and automatic retry mechanisms are not supported in Topaz. - `assert(...)` is available only in explicitly selected `test-profile` builds. It is not an assertion mechanism for general application logic. - Resource cleanup on normal scope exit should use `defer`. Fault cleanup operates within a narrower runtime scope, so faults must not be treated as recoverable `Result` values. Continue with [Failures and Resources](/en/docs/learn/failures-resources) for the guided exercise, [Defer & Resources](/en/docs/language/defer-resources) for cleanup execution timing, and [Runtime Behavior](/en/docs/runtime/behavior) for the runtime fault boundary model. --- route: https://topaz.ooo/v5.20/en/docs/language/defer-resources markdown: https://topaz.ooo/docs/language/defer-resources.md covers: §1, §3, §4, §5, §6, §7, §9, §13, §13a, §14, §22 # Defer & Resources This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. Use `defer` for general scope-exit cleanup. For one owned `File`, `using` combines acquisition, use in a child scope, and cleanup through an implicit close. ## Watch the cleanup order Place a text file named `defer-resources.txt` beside the program. Its exact contents are: ```text ready ``` Save this as `defer-resources.tpz`: ```topaz function inspectNote(path: string) -> Result<(), string> { using file = open(path)? { defer { print("body cleanup") } let note = file.read()? print("note scalars: {note.scalars().length}") } print("resource scope closed") Ok(()) } match inspectNote("defer-resources.txt") { case Ok(_) => print("done") case Err(message) => print("error: {message}") } ``` Check and run it: ```bash topaz check defer-resources.tpz topaz run defer-resources.tpz ``` The observed order is: ```text output note scalars: 6 body cleanup resource scope closed done ``` `open(path)?` acquires the file once. Only after acquisition succeeds does `using` register its implicit close. The subsequent body `defer` therefore runs first, and the implicit close runs before execution reaches `resource scope closed`. ## Choose the form that owns the lifetime | Situation | Use | | ----------------------------------------------------------------- | --------------------------------------------------------------- | | One cleanup call or block belongs to the current scope | `defer call` or `defer { ... }` | | One acquired `File` belongs to one child block | `using file = open(path)? { ... }` | | Acquisition, reading, or writing can fail under normal conditions | Return or match `Result`; use `?` only in a compatible function | | The program has violated a runtime contract | Treat it as a fault, not as recoverable cleanup control | Several `defer` statements in one scope run in last-in, first-out order. They also run when `return`, postfix `?`, `break`, or `continue` exits that scope. A `using` binding is immutable and visible only inside its body. A closure may retain the `File` value, but the implicit close still occurs when the `using` scope ends, so retaining it does not extend the resource lifetime. ## Result propagation is not a runtime fault If `open` returns `Err`, no cleanup has been registered yet. If `file.read()` returns `Err`, postfix `?` exits the active `using` scope, so its registered cleanup runs before the error leaves `inspectNote`. A runtime fault aborts evaluation immediately. Lexical `defer` statements and `using` cleanup after that point are unspecified. A fault raised by a deferred action follows the runtime logging or collection policy. It is not a catchable value and leaves an `Err` already being returned unchanged. ## Common correction Do not spell the File operation as `File.open(...)`, and do not invent a general `using` protocol. The exact acquisition function is `open(path)`, and the exact statement accepts one bare immutable name and one `File`. Explicitly calling `close()` inside a `using` body does not cancel the registered close attempt. Behavior after close or repeated close operations is not portable, so standard code should let one owner close the resource once. ## Exact boundary Current Topaz resource behavior covers the `File`, `defer`, and `using` operations described above. User-defined disposal protocols, multi-resource acquisition, resource transfer, asynchronous cleanup, cancellation cleanup, host-exception cleanup, and module-lifetime finalization remain future decisions. Generated targets preserve the documented behavior or reject before artifact output. Check target-specific behavior on the intended target. Review the guided failure model in [Failures and Resources](/en/docs/learn/failures-resources), compare absence and recoverable errors in [Null, Option, Result & Faults](/en/docs/language/null-option-result-faults), and see the exact File operations in [Files & Resources](/en/docs/stdlib/files-resources). --- route: https://topaz.ooo/v5.20/en/docs/language/concurrency markdown: https://topaz.ooo/docs/language/concurrency.md covers: §2, §3, §4, §5, §7, §8, §10, §13a, §15, §19, §22 # Concurrency This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. Use `concurrent` when independent expressions run concurrently and the caller requires a single named result record. Standard application code declares a duration and an explicit timeout fallback. ## Complete successfully Save this as `concurrency-success.tpz`: ```topaz let result = concurrent(timeout: 3s) { total: 1 + 2 label: "ready" } else { { total: -1, label: "timeout" } } print("{result.total} {result.label}") ``` Check and run it: ```bash topaz check concurrency-success.tpz topaz run concurrency-success.tpz ``` Both arms finish before the deadline, so their names become record fields: ```text output 3 ready ``` The record associates each value with its arm name. This does not indicate which arm started or completed first. ## Let the deadline choose the fallback This second program has an arm that cannot finish. Save it as `concurrency-timeout.tpz`: ```topaz function spin() -> int { let mut i = 0 while true { i += 1 } i } let result = concurrent(timeout: 5ms) { stuck: spin() } else { { stuck: -1 } } print("{result.stuck}") ``` Check and run it: ```bash topaz check concurrency-timeout.tpz topaz run concurrency-timeout.tpz ``` The deadline wins, no partial arm record is exposed, and the fallback supplies the entire result: ```text output -1 ``` ## Distinguish the three outcomes | What happens before the deadline? | Expression result | | --------------------------------- | ---------------------------------------------------- | | Every arm completes | A record with one field per arm | | The timeout wins | The `else` block value | | An arm faults | The whole expression faults, and `else` does not run | An arm evaluating to `Err(error)` has not faulted. That `Result` is an ordinary value stored in its named record field. The fallback must be type-compatible with the successful arm record, which is why each example returns identical field names and compatible value types on both paths. ## Result names and scheduling Arm names map results to record fields. Scheduler start order, completion order, output interleaving, fairness, and deterministic interleaving are unspecified. When output order matters, join the tasks first and then print the record. After a timeout, runtime policy handles cancellation and resource cleanup. Later faults from abandoned work are not delivered to the Topaz expression. Code should remain independent of scheduler choice and post-timeout cleanup details. ## Common corrections - Write `3s` or `5ms` with no space. Property forms such as `.seconds` are not duration syntax. - A timeout duration scaled to milliseconds must fit unsigned `u64` (`0..=18446744073709551615`), with exact maxima `18446744073709551615ms`, `18446744073709551s`, and `307445734561825m`; larger values are rejected with `TPZ5001`. - A timeout form requires `else`. A plain join cannot have `else`. - `else` handles timeout only. It does not catch a fault or unwrap an `Err`. - Arm names must be unique, and no syntax exposes a partial result. The plain `concurrent { ... }` join is useful on an advanced concurrency page when waiting without a timeout is itself the point. It is not the default form for ordinary examples. There is no implicit async model, `async`/`await` syntax, public task handle, general duration arithmetic, or specified cancellation protocol. See [Runtime Behavior](/en/docs/runtime/behavior) for observable fault and order boundaries and [Forbidden & Deferred Forms](/en/docs/language/forbidden-deferred) for familiar-looking forms that are not current Topaz. --- route: https://topaz.ooo/v5.20/en/docs/language/strings-templates markdown: https://topaz.ooo/docs/language/strings-templates.md covers: §1, §16, §22 # Strings & Templates This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. Use ordinary strings for text your program displays or compares. Use registry-tagged templates when values carry path, regular expression, shell, or SQL intent that requires structured representation. Template values carry structured tag intent alongside their content. ## Start with observable Unicode text Save this as `strings-templates.tpz`: ```topaz let title = "Topaz 💎" let scalarCount = title.scalars().length let configPath = p"config/topaz.toml" let query = sql"SELECT name FROM projects WHERE title = {title}" print("{title}: {scalarCount}") ``` Check and run it: ```bash topaz check strings-templates.tpz topaz run strings-templates.tpz ``` The output is: ```text output Topaz 💎: 7 ``` `title` contains seven Unicode scalar values: five letters, one space, and one emoji. The `scalars()` method returns an `Array` whose elements are single-scalar strings. This provides the standard method to inspect text positions. Storage bytes do not represent string positions, and Topaz performs no implicit Unicode normalization. Because `configPath` uses the `p` template, it carries path-normalization intent. `query` uses the `sql` template, where `{title}` serves as a parameter boundary instead of direct SQL text insertion. The program omits printing both templates because template values are structured, non-renderable entities rather than ordinary display strings. ## Choose the form by the value you need | Need | Form | Boundary | | ----------------------- | ---------------------- | ------------------------------------------------------ | | Display or compare text | `"Hello, {name}"` | Interpolation uses the standard value renderer | | Multiline text | `"""..."""` | Closing-delimiter indentation controls exact stripping | | A path template | `p"config/app.toml"` | Carries platform path-normalization intent | | A regex template | `r"^[a-z]+$"` | Only the specified reduced-escaping floor is portable | | Shell command data | `sh"tool {argument}"` | Construction does not execute a process | | Parameterized SQL data | `sql"WHERE id = {id}"` | Interpolation becomes parameter metadata | The `p`, `r`, `sh`, and `sql` tags work with ordinary or triple-quoted strings. The tag must directly precede the opening quote without intervening whitespace. With a space, `p "file"` parses as an identifier followed by a string rather than a call, producing a parse error. Ordinary and triple-quoted strings support `{expression}` interpolation and the fixed escape set. When a newline immediately follows a triple-quoted opening delimiter, that initial newline is omitted. The exact spaces-and-tabs prefix preceding the closing delimiter must appear on each nonblank content line before Topaz removes it. All tagged templates (`p`, `r`, `sh`, and `sql`) produce the built-in opaque type `template`. Use `template` in binding and return annotations, for example `let query: template = sql"..."` or `function query() -> template { ... }`. A `template` is distinct from `string`. Its `.tag` accessor returns the producing tag name as a `string`, and `.parts` returns the literal segments between interpolations as an `Array`. ## Common correction Do not use `text[0]`, `text.length`, or slicing operations to inspect a string. Call `text.scalars()` and process the result using standard Array operations. This provides scalar access rather than grapheme-cluster segmentation. Backticks, `${expression}`, single-quoted strings, `html"..."`, unrecognized adjacent tags, and user-defined tags are non-canonical. An `sh` template does not execute commands, and SQL interpolation does not permit direct text insertion into queries. HTML templates, user tag registration, grapheme-cluster APIs, direct string slicing, a fully fixed cross-platform regex dialect, and shell execution semantics fall outside this language surface. A `template` value cannot be used as an ordinary `string`. Because `template` is a built-in type name, declarations named `record template`, `enum template`, or `newtype template` are rejected. Refer to [Lexical Structure & Layout](/en/docs/language/lexical-layout) for quoting and newline rules, [Runtime Behavior](/en/docs/runtime/behavior) for rendering details, and [Forbidden & Deferred Forms](/en/docs/language/forbidden-deferred) for familiar syntax options that are intentionally unavailable. --- route: https://topaz.ooo/v5.20/en/docs/language/modules-visibility markdown: https://topaz.ooo/docs/language/modules-visibility.md covers: §17 # Modules & Visibility This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. Use modules when a file defines responsibilities required by another file. Each `.tpz` file constitutes a single module, with no separate `module` declaration. ## Build one program from two files The study-plan application uses the following source layout: ```text tree study-plan/ ├── topaz.toml ├── topaz.lock └── src/ ├── main.tpz └── plan.tpz ``` `src/plan.tpz` defines the data model and reusable operations: ```topaz export record StudyTask { title: string, minutes: int, done: bool = false, } function parseMinutes(text: string) -> Result { match toInt(text) { case Some(value) if value > 0 => Ok(value) case _ => Err("minutes must be a positive integer") } } export function makeTask( title: string, minuteText: string, done: bool = false, ) -> Result { let minutes = parseMinutes(minuteText)? Ok(StudyTask { title: title, minutes: minutes, done: done }) } export function summarize(tasks: Array) -> string { let mut remaining = 0 for task in tasks { if !task.done { remaining = remaining + task.minutes } } "tasks={tasks.length}, remaining={remaining} minutes" } ``` `src/main.tpz` imports the required three names: ```topaz import src.plan { StudyTask, makeTask, summarize } export function main(args: Array, stdin: string) -> Result { let tasks: Array = [ makeTask("Run first program", "10", true)?, makeTask("Build application", "25", false)?, ] print(summarize(tasks)) Ok(0) } ``` From the package root, check and execute the resolved unit: ```bash topaz check --root . --locked topaz run --root . --locked ``` ```text output tasks=2, remaining=25 minutes ``` Specifying an explicit root resolves `src.plan` to `src/plan.tpz`. The entry file and all targets reachable through its imports constitute the compilation unit. Unrelated files outside this closure are excluded from verification for this entry. ## Export only the boundary another module needs Declarations are private by default unless prefixed with `export`. `StudyTask`, `makeTask`, and `summarize` cross the module boundary. `parseMinutes` remains an internal implementation detail; importing or accessing it from `src/main.tpz` results in a static error. Imported bindings are read-only. A selected import binds exported names directly: | Goal | Canonical shape | | ------------------------ | ------------------------------------------------ | | Select a few names | `import src.plan { StudyTask, makeTask }` | | Rename one selected name | `import src.plan { summarize as renderSummary }` | | Bind a namespace | `import src.plan as plan` | With the namespace form, exported values are accessed as `plan.makeTask(...)`. A namespace acts as a compile-time lookup name rather than a runtime value. A qualified exported type such as `plan.StudyTask` is valid in type position, but `plan.StudyTask { ... }` is not a construction form. Select `StudyTask` when direct nominal construction is needed. **Common correction:** `import src.plan as plan { summarize }` does not combine the two import forms. Choose either a namespace import or a selected import. ## Root, initialization, and package are different decisions - A module path is a dotted address relative to the selected root, matching exact file and directory names. - Imports must be placed in the top-level import prologue, preceding other items. - Dependencies initialize once, prior to the module that imports them. An imported module may include declarations and bindings, but no free runtime-bearing statements such as a top-level loop or assignment. - `topaz.toml` specifies the package identity, entry point, dependencies, and build target for the toolchain. It does not introduce a second module declaration syntax. The package command can supply the entry point and root, as demonstrated above. When specifying a file directly, the directory of the entry file serves as the default root unless `--root` explicitly designates a containing root. ## Import cycles are rejected Suppose `a.tpz` imports `b.tpz`: ```text import b export function fromA() -> int { 1 } ``` and `b.tpz` imports `a.tpz`: ```text import a export function fromB() -> int { 2 } ``` Executing `topaz check a.tpz --root .` reports the detected cycle: ```text output error[TPZ3006]: import cycle: a -> b -> a --> a.tpz:1:8 | 1 | import b | ^ a.tpz: 1 diagnostic ``` Every import edge participates in cycle detection, even when an imported name is used solely as a type. Resolve the cycle by moving shared declarations into a third acyclic module or by assigning the dependency to one direction. No exception exists for type-only cycles. ## Exact boundaries The current module syntax does not include `use`, string or template paths, an alias combined with a selection list, export lists, wildcard exports, re-exports, export-site renaming, or side-effect-only imports. `export let mut` is also rejected. Imports cannot reference paths outside the root, and namespaces cannot expose private members. Continue with [First Application](/en/docs/learn/first-application) for the complete tested package, [Packages & Distribution](/en/docs/toolchain/packages) for manifests and locks, and [Forbidden & Deferred Forms](/en/docs/language/forbidden-deferred) for familiar module constructs that Topaz deliberately does not accept. --- route: https://topaz.ooo/v5.20/en/docs/language/forbidden-deferred markdown: https://topaz.ooo/docs/language/forbidden-deferred.md covers: §20 # Forbidden & Deferred Forms This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. Refer to this guide when `topaz check` rejects syntax originating from JavaScript, Python, Rust, or an earlier Topaz draft. Topaz accepts the canonical forms below. Most familiar spellings have direct current replacements; the remaining feature families are listed as deferred. ## Check a program that uses the current forms Save this as `forbidden-deferred.tpz`: ```topaz let mut count = 0 let values: Array = [1, 2] let transform: (int) -> int = value => value + 1 match values { case [head, ..tail] => { count = transform(head) + tail.length } case [] => { count = 0 } } print("{count}") ``` Check and run it: ```bash topaz check forbidden-deferred.tpz topaz run forbidden-deferred.tpz ``` The output is: ```text output 3 ``` This single program demonstrates current binding order, named collection types, callable types, lambdas, and list-rest patterns. ## Replace the spelling, not the meaning | If you tried | Use instead | | ------------------------------------- | ----------------------------------------------------------------------------- | | `mut let count = 0` | `let mut count = 0` | | `count++` or `count--` | `count = count + 1` or `count = count - 1` | | `[T]` in a type | `Array` | | `function(T) -> U` in a type | `(T) -> U` | | `args: ...T` | `...args: T` | | `[head, ...tail]` | `[head, ..tail]` | | An anonymous `function (...) { ... }` | A lambda such as `value => value + 1` | | Backticks or `${value}` | Double quotes and `{value}` interpolation | | String indexing or `.length` | `text.scalars()` and Array operations | | `async` or `await` | Explicit `concurrent` execution with a declared timeout where that model fits | Module imports and exports follow fixed syntax rules. Use explicit member imports and inline export declarations from [Modules & Visibility](/en/docs/language/modules-visibility). Do not use `use`, string or template module paths, export lists, wildcard exports, re-exports, or export-site renaming. Invalid spellings above are shown as text reference rather than valid Topaz examples. Run `topaz check` before selecting a backend target. The shared checker rejects these forms at the language boundary rather than asking a generated target to infer their meaning. ## Deferred forms and current alternatives Some rejected forms have current replacements. Other features remain deferred because their semantics are outside the current language specification. Current code should use the documented alternatives; the listed feature families await a future language decision. The unavailable feature families include grapheme-cluster and direct string-slicing syntax, HTML and user-defined template tags, broader or higher-rank generic constraints, recursive type aliases, broad protocol dispatch, package re-exports, general iterable spread, asynchronous cleanup, `async`/`await`, fault catching, panic/throw/catch keywords, and optional-property pipe syntax. Rust ownership and receiver tokens such as `&self`, `&mut self`, `Vec`, and `use crate::` belong in labeled interop specifications. Standard Topaz uses module-level `impl Name`, built-in collection types, canonical imports, and its own record-update forms. Start with [Syntax at a Glance](/en/docs/language-tour) for current forms. Use [Operators & Expressions](/en/docs/language/operators-expressions) for operator corrections and [Modules & Visibility](/en/docs/language/modules-visibility) for the exact program-structure surface. --- route: https://topaz.ooo/v5.20/en/docs/stdlib/overview markdown: https://topaz.ooo/docs/stdlib/overview.md covers: §22 # Standard Library Overview This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. Start with the task rather than the API inventory. The standard library provides a cohesive, statically checked surface for values, collections, files, and host boundaries. Unrecognized names and members trigger checking errors rather than falling through to host-language methods. ## Choose a capability | What you need to do | Start here | Failure shape | | ----------------------------------------------------------------- | ----------------------------------------------------------------- | ---------------------------------------------------- | | Print text, convert a value, inspect Unicode, or transform values | [Core Library](/en/docs/stdlib/core) | A value, `Option`, or the helper's declared `Result` | | Store ordered items, look up a key, or keep unique values | [Collection Operations](/en/docs/stdlib/collections) | Usually a value, `bool`, or `Option` | | Read or write one whole file | `FS.readText`, `FS.writeText`, `FS.readBytes`, or `FS.writeBytes` | `Result` | | Hold a file open across several operations | `open` with `using` or `defer` | `Result`, followed by lexical cleanup | | Understand printing, numbers, order, or a runtime stop | [Runtime Behavior](/en/docs/runtime/behavior) | A documented value or a loud fault | Choose `Array` for ordered elements, `Map` for key-value lookups, and `Set` for unique values. Use `Option` when absence is an expected outcome, such as a missing map key. Use `Result` when external input or host effects can fail with a descriptive reason. A fault indicates that the running program violated a contract, whereas an invalid call or type is rejected statically. ## An everyday recipe This program parses text and transforms an array. `toInt` uses `Option` because input text might not contain an integer. `map` returns a new array preserving the input order. ```topaz let parsed: Option = toInt("42") let values: Array = Array.of(1, 2, 3) let doubled = map(values, value => value * 2) print("{parsed}:{doubled}") ``` ```text output Some(42):[2, 4, 6] ``` `Some(42)` keeps the successful parse explicit. If the text does not represent an integer, the first part evaluates to `None`. The library does not substitute default values like `0` or raise catchable exceptions. ## How the surface is organized Prelude functions and constructors including `print`, `toInt`, `Some`, `None`, `Ok`, `Err`, `map`, `filter`, and `reduce` require no imports. Built-in namespaces encompass focused module families such as `Math`, `Bytes`, `Hash`, `FS`, and `Path`. Package code may also reference the fixed `std.*` modules documented by the active toolchain. These represent checked exports rather than dynamic host namespaces. The built-in namespaces available without an import are `Array`, `Map`, and `Set` for collections, `JSON`, `CSV`, `TOML`, and `URL` for data formats, `Bytes`, `ByteBuffer`, `Encoding`, `Codec`, `Hash`, and `Regex` for text and bytes, `Math`, `BigInt`, and `Decimal` for numbers, `Date` for calendar dates, `FS`, `Path`, and `Cli` for host access, and `Test` for assertions. Both compilers resolve these names the same way, and an imported `std.*` module with the same name shadows the built-in head inside that file. The broader API surface covers encodings, hashes, paths, command-line parsing, regular expressions, CSV, TOML, JSON, URLs, dates, large integers, decimals, generators, parsers, and host capabilities declared by the selected profile. Open the focused page for exact signatures and target boundaries. **Common correction:** Importing a standard module leaves filesystem, network, process, environment, clock, and database authority unchanged. Host effects run in profiles and environments that provide the corresponding capability. ## Exact boundaries - String indexing uses Unicode scalar values unless an API explicitly specifies bytes. - Collection iteration and snapshots preserve their documented order. - Parsing and decoding preserve their declared `Option` or `Result` failure. - Invalid arithmetic operations and contract violations remain loud. - `Test.*`, free `assert`, and `std.test` are available in the test profile. - Unknown direct names, virtual modules, and members produce static diagnostics. ## Continue by task - [Core Library](/en/docs/stdlib/core) - [Collection Operations](/en/docs/stdlib/collections) - [Files & Resources](/en/docs/stdlib/files-resources) - [Runtime Behavior](/en/docs/runtime/behavior) --- route: https://topaz.ooo/v5.20/en/docs/stdlib/core markdown: https://topaz.ooo/docs/stdlib/core.md covers: §9, §22.1, §22.2, §22.5 # Core Library This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. The core library handles common application tasks: printing text, converting values, inspecting Unicode, representing absence or failure, and transforming collections. Start here before reaching for a specialized namespace. ## Choose the everyday helper | Task | Use | Result | | ---------------------------------------------- | ------------------------- | ---------------- | | Print one line | `print("text")` | `()` | | Parse an integer from text | `toInt(text)` | `Option` | | Parse with a radix from 2 through 36 | `toIntRadix(text, radix)` | `Option` | | Convert an `int` to a `float` | `toFloat(value)` | `float` | | Construct a Unicode scalar from its code point | `fromCodePoint(value)` | `Option` | | Inspect a string by Unicode scalar | `text.scalars()` | `Array` | | Transform, select, or fold iterable values | `map`, `filter`, `reduce` | A new value | `Some`, `None`, `Ok`, and `Err` are prelude constructors. An unannotated `None`, `Ok`, `Err`, or empty collection requires an expected type when inference cannot determine every type argument. ## Parse and transform values This program retains failed parses as `None` and counts Korean text by Unicode scalar count rather than byte count. ```topaz function parseAll(values: Array) -> Array> { return map(values, value => toInt(value)) } let scalars = "토파즈".scalars() let parsed = parseAll(["1", "x", "3"]) print("{scalars.length}:{parsed}") ``` ```text output 3:[Some(1), None, Some(3)] ``` `print` accepts a string, so interpolation renders the conversion visible. `toInt` returns `None` for `"x"`. `map` does not turn ordinary absence into a fault. Use `match` when the caller needs to convert absence into a descriptive `Result`. **Common correction:** `toFloat` converts an `int`. It does not parse a string. There is no general free `toString`, `len`, `println`, or `type_of` function. Use interpolation, specific type members such as `.length`, and documented conversion helpers instead. ## Public surface Use these standard names: - `print(string) -> ()` writes one line. `input() -> string` returns the complete host-provided text payload for that run rather than reading line by line. - `toInt(string) -> Option` and `toIntRadix(string, int) -> Option` parse integers. - `toFloat(int) -> float` converts an integer. It is not a text parser. - `fromCodePoint(int) -> Option` constructs one valid Unicode scalar. - `text.scalars() -> Array` exposes a string as Unicode scalars. - `Some` and `None` construct `Option`. `Ok` and `Err` construct `Result`. - `option.okOr(error: E) -> Result` evaluates the error eagerly. `option.okOrElse(f: () -> E) -> Result` calls `f` only for `None`. - `option.map(f: (T) -> U) -> Option` transforms `Some`. `option.flatMap(f: (T) -> Option) -> Option` avoids a nested `Option`. Both preserve `None` without calling `f`. - `result.map(f: (T) -> U) -> Result` transforms `Ok`. `result.flatMap(f: (T) -> Result) -> Result` avoids a nested `Result`. Both preserve `Err` without calling `f`. - Top-level `map`, `filter`, and `reduce` transform iterable values. They operate independently from the receiver helpers above, and none of these helpers catches a fault. The `Bytes`, `ByteBuffer`, `Hash`, and `Codec` sections below list their public members and exact signatures. Similarly named helpers inside a runtime or generated target remain internal to that implementation. ## Assemble bytes in place Use `ByteBuffer.allocate(length, value = 0)` when an algorithm must write many bytes into one contiguous fixed-length value. `set`, `fill`, and `copy` require a `let mut` receiver root. Every range and byte value is checked before the first write, so a fault cannot leave a partially changed target. ```topaz let mut row = ByteBuffer.allocate(8) row.fill(0, 8, 255) row.set(0, 137) let mut frame = ByteBuffer.allocate(12) frame.copy(row, 0, 2, 8) let payload = frame.toBytes() print(payload.toHex()) ``` ```text output 000089ffffffffffffff0000 ``` Assignment shares a `ByteBuffer`'s mutable identity. `ByteBuffer.fromBytes` copies into a new buffer, and `toBytes()` returns an immutable snapshot. A `ByteBuffer` cannot cross a Web export, participate in equality or ordering, serve as a Map or Set key, enter text or JSON, or be used after an invalid range is silently repaired. ## Compute a CRC-32 integrity field `Hash.crc32(data)` returns the unsigned CRC-32/ISO-HDLC value. It is useful for file-format integrity fields, not for authentication or adversarial tamper resistance. ```topaz let checksum = Hash.crc32(Bytes.encodeUtf8("123456789")) print("{checksum}") ``` ```text output 3421780262 ``` `std.hash.crc32` exposes the same operation through the fixed standard-module surface. ## Produce deterministic fixed DEFLATE Use `Codec.deflateFixedCompress(bytes)` for one canonical raw fixed-Huffman DEFLATE block without zlib or gzip framing. ```topaz match Codec.deflateFixedCompress(Bytes.encodeUtf8("hello")) { case Ok(compressed) => print(compressed.toHex()) case Err(error) => print(error) } ``` ```text output cb48cdc9c90700 ``` Input above 256 MiB returns `Err`, setting the operation's search and memory ceiling. Generated Python does not expose this Codec member and declines before writing an artifact. ## Produce deterministic fixed zlib Use `Codec.zlibFixedCompress(bytes)` when the format needs a complete zlib stream: the fixed `78 01` header, fixed-Huffman payload, and Adler-32 trailer. ```topaz match Codec.zlibFixedCompress(Bytes.encodeUtf8("hello")) { case Ok(compressed) => print(compressed.toHex()) case Err(error) => print(error) } ``` ```text output 7801cb48cdc9c90700062c0215 ``` Input above 256 MiB returns `Err`. Generated Python rejects this Codec member before artifact output. This member's API is fixed zlib compression; decompression, levels, dictionaries, streaming, and general Adler-32 operations use their own API surfaces. ## Add Reed–Solomon protection bytes `Codec.reedSolomon255223Protect(bytes)` emits systematic RS(255,223) codewords. Each 223-byte data shard is followed by 32 parity bytes, and the final data shard is zero-padded. ```topaz match Codec.reedSolomon255223Protect(Bytes.encodeUtf8("hello")) { case Ok(protected) => print("{protected.length()}") case Err(error) => print(error) } ``` ```text output 255 ``` The caller retains the original logical length. Input must be non-empty and may use at most 65,535 shards. The function creates protection bytes. It does not detect or repair corruption. Generated Python does not expose this Codec member and declines before artifact creation. ## Exact boundaries - String operations use scalar positions unless their name explicitly specifies bytes. - `Option.map` preserves ordinary absence. `Result.map` does not catch faults. - `map`, `filter`, and `reduce` accept only the fixed iterable families. - Typed JSON needs one written, fully known target and returns a `$`-rooted error path for data failure. - `input` and `print` require an environment that provides their host capability. - `assert` is a function only in the test profile, never a language keyword. Continue with [Standard Library Overview](/en/docs/stdlib/overview), [Null, Option, Result & Faults](/en/docs/language/null-option-result-faults), or [Runtime Behavior](/en/docs/runtime/behavior). --- route: https://topaz.ooo/v5.20/en/docs/stdlib/collections markdown: https://topaz.ooo/docs/stdlib/collections.md covers: §9, §22 # Collection Operations This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. Select a collection based on the questions you ask of the data. `Array` preserves positions, `Map` associates unique keys with values, and `Set` maintains unique elements. Their methods preserve these roles instead of inheriting whatever a host container happens to do. ## Choose the operation by task | Task | Prefer | Result and risk | | ------------------------------------------------ | -------------------------------------- | -------------------------------------------------- | | Read an array position that may be absent | `items.get(index)` | `Option`; never faults for the missing position | | Require an array position as a program invariant | `items[index]` | `T`; faults when the index is invalid | | Find a map key that may be absent | `values.get(key)` | `Option` | | Supply a default for a missing map key | `values.getOr(key, default)` | `V` | | Ask whether a set contains a value | `values.contains(value)` | `bool` | | Inspect map order without sharing later mutation | `keys`, `values`, or `entries` | A copied insertion-order snapshot | | Produce a transformed collection | `map`, `filter`, `sorted`, set algebra | A new collection | | Change the existing collection | A documented mutator on `let mut` | In-place mutation | ## Use Array, Map, and Set together ```topaz let tasks = ["draft", "check", "ship"] let selected = tasks.get(1) let mut scores: Map = Map.new() scores.insert("ada", 10) scores.insert("lin", 12) let mut labels = Set.of("docs") labels.add("tests") print("{selected}:{scores.get("lin")}:{scores.keys}:{labels.toArray()}") ``` ```text output Some(check):Some(12):[ada, lin]:[docs, tests] ``` The two lookups return `Some` because absence is an ordinary possibility. `scores.keys` and `labels.toArray()` preserve insertion order. Mutating `scores` or `labels` later cannot change an already produced snapshot. ## Safe reads, mutations, and faults Array `get`, `indexOf`, `pop`, and `removeAt` return `Option`. `pop` and `removeAt` also mutate, so their receiver root must be `let mut`. `slice` clamps its half-open range and returns a new array. By contrast, direct indexing and an invalid `insert` position are contract violations and fault. Map `get` and `remove` return `Option`. `containsKey` returns `bool`. `insert`, `clear`, and `update` mutate. `mapValues` and `filter` return new maps while preserving the surviving key order. Set `contains` and `remove` return `bool`. `add`, `remove`, and `clear` mutate. `union`, `intersection`, and `difference` return new sets in their documented order. No core Array, Map, or Set operation returns `Result`. Recoverable external failure belongs to the parser, decoder, file, or other effect that produced the value. Handle or propagate that `Result` before applying the collection operation. **Common correction:** `x in map` is not key membership. Use `map.containsKey(x)` or `x in map.keys`. Strings are not arrays and have no `.length`. Use `text.scalars().length` when scalar count is the task. ## Exact boundaries - Map keys and Set elements must be recursively keyable. Invalid types are rejected statically. - Mutating through an immutable binding is a static diagnostic. - Updating an existing map value keeps its position. Removing and reinserting moves it to the end. - Map and Set do not gain general equality from their methods. - Callback order follows the collection's specified iteration order. Continue with [Collections & Comprehensions](/en/docs/language/collections-comprehensions), [Null, Option, Result & Faults](/en/docs/language/null-option-result-faults), or [Standard Library Overview](/en/docs/stdlib/overview). --- route: https://topaz.ooo/v5.20/en/docs/stdlib/files-resources markdown: https://topaz.ooo/docs/stdlib/files-resources.md covers: §4, §13, §14, §22.3, §22.5 # Files & Resources This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. File access crosses an explicit host boundary. Choose a one-shot `FS` operation when a single call completes the task. Choose `File` when multiple reads or writes must share a single open resource and a visible lifetime. ## Choose `FS` or `File` | Task | Use | Exact result | | ----------------------------------------- | ------------------------------- | ------------------------------------------------------------------------------- | | Read a text file in one call | `FS.readText(path)` | `Result` | | Replace a text file in one call | `FS.writeText(path, text)` | `Result<(), string>` | | Read or write immutable bytes | `FS.readBytes`, `FS.writeBytes` | `Result` or `Result<(), string>` | | List one directory | `FS.list(path)` | `Result }>, string>` | | Keep one file open for several operations | `open(path)` | `Result` | The `FS` calls accept `Path | string`. The lifetime-bearing surface is deliberately smaller: ```text api-signatures open(path: string) -> Result file.read() -> Result file.write(s: string) -> Result<(), string> file.close() -> () ``` There is no canonical `File.open`, `File.create`, `File.append`, `readLine`, `writeLine`, or `flush`. ## Read through one managed File Create `note.txt` containing `ready`, then save this program as `files-resources.tpz`: ```topaz function readNote(path: string) -> Result { let mut result: Result = Err("not read") using file = open(path)? { result = file.read() } return result } match readNote("note.txt") { case Ok(text) => print(text.trim()) case Err(error) => print(error) } ``` ```text output ready ``` `open` may fail before a `File` exists, so it returns `Result`. Postfix `?` propagates that error before cleanup is registered. After acquisition, `using` creates one immutable File binding in its child scope and registers one implicit `close`. The result is copied to the outer mutable binding before the scope closes. ## Choose `using` or `defer` - Prefer `using file = open(path)? { ... }` when one block owns exactly one File. The binding cannot escape its child scope as an open lifetime. - Prefer `let file = open(path)?` followed immediately by `defer { file.close() }` when the owning function requires the File across several nested blocks. - An explicit `file.close()` call inside `using` does not cancel the registered close operation. Repeated-close behavior is not portable. Both `using` and `defer` clean up on normal exit and structured exits through `return`, postfix `?`, `break`, or `continue`. A runtime fault stops evaluation immediately; lexical cleanup after that point is unspecified. **Common correction:** do not describe `file.write` as an append operation. Append modes, binary File overloads, buffering, permissions, and line-oriented File methods fall outside the portable File contract. Use the exact `FS` byte operations when whole-file binary data is the task. ## Capability and value boundaries - `File`, `FS`, `std.fs`, and `std.io` require a host profile with filesystem access. - Importing a module grants no path, environment, process, network, or database authority. - Acquisition, read, and write failures stay in `Result`. It is not a catchable exception. - `File` is opaque, non-comparable, and has no portable post-close behavior beyond the listed signatures. - User-defined disposable protocols, multiple-resource `using`, asynchronous cleanup, cancellation cleanup, and module-lifetime finalization are not current language contracts. Continue with [Defer & Resources](/en/docs/language/defer-resources), [Null, Option, Result & Faults](/en/docs/language/null-option-result-faults), or [Standard Library Overview](/en/docs/stdlib/overview). --- route: https://topaz.ooo/v5.20/en/docs/runtime/behavior markdown: https://topaz.ooo/docs/runtime/behavior.md covers: §2, §4, §9, §12, §13a, §15, §21, §22 # Runtime Behavior This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. Runtime behavior matters when a program has checked successfully, but its values, output, or failure still depend on execution. Start with what a user can observe: printed text, computed values, ordered collections, and a loud fault. ## Observe ordinary values Save this as `runtime-behavior.tpz`: ```topaz let values = [10, 20] print("int: {7 / 2} {-7 % 3}") print("float: {1.5 + 1.5}") print("safe: {values.get(9)}") ``` Run it with `topaz run runtime-behavior.tpz`: ```text output int: 3 -1 float: 3.0 safe: None ``` `print` accepts a `string`. Interpolation evaluates integer, floating-point, and `Option` values before the call. There is no separate public `println` prelude helper. Use the exact `print` surface shown here. An `int` is a signed 64-bit integer. Integer division truncates toward zero, and the remainder follows the dividend’s sign. Runtime integer overflow and division by zero trigger a fault instead of wrapping. A `float` is IEEE-754 binary64, following floating-point rather than decimal arithmetic. `values.get(9)` returns `None` because a missing element is an ordinary outcome for the safe lookup API. ## Observe a violated contract Contrast that lookup with direct indexing: Save this second snippet as `intentional-fault.tpz`: ```topaz let tasks = ["Run first program", "Build application"] print(tasks[2]) ``` `topaz run intentional-fault.tpz` exits nonzero and reports the source span: ```text output error[TPZ4001]: index 2 is out of bounds for an array of length 2 --> intentional-fault.tpz:2:7 | 2 | print(tasks[2]) | ^^^^^^^^ ``` A fault is not `None`, `Err`, or another value. It aborts evaluation and cannot be caught by postfix `?`, optional operators, or `concurrent`. Constantly invalid arithmetic may instead be rejected during checking. The runtime fault applies when the invalid condition is reached during execution. ## Choose the observable failure model | Situation | Use or expect | | ------------------------------------------ | ------------------------------------------- | | A value may normally be absent | An `Option`, such as `get` returning `None` | | A caller can respond to a reason | A `Result` with `Ok` or `Err` | | Executing code violates a runtime contract | A fault and nonzero process exit | | Source is invalid before execution | A static diagnostic from `topaz check` | Direct array access, dynamic integer division or remainder by zero, checked integer overflow, a dynamic zero range step, a dynamically negative integer exponent, and a runtime `match` miss are named fault sources. Prefer a safe library operation when absence is part of the task. Call depth is bounded at 1000 frames on every route. A deeper call reports `TPZ5009` at the call site, and no command line option changes the limit. ## What remains stable String comparison uses exact Unicode scalar sequences without implicit normalization. Arrays preserve their element order. Map key snapshots use key insertion order, and collection operations preserve the order specified by their own contract. Nominal values keep their constructor identity when the canonical renderer requires it. Backend representations may vary across targets while observable execution results remain equal. Continue with [Null, Option, Result & Faults](/en/docs/language/null-option-result-faults) for the value-level decision, [Concurrency](/en/docs/language/concurrency) for timeout and ordering boundaries, and [Core Library](/en/docs/stdlib/core) for the exact everyday helper surface. --- route: https://topaz.ooo/v5.20/en/docs/philosophy markdown: https://topaz.ooo/docs/philosophy.md covers: §0, §20, §21 # Language Philosophy This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. Topaz treats a program as an executable statement of intent, readable enough for review and precise enough for execution. The goal is not to maximize syntax, but to make ordinary programs direct and consistent. ## One clear form Ordinary code has one preferred form. Domain names can remain in Unicode, effects and failures stay visible, and unready features remain out of examples. This keeps human-written code and generated code equally readable. ## Predictable execution When a selected target supports a program, the same source preserves its visible behavior whether it runs directly, compiles to a native binary, or is emitted as Python. The current SPEC defines values, ordering, errors, and cleanup while leaving implementations free to choose their internal representations. A target that cannot preserve required behavior must reject the program explicitly. ## Multiple useful outputs Rust, Python, and Web targets address distinct deployment requirements without creating separate Topaz dialects. A program is checked once, then routed to a supported output path. Unsupported constructs are rejected rather than assigned target-specific meanings. Behavioral differences between paths accepting the same source are treated as compiler bugs rather than language choices. ## Coherent grammar, explicit semantics A focused grammar and standard library provide readers, tools, and implementations with the same target. Topaz grows, and a new feature arrives with defined behavior, a working implementation, examples, and documentation. ## Real programs guide growth Maintained applications reveal real library and runtime demand across strings, numbers, collections, files, packages, and diagnostics. The Lispex test and integration surface exercises recursion, portable values, state management, printing, and error handling in end-to-end workflows. Working code guides implementation and language priorities without accumulating one-off syntax. ## Current product status The documentation presents current-release capabilities, target-specific behavior, and planned additions in their relevant contexts, while internal development procedures remain in engineering records rather than public language rules. ## Related documentation - [Introducing Topaz](/en/docs/introduction) - [History](/en/docs/project/history) - [Language and Compatibility](/en/docs/project/conformance) - [Toolchain Status](/en/docs/toolchain/status) --- route: https://topaz.ooo/v5.20/en/docs/project/history markdown: https://topaz.ooo/docs/project/history.md covers: §0 # History This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. Topaz grows through public minor releases. Each public minor marks the product boundary users install. ## Releases ### 5.20 Completed LIT and aligned the Stage 0 and self-hosted compilers. The public source release included a selectable self-hosted compiler and an embedded Lispex evaluator updated to 1.20.0, followed by fixes for Python behavior, self-hosted diagnostics and type resolution, and npm installation. | Patch | Visibility | User-visible result | | ---------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | 5.20.2 | Public | 5.20.2 fixes `topaz fmt`, which could move a single-line import into a multi-line import list and leave a file that no longer parsed. Imports are sorted as whole statements, and `topaz fmt` writes no files when a formatted result does not parse. The npm installer falls back to the release manifest. | | 5.20.1 | Public | 5.20.1 fixes the defects found by a field report on 5.20.0. Generated Python accepts ranges in `map`, `filter`, and `reduce`, keeps a binding per iteration, renders `URL` and `TOML` as the interpreter does, and stops at depth 1000 with `TPZ5009`. The self-hosted compiler reports `TPZ5022` for oversized integers, resolves `Date`, `BigInt`, and `Decimal`, and skips unused input. The npm installer falls back to GitHub. | | 5.20.0 | Public | 5.20.0 is the first public release of this line. The self-hosted compiler written in Topaz ships in the binary and is selectable with `--compiler self`, and the embedded Lispex evaluator moves to Lispex 1.20.0. The source is public at topaz.ooo/dl. | ### 5.19 Expanded Lispex decision applications, completed LIT's 205 capabilities across targets, and updated the embedded Lispex evaluator to 1.19.4 with matching behavior across execution hosts. Compiler and runtime work aligned language behavior, diagnostics, and editor support while reducing repeated work and improving package and file handling. | Patch | Visibility | User-visible result | | ---------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 5.19.815 | Internal | The shared corpus manifest reader admits a fixture row only when phase, versions, and result are present, the source is exactly one file or directory, the outcome carries a matching code, and the recorded totals agree. Incomplete rows are rejected instead of becoming empty values. | | 5.19.814 | Internal | Manifest reading for the v5.4 package, check, and performance smoke corpora goes through the same decoder as `topaz check-corpus`. A malformed or unknown manifest line is rejected everywhere instead of being quietly ignored by a second reading path. | | 5.19.813 | Internal | The v5.4 package run and test corpora execute rows that expect a runtime error, so the expected fault code, its message, and the output printed before the fault are all checked. A new package fixture prints, then stops with `TPZ4002` for integer division by zero. | | 5.19.812 | Internal | Expected output files in the corpus keep their meaningful trailing blank lines. Only one final newline counts as the file terminator, so a program that ends with an extra blank line is compared exactly instead of passing against a stripped transcript. | | 5.19.811 | Internal | `topaz run` and the native build now accept arguments after `--` only when the program exports `main(args, stdin)`. A program without that entry point is rejected before it starts instead of silently dropping the arguments. | | 5.19.810 | Internal | The self-hosted compiler keeps piped standard input under `topaz test`. Test mode used to replace it with an empty string, so a program reading `main(args, stdin)` with `--compiler self` now receives the same input it gets from `topaz run`. | | 5.19.809 | Internal | Package building, running, and documentation share one path that resolves and checks a package once instead of repeating that work. The commands keep their existing behavior, diagnostics, and output. | | 5.19.808 | Internal | `topaz test` hands piped standard input to a program that exports `main(args, stdin)`, matching `topaz run`. Package tests in the corpus also replay their external functions and apply the same exit status rule as the command line. | | 5.19.807 | Internal | Checking, editor support, and the corpus phases build the checker's module input through one shared projection. Module identity, entry and external roles, source slices, and module order are preserved, and every caller keeps its existing diagnostics and behavior. | | 5.19.806 | Internal | An import directory that exists but cannot be read is no longer reported as missing. Unreadable directories, unreadable sources, and entries with invalid UTF-8 names report `TPZ3003` instead of `TPZ3001`, both in the compiler and in the editor server. | | 5.19.805 | Internal | Corpus sidecar files such as `.stdin`, `.args`, and `.defer` count as absent only when they really do not exist. An unreadable or non UTF-8 sidecar fails with its path instead of silently dropping input or an expected check, and virtual file blocks require a unique path and the exact delimiter. | | 5.19.804 | Internal | Directory shaped corpus fixtures are read exactly. A missing or unreadable fixture file can no longer produce the diagnostic its row expects and pass by accident, and the reader rejects non UTF-8 paths, irregular entries, duplicate paths, and an entry marker that disagrees with the manifest. | | 5.19.803 | Internal | The corpus manifest reader reports malformed input with a path and a line number. Repeated or missing totals, counts that are not numbers, duplicate or unquoted fixture keys, and a bad clock value are rejected, and an empty file is no longer mistaken for a manifest that declares zero rows. | | 5.19.802 | Internal | The interpreter's exec corpus check no longer restates its own per area counts as one derived total. Each area count, the parsing and execution of every row, exact output transcripts, and stop code and message matching all remain. | | 5.19.801 | Internal | The eight printing programs from the examples document run straight from their canonical sources in the corpus. Eight byte identical copies and the check that compared them with the originals are gone, while every expected output file still observes the same program output. | | 5.19.800 | Internal | The interpreter matches an external function against a function type such as `(int) -> int`, reading its parameter count, defaults, and variadic flag the way generated code does. A package that replays external functions runs correctly under both `topaz run` and `topaz build --run`. | | 5.19.799 | Internal | Virtual files in the deterministic host keep raw bytes, so a program that writes a byte such as `0x80` reads back exactly what it wrote. Text reading and writing stay UTF-8 views, and directory listings report raw byte sizes. | | 5.19.798 | Internal | Assembling the compiler's observation output ends in the single routine that already owned the root digest, manifest entries, and size budget. Member order, hashes, manifest bytes, budget error text, and the produced result stay the same. | | 5.19.797 | Internal | Preview projections for the editor build one span index per module and read it for every scope, declaration, reference, export, and typed fact, instead of scanning the whole syntax tree again for each one. First match behavior and every reported fact are unchanged. | | 5.19.796 | Internal | A decimal integer literal one past the signed 64 bit maximum keeps its written spelling in the syntax observation instead of becoming an empty value. Stage 0 and the self-hosted compiler report it the same way, and the duration ceiling observation is unchanged. | | 5.19.795 | Internal | One observation check no longer runs the same request twice and compares the result with itself. It compiles and observes once while keeping intake validation, comparison against deliberately different bundles, exact projection contents, rejection of corrupt projections, and the resource limits. | | 5.19.794 | Internal | Constructor calls such as `Html.Text("ready")` and `Command.Dispatch(Msg.Ready)` take their exact callee and specialized result type from the surrounding context. Four hard coded exceptions for the `std.dom` module that forced results to an unknown type were removed. | | 5.19.793 | Internal | A receiver method call reports the module that defines the method, so a local target reads as `main::Point` and an imported one as `model::Point` for both direct and pipeline calls. The 5.20 export view puts method parameters, return types, and that identity in one canonical form. | | 5.19.792 | Internal | Call observations carry callee evidence from the checker itself for ordinary, optional, and member calls, and a pipeline stage registers a callable target only when the reference is unique and its member name matches. Both compilers report the same shapes for direct, optional, and pipeline calls. | | 5.19.791 | Internal | The self-hosted checker types a receiver call on a generic optional value, such as `value?.run(41)` on `Option`, the way Stage 0 does, inferring arguments once and checking each union arm against its own signature. The `TPZ5006`, `TPZ5005`, and `TPZ5003` reports are unchanged. | | 5.19.790 | Internal | Optional calls such as `Option>?.get(0)` and `runner?.run(41)` keep their specialized callee type through checking, so the next stage sees the same call representation as the self-hosted compiler. Runtime behavior and language meaning are unchanged. | | 5.19.789 | Internal | A call to a user defined receiver method keeps the function type specialized at the call site, so a method called with a named argument and a default lowers to the same representation in Stage 0 and in the self-hosted compiler. Runtime behavior and language meaning are unchanged. | | 5.19.788 | Internal | The self-hosted compiler places function parameter defaults beside the parameters, in the order the parameters appear, matching Stage 0 for ordinary functions and methods. The runtime reads that shape and still accepts the older nested shape from sealed compiler images. | | 5.19.787 | Internal | The published lowered view of the self-hosted compiler no longer reports an empty call on every operation. It rebuilds the call object from the callee, receiver, argument, and evaluation order facts it already held, covering value, member, and pipeline calls. | | 5.19.786 | Internal | Stage 0 lowering agrees with the self-hosted one on binding identities for ordinary functions, on treating a payload free enum variant in a pattern as a constructor rather than a local binding, and on the representation evidence it emits. Schemas, diagnostics, and execution are unchanged. | | 5.19.785 | Internal | An optional call inside a pipeline, such as `lead \|> absent?.get(sideEffect(_))`, keeps its short circuit. When the receiver is absent the stage returns `None` without evaluating the written arguments, and the compiled product carries and checks that guard instead of dropping it. | | 5.19.784 | Internal | Six comparisons in the type checking suite put a value against itself or its own clone. They now use independently built `Array`, `Map`, record, and foreign operands, so they observe structural equivalence and the static rejection of non comparable values rather than identity. | | 5.19.783 | Internal | A top level initializer may refer to a later binding from the right side of `&&`, `\|\|`, or `??`, or from the arguments of an optional call, since those parts run only when the left side or receiver allows it. If one really runs too early, the existing runtime unbound error still applies. | | 5.19.782 | Internal | A source file that exists but cannot be read, or that is not valid UTF-8, is no longer reported as missing. Entry and import resolution report the loader diagnostic `TPZ3003` for it instead of the file not found `TPZ3001`. | | 5.19.781 | Internal | One parser helper that had no consumer outside a check comparing it with itself is now private, and that check is gone. The staged parsing surface used for token retention and observation is unchanged, and so are parsing, syntax trees, and diagnostics. | | 5.19.780 | Internal | The frozen v5.1 fixture counts and the v5.2 area table live in one place that both the parser checks and `topaz check-corpus` read. A check that only proved two copies of the same table agreed was removed, and the counts themselves are unchanged. | | 5.19.779 | Internal | A duplicate name in a selected import reports `TPZ2011` exactly once for each later offending item, in source order. When a name collides both as an exported source and as a local binding, the exported source takes precedence, and both compilers agree. | | 5.19.778 | Internal | `None` is the `Option` constructor and cannot be used as a binding name, so declaring a constant named `None` is rejected with `TPZ2012` at the name itself. Stage 0 and the self-hosted parser apply the same rule. | | 5.19.777 | Internal | Four source findings in the Python differential harness were cleaned up, with temporary values moved straight into their builders and two conditions simplified. The compiler, the runtime, and the language source did not change, and the harness keeps comparing its fixtures with no mismatch. | | 5.19.776 | Internal | A field named with a keyword, reached after `.` or `?.` or written at a record field label, no longer switches the layout into a construct such as `match` or `concurrent`. Multi statement blocks after such a member access and nested record patterns after such a field parse correctly. | | 5.19.775 | Internal | A pipeline stage finds the `_` placeholder anywhere in its arguments and stops at a nested pipeline. A stage without a placeholder still receives the piped value as its first argument, and a stage with one keeps every written positional, named, and spread argument and evaluates them in source order. | | 5.19.774 | Internal | The Stage 1 and Stage 2 compiler image identities are written once while the toolchain is built, so asking for one no longer hashes the whole embedded image again on every call. The image bytes, their manifests, and the separate check that validates an image before it runs are unchanged. | | 5.19.773 | Internal | The required Rust toolchain version is read from the package metadata instead of being written out in three separate places. The sealed manifests still record the same version, and image bytes, runtime behavior, and language meaning are unchanged. | | 5.19.772 | Internal | Building the compact compiler image no longer collects three temporary lists only to read their lengths and serialize them right away. The image schema, ordering, and bytes stay identical, as do the manifests and runtime behavior. | | 5.19.771 | Internal | The Stage 2 fixed point tool requires exactly one input path and rejects a missing or extra argument before it reads anything. Path names are taken as the operating system gives them, and the produced report keeps its existing shape. | | 5.19.770 | Internal | A retired canary tool for the Stage 1 compiler image was removed. It had no caller left and duplicated response and provenance handling that the official generation and comparison paths already own. | | 5.19.769 | Internal | The generator for the second Stage 2 round reads its options in one pass and rejects a missing value, a duplicate option, or an unknown one before it generates anything. Option handling, the digest helper, and the atomic write are now shared with the Stage 1 generator. | | 5.19.768 | Internal | The Stage 1 image generator parses its arguments once and rejects an option without a value, a duplicate or unknown option, and an input manifest it would not use. The interpreted producer applies only when no producer is named, and the linked one requires and reads its manifest first. | | 5.19.767 | Internal | The Stage 1 comparison tool no longer generates the same output twice and calls the match stability. It generates once and requires the linked and the interpreted path to produce equal source, while keeping the compile and run check, image identity, and rejection of corruption and a wrong producer. | | 5.19.766 | Internal | Two checks that produced the same output twice and compared it with itself were removed, and their names now describe what they really observe. Completed status, the typed, lowered, export, and generated surfaces, manifest intake, and compiling and running the result to `42` all remain. | | 5.19.765 | Internal | Test fixture hosts borrow their source text instead of requiring a value that lives for the whole program, so three dynamically built sources no longer have to be leaked. The fixture text and everything the checks observe are unchanged. | | 5.19.764 | Internal | Numbers in the JSON the compiler emits are built directly instead of being printed to text and parsed back through the general reader. Exact decimal rendering, key order, field names, counts, ordinals, and spans are unchanged. | | 5.19.763 | Internal | Projecting the target adapter builds one index per module instead of scanning the same syntax tree again for every parent, field, and name lookup. Name counts, the first exported function chosen, and the member, payload, default, and shape projections stay the same. | | 5.19.762 | Internal | The request layer of the self-hosted compiler drops three record types that duplicated the canonical shapes and the two conversions that rebuilt every host fact, directory entry, and generated standard module. Field names, shapes, fact order, and duplicate handling are unchanged. | | 5.19.761 | Internal | The profiler in the self-hosted compiler indexes references with typed nested maps instead of packing identities into delimiter strings, and one catalog owns the original references with their target and member indexes. Profile decisions, codes, notes, spans, and ordering are unchanged. | | 5.19.760 | Internal | The emitter in the self-hosted compiler walks the lowered operations once and keeps one catalog with a first occurrence index and the sorted runtime leaves, instead of building a map and rescanning the same list. The operation limit, closed integer evaluation, and the produced bytes are unchanged. | | 5.19.759 | Internal | The checker in the self-hosted compiler keeps nineteen internal record types to itself, leaving its single check entry point as the only export. Every field, lookup, phase, diagnostic, and span behaves as before. | | 5.19.758 | Internal | Name resolution in the self-hosted compiler looks up host facts through a path index per kind instead of scanning the whole fact list on every read, containment test, and directory listing. Query order, first match on duplicates, statuses, payload bytes, and diagnostics are unchanged. | | 5.19.757 | Internal | The parser state record in the self-hosted compiler is private now, matching Stage 0 where the same state sits behind the public entry points. The exported parse function, syntax trees, diagnostics, notes, spans, and recovery are unchanged. | | 5.19.756 | Internal | An unused Unicode version constant was removed from the generated tables. The generator keeps the single authority for the pinned Unicode 16.0.0 data, the generated header still records it, and no character range or mapping byte changed. | | 5.19.755 | Internal | Three Unicode helpers in the self-hosted compiler that were only used inside their own module became private, leaving seven exported functions. Signatures, bodies, callers, and the Unicode 16.0 tables are unchanged. | | 5.19.754 | Internal | Four lexer helpers in the self-hosted compiler kept an export they no longer needed after name resolution moved to token graph discovery. They are private now, leaving the lexing entry point and the keyword lookup as the only exports, with signatures, bodies, and callers unchanged. | | 5.19.753 | Internal | The payload record and three schema constants stay inside the emitter module of the self-hosted compiler, leaving its single emit step as the only export, and one module dropped an import it did not need. Schema values, the produced bytes, and provenance are unchanged. | | 5.19.752 | Internal | One provenance schema constant used only inside its own module is no longer exported. The value itself, the bytes it produces, and the separate validation on the reading side are unchanged. | | 5.19.751 | Internal | An exchange record that nothing used was removed along with its unreferenced import. Request intake keeps its own private record, and package processing, diagnostics, spans, and output shapes are unchanged. | | 5.19.750 | Internal | A leftover local value in the import handling of the self-hosted parser was removed. Import paths, selections, aliases, parser position, recovery, diagnostics, and spans behave exactly as before. | | 5.19.749 | Internal | The internal execution supervisor on macOS writes its request and drains output at the same time, under the existing time and memory guard, so a child that stops reading cannot stall it. A result file that carried doubled braces and broke JSON reading is emitted as one valid object again. | | 5.19.748 | Internal | The internal sandbox supervisor on macOS writes a framed request to the child and drains its output at the same time, under the active wall clock guard. A child that never reads its input can no longer block the supervisor before the timeout takes over. | | 5.19.747 | Internal | Installing, staging, and cleaning up managed artifacts now requires every existing parent directory to be a real directory rather than a symbolic link. Legacy Python migration leaves a linked cache directory and the bytes it points to untouched. | | 5.19.746 | Internal | The self-hosted compiler applies profile checking to every final compile result, not only to a successful one. A rejected program keeps its profile findings such as `TPZ2001` alongside its syntax error, matching what the Stage 0 compiler reports. | | 5.19.745 | Internal | The command line no longer builds and validates a placeholder compilation record that was thrown away without ever reaching the real result. Check output now comes straight from the actual compilation, and what it reports is unchanged. | | 5.19.744 | Internal | Migration of a pre-5.6.1 Web output directory claims a folder only when the loader carries all three signatures that Topaz itself generates. A user bundle that merely mentions Topaz keeps its own JavaScript, WASM, and `Cargo.toml` files instead of being cleaned up. | | 5.19.743 | Internal | Windows extended drive and UNC paths are converted to command-safe form by one shared routine, replacing two identical copies. Workspace root, build output, source, and log paths behave exactly as before. | | 5.19.742 | Internal | Discovery of an installed Lispex product reads its four artifact identities from one fixed ordered table and checks order, status, host, path, size, and hash through a single shared admission. Reported information and run requests are unchanged. | | 5.19.741 | Internal | Lispex application planning and host admission carry one locked rule identity record instead of six repeated fields, and the bounded and full profiles build that record the same way. Application behavior together with lock and artifact checking is unchanged. | | 5.19.740 | Internal | Import cycle reporting walks a sorted neighbour catalog inside each cycle group instead of rescanning every edge at each step. The `TPZ3006` message, its position, and the rule of one diagnostic per cycle stay the same. | | 5.19.739 | Internal | Building a preview observation no longer serializes a request, parses it again to correct its phase, and encodes it once more. Shared files are assembled once for the caller's actual phase, and observation contents, schemas, and diagnostics are unchanged. | | 5.19.738 | Internal | The checker carries one call site record through call resolution, completion, and type scheme application instead of passing its seven fields apart. Module checking also returns a named result record rather than a deeply nested tuple, with language semantics and diagnostics unchanged. | | 5.19.737 | Internal | Runtime type matching in the interpreter keeps its alias cycle stack and fault position in one state value, and enum, record, and newtype checks read the runtime definition directly. Interpreter behavior, fault codes, and fault positions are unchanged. | | 5.19.736 | Internal | The Python backend builds each module's runtime export table once and shares it read only with namespace imports, where it used to be deep copied. Generated Python code, its order, and its fault behavior are unchanged. | | 5.19.735 | Internal | A compiled program whose comprehension body does not match its collection kind is rejected with source context before the body runs. Such a mismatch used to accumulate into the wrong place and silently return an empty collection. | | 5.19.734 | Internal | One check during the build validates the whole generated compiler manifest, rejecting duplicate JSON keys and unknown fields, and validates the compiler payload before it is compacted. The runtime reads that validated result instead of parsing the manifest bytes again. | | 5.19.733 | Internal | Checks for the Lispex product describe the release that is deployed now, where every hosted capability is implemented and none is unsupported. History-only material moved behind an opt-in, and the archived evidence itself is untouched. | | 5.19.732 | Internal | Copying a package into `vendor` validates the candidate name, version, and hash before touching the destination, refuses symbolic link parents, and rolls back unfinished replacements on failure. `topaz.toml` and `.tpz` sources must be plain files. | | 5.19.731 | Internal | `topaz.toml` rejects unknown fields and refuses a dependency that declares both a version and a path. `topaz.lock` must hold exactly one root row and one row per direct dependency, and manifest and lock reads stay inside the package, refusing links that point outside it. | | 5.19.730 | Internal | Replay fixtures and optional artifacts of a package are read through the strict package reader, which refuses a symbolic link in any path segment, an escape outside the package root, and anything that is not a regular file. Lock checking and runtime replay share the same bytes. | | 5.19.729 | Internal | An exported `let` or `const` is recognised by the self-hosted route and by its documentation and export listing, instead of being reported as having no typed declaration. Each module also runs its top level exactly once, so an exported initializer no longer takes effect twice. | | 5.19.728 | Internal | `Map.filter` and `Map.mapValues` take an insertion ordered snapshot of pairs before calling back, so a callback that changes the same Map no longer crashes the generated Rust program. Changes still persist in the source Map and the number of callback calls does not grow. | | 5.19.727 | Internal | Named and qualified JSON schema emission passes its eight fixed inputs as one value and leaves only the traversal state separate. Schema lookup, generic substitution, recursion, and fault behavior are unchanged. | | 5.19.726 | Internal | The generated Rust backend names the module, function, and span hierarchy it looks up, groups the fixed inputs of `for` pattern emission into one value, and stops carrying a declaration catalog nothing read. Generated code and fault behavior are unchanged. | | 5.19.725 | Internal | At the native call boundary a byte record is matched by the identity of the module that declares it under Topaz 5.20, not by its short name. A same-named record from another module is no longer accepted there, while Topaz 5.19 keeps its short name behavior. | | 5.19.724 | Internal | The rule that picks an explicit declaration identity when one exists and the published name otherwise lives in one place for each runtime. Equality, ordering, Map and Set keys, record spread, unwrapping, and pattern and type tests all read it, with behavior unchanged. | | 5.19.723 | Internal | Set snapshots are created by one routine where two identical ones stood side by side. Iteration, `Set.toArray`, `union`, `intersection`, and `difference` keep the same order, deduplication, and results. | | 5.19.722 | Internal | Pending callback state is the only place the runtime reads the callee and the item for `Array.sortedBy`, `sortBy`, and `retain`, rather than carrying copies beside it. Call order, argument count, kept item semantics, and faults are unchanged. | | 5.19.721 | Internal | Array and Map higher order callbacks share one step protocol that used to be written twice. `map`, `filter`, `reduce`, `Map.filter`, and `Map.mapValues` keep the same call order, results, and faults across every runtime. | | 5.19.720 | Internal | Alias, record, enum, and newtype lookups used while building a typed JSON schema share one declared shape instead of repeating it four times. Lookup results, substitution, recursion limits, and typed JSON faults are unchanged. | | 5.19.719 | Internal | Two checks over schema type syntax share one traversal in source order instead of each listing every kind of type node. Rejection of an imported schema before Topaz 5.20 and of a block local alias behaves exactly as before. | | 5.19.718 | Internal | Namespace qualification of a type reuses the shared structural walk instead of rebuilding every kind of type by hand, replacing only the enum, record, and newtype nodes the namespace table names. Typing results are unchanged. | | 5.19.717 | Internal | Two more hand written inventories of type shapes in the checker were replaced by the shared walks. Collecting exported nominal identities and sealing the generated Lispex carrier types keep the same results. | | 5.19.716 | Internal | Renaming nominal identities in the checker happens in one routine that had been written twice, once for Topaz 5.19 selected import rebinding and once for Topaz 5.20 module stable canonicalization. Both keep their existing results. | | 5.19.715 | Internal | Detection of a top level reference to a function or `let` that is not available yet uses one lookup and one cutoff rule for both expressions and blocks. The `TPZ5002` message, the position of the first violation, and the exemption for `const` are unchanged. | | 5.19.714 | Internal | Collecting inference variables in the checker states its condition once instead of nesting two checks. First seen ordering and duplicate suppression are unchanged. | | 5.19.713 | Internal | Building a JSON schema key takes only the values it actually uses, where a declaration catalog used to pass through untouched. Key bytes, order, substitution, recursion limits, and typed JSON results are unchanged. | | 5.19.712 | Internal | The cycle anchor is passed directly while a module cycle is reported, without an extra layer of reference. Cycle detection, the reported path, and its message are unchanged. | | 5.19.711 | Internal | Module exports are classified only as a type or a value, where function, `let`, and `const` were tracked apart although nothing read the difference. Import eligibility, suggestions, and every export diagnostic behave as before. | | 5.19.710 | Internal | Resolving a value reference reports back only the cases that need a follow up diagnostic, namely an imported binding and a namespace. An ordinary module binding reports nothing, and reads, writes, and diagnostics are unchanged. | | 5.19.709 | Internal | Deciding whether a local value shadows an imported namespace uses the same lexical scan as every other local lookup, where a second almost identical scan stood. Shadowing, precedence, and namespace diagnostics are unchanged. | | 5.19.708 | Internal | Resolving a named type checks the lexical scopes first and consults the module table only when no local binding exists, rather than always querying both. Shadowing, precedence, and reporting of unresolved references are unchanged. | | 5.19.707 | Internal | Checking exported signatures walks each public type where it is found instead of gathering them all into a list first. The `TPZ3014` message for a private type in a public signature keeps its position and its order. | | 5.19.706 | Internal | Import surface validation no longer receives the current module table it never read. The `TPZ3010` and `TPZ3009` messages, their positions, order, and suggestions are unchanged. | | 5.19.705 | Internal | Recording a value reference reports what it found, so the identifier and assignment paths stop repeating the scope scan and the table lookup. The `TPZ3012` and `TPZ3015` messages keep their positions and order. | | 5.19.704 | Internal | Resolving a namespace member looks its export up once and reuses that result for classification and for every diagnostic branch. The `TPZ3013`, `TPZ3010`, and `TPZ0002` messages and the private record default exception are unchanged. | | 5.19.703 | Internal | A qualified type name resolves its namespace head once and reuses that target for member, export, and reference handling. The `TPZ3013` message for an invalid head keeps its position and order. | | 5.19.702 | Internal | In the self-hosted compiler, resolving a reference hands back the module binding it found, so the assignment and value paths no longer repeat both the local and the table lookup. The `TPZ3013` and `TPZ3005` messages are unchanged. | | 5.19.701 | Internal | A selected import alias used in a type position records the local alias name, so the self-hosted compiler and the Stage 0 compiler agree on that reference target. A value position still records the module it came from and the exported name. | | 5.19.700 | Internal | Building a module table keeps a missing binding as an absent value rather than a negative index while it chooses the export index and decides between a collision and an insertion. The `TPZ3008` message and duplicate export behavior are unchanged. | | 5.19.699 | Internal | Namespace member resolution keeps a missing export as an absent value instead of flattening it into a negative index and an empty namespace string. Member semantics, the private record default exception, and every diagnostic are unchanged. | | 5.19.698 | Internal | Read only lookups in the self-hosted compiler take the module table record directly rather than asking for an index that might be a negative marker and then indexing an array. Diagnostics, order, and the import and export surfaces are unchanged. | | 5.19.697 | Internal | Eight paths that only read a module binding take the binding record directly instead of an index that might be a negative marker. Reference targets, namespace and export status, and diagnostics are unchanged. | | 5.19.696 | Internal | A protocol declaration is recorded directly as a declaration fact instead of first being built as a temporary whole binding. Order, positions, namespace and export status, and diagnostics are unchanged. | | 5.19.695 | Internal | Declaration facts are projected from the existing module binding with the final namespace and export status passed explicitly, instead of rebuilding a whole binding for each one. Facts, order, positions, and diagnostics are unchanged. | | 5.19.694 | Internal | Each exported name is looked up once as a single entry carrying both its binding index and its namespace, replacing two separate lookups over the same name. Export facts, their order, suggestions, and diagnostics are unchanged. | | 5.19.693 | Internal | Each module keeps one sorted list of its unique export names, which empty export detection, suggestions, and export facts all reuse. Every use previously rescanned the bindings and sorted a fresh array. | | 5.19.692 | Internal | Export facts are appended in module order with each module's names already sorted, replacing a helper that rescanned and shifted the whole accumulated list for every export. The resulting order and content are identical. | | 5.19.691 | Internal | Export signature validation takes the current module table by position, since the tables are built in module order. A redundant identity lookup that could silently skip the check is gone. | | 5.19.690 | Internal | Module source text is encoded to UTF-8 once when the module is parsed, and the later passes share that view. Five passes each used to encode the same whole source again. | | 5.19.689 | Internal | The admitted module list is the only record of which modules parsed successfully, where a separate identity set was kept beside it. Discovery, edge pruning, cycle reporting, and dependency order are unchanged. | | 5.19.688 | Internal | Admitting an import is decided once and produces its path, source, physical alias, and standard module origin together, where six places used to rebuild the path and rescan the same facts. Query order, routing, alias collisions, and diagnostics are unchanged. | | 5.19.687 | Internal | A module rejected for sharing a physical alias no longer asks the host about files its own imports name. The separate scanner that ran before parsing is gone, the module graph built by the parser is the only place imports are found, and the `TPZ3004` message is unchanged. | | 5.19.686 | Internal | A brace written inside a comment after an import no longer hides the import that follows it. Module discovery skips line and block comments before it counts braces, so a source with such comments needs no extra rounds of questions about files. | | 5.19.685 | Internal | Module discovery accepts only a real identifier as a module path segment, so `import function`, `import *`, `import lib.function`, and `import lib.sub"text"` no longer make the compiler ask the host for files. None of these forms produced an import edge before either. | | 5.19.684 | Internal | Spaces, newlines, and comments around the dot in a dotted import are read the way the parser reads them, so `import lib . sub` asks the host for the nested module file rather than the parent one. A dot with no segment after it discards the path instead of leaving it half formed. | | 5.19.683 | Internal | A statement separator after an `import` head ends the import for module discovery too, so `import` followed by a newline, a semicolon, or a line comment no longer makes the compiler ask the host for a file. The parsers never read those as imports. | | 5.19.682 | Internal | Module discovery ends a line comment only at a line feed and no longer treats a form feed as whitespace, matching the lexer. A commented out import followed by a lone carriage return no longer makes the compiler ask the host for that file. | | 5.19.681 | Internal | Module discovery ends a block comment at the first closing marker, as the language specification defines it, instead of tracking a nesting depth. Resolution results, diagnostics, and the questions asked of the host are unchanged. | | 5.19.680 | Internal | Finding where an identifier ends is done by one shared routine that both the lexer and module discovery use. Duration suffixes, labels, keywords, tagged strings, and the Unicode identifier rules keep their behavior. | | 5.19.679 | Internal | Identifier scanning in the self-hosted lexer reads character width through one shared path instead of two copies. Which characters may start or continue an identifier, including Unicode letters, numbers, and emoji, is unchanged. | | 5.19.678 | Internal | Line-continuation tokens are declared once per layout engine instead of in two long lists. Which tokens continue a line before or after a break, including the leading-only `else` and the trailing-only comma and fat arrow, is unchanged. | | 5.19.677 | Internal | Recovery from a line break inside a single-line interpolation runs through one shared path in the Stage 0 lexer, whether the break is reached directly or from a nested string. Tokens, diagnostics, and indentation checks stay the same. | | 5.19.676 | Internal | String scanning in the self-hosted lexer derives the delimiter width from whether the string is multiline rather than keeping a second copy of it, removing a state where the two could disagree. Tokens, diagnostics, and spans are unchanged. | | 5.19.675 | Internal | When a multiline string sits inside a single-line interpolation, the Stage 0 lexer finds the enclosing interpolation once instead of scanning for it twice. Line-break recovery, tokens, diagnostics, and indentation validation are unchanged. | | 5.19.674 | Internal | An interpolation in the Stage 0 lexer takes its single-line or multiline kind from the string that encloses it instead of carrying a copy, so the two can no longer describe different string kinds. Lexing behavior is unchanged. | | 5.19.673 | Internal | Stage 0 string modes carry their multiline indentation data themselves rather than in a second stack kept alongside, so the two can no longer fall out of step. Tokens, diagnostics, indentation validation, and recovery are unchanged. | | 5.19.672 | Internal | Tagged string scanning in the self-hosted lexer passes the result of the string scan straight through instead of taking it apart and rebuilding it field by field. Tagged templates tokenize exactly as before. | | 5.19.671 | Internal | Ordinary code, string, and interpolation scanning in the self-hosted lexer return one result shape instead of two that carried the same payload under different names. Lexical output is unchanged. | | 5.19.670 | Internal | Line and block comments enter the self-hosted lexer through one shared branch, with the comment kind decided in a single place. Comment tokens, unterminated-comment diagnostics, and the `/` and `/=` tokens are unchanged. | | 5.19.669 | Internal | String scanning and ordinary token scanning in the self-hosted lexer share one result record, so the same payload no longer has two separate type definitions. Tokenization, tagged-template adjacency, and recovery are unchanged. | | 5.19.668 | Internal | Interpolation scanning in the self-hosted lexer returns only what its caller uses after it hands over the tokens and diagnostics it collected, so consumed payload no longer escapes. String tokenization and recovery are unchanged. | | 5.19.667 | Internal | Choosing how a brace is laid out and consuming the pending `concurrent` marker happen in one decision in the self-hosted layout, removing a repeated state check. Module import, record pattern, and concurrent body layout are unchanged. | | 5.19.666 | Internal | Brace lookahead in the self-hosted layout reads the next two significant tokens in a single pass rather than rescanning the leading newlines for the second one. Record field detection and brace modes are unchanged. | | 5.19.665 | Internal | Layout frames in the self-hosted compiler hold their mode and their opening delimiter as fixed choices rather than free strings, so a frame can no longer hold a value outside the known set. Layout output is unchanged. | | 5.19.664 | Internal | The self-hosted layout records the previous significant token as present or absent instead of using an empty string to mean none. Continuation decisions, separators, and layout output are unchanged. | | 5.19.663 | Internal | An active pattern region in the self-hosted layout keeps its depth and its kind together as one value, and the kind is limited to binding, `case`, and `for` regions. Pattern layout and pattern pipe continuation are unchanged. | | 5.19.662 | Internal | Layout frames in the self-hosted compiler live in one stack instead of three parallel arrays for mode, opening delimiter, and item presence, so the three can no longer disagree about the current frame. Layout output is unchanged. | | 5.19.661 | Internal | A pending implicit separator in the self-hosted layout is held as one optional token instead of a flag beside a span that could go stale. Implicit separator placement, explicit semicolons, and token output are unchanged. | | 5.19.660 | Internal | Malformed source with a repeated `concurrent` header no longer produces a stray statement separator in the self-hosted layout. Every stale marker at the current depth is cleared at a separator, while a normal `concurrent` body still consumes exactly one. | | 5.19.659 | Internal | An unused field in the self-hosted lexer's interpolation result is gone. Normal closure, end-of-file balancing, and line-break recovery were already described by the emitted tokens and the next position, so tokens, diagnostics, and spans are unchanged. | | 5.19.658 | Internal | A form feed character in source is rejected with `TPZ0001` by the self-hosted lexer instead of being quietly accepted as whitespace, matching the Stage 0 compiler. Space, tab, carriage return, and line feed handling is unchanged. | | 5.19.657 | Internal | Out-of-range lookahead in the self-hosted parser returns the stream's actual final token rather than a separately built end marker, so all fallback paths agree. The end token kind, its span, diagnostics, and recovery are unchanged. | | 5.19.656 | Internal | Two parser admission flags in the self-hosted compiler are read and written directly instead of through a lookup with a default and a pop-and-push, so an invalid empty slot can no longer be silently repaired. Parsing results are unchanged. | | 5.19.655 | Internal | The self-hosted parser reads its position and last-span cursors directly rather than through a helper that could invent a caller-supplied value when the slot was empty. The syntax tree, diagnostics, spans, and recovery are unchanged. | | 5.19.654 | Internal | Both compilers consume a pending `>` through the ordinary token cursor, so a closing angle bracket has one owner. Nested generics, comma-separated type arguments, the split of `>>`, and malformed extra-close handling are unchanged. | | 5.19.653 | Internal | The self-hosted parser ends a type argument list on the token cursor alone instead of also checking pending close-angle storage. Type argument parsing, the syntax tree, and diagnostics are unchanged. | | 5.19.652 | Internal | A type written as a string literal that contains an interpolation is rejected by the self-hosted parser with `TPZ2001`, the same code, message, and span the Stage 0 compiler already reported. Such a type used to be accepted. | | 5.19.651 | Internal | Using `None` as a parameter name is rejected with `TPZ2012` in both compilers, for ordinary functions, protocol methods, and both lambda forms. `None` is the `Option` constructor and was never an ordinary binding name. | | 5.19.650 | Internal | A `using` resource bound to the name `None` is rejected by the self-hosted parser with `TPZ2012`, matching the Stage 0 compiler. Resource value and body parsing and every other binding form are unchanged. | | 5.19.649 | Internal | Referring to a namespace member named with a keyword reports `TPZ3013` from the self-hosted compiler instead of the general not-exported error, so both compilers give the same diagnostic for the same source. | | 5.19.648 | Internal | A namespace member used in a type position records a type target in the self-hosted compiler, and the exception for a private record default applies only in value positions, so a private binding referenced as a type reports `TPZ3009` again. | | 5.19.647 | Internal | A module that hits a top-level name collision keeps its declared exports in the self-hosted compiler, so an importing module no longer gets a false report that it exports nothing. The collision itself is still reported. | | 5.19.646 | Internal | Import surface checks in the self-hosted compiler run even when a module has already reported a name collision, so an unrelated import error is no longer swallowed. Both compilers report the collision and then the import error. | | 5.19.645 | Internal | An `export let mut` declaration reports `TPZ3011` in the self-hosted compiler even where the same name already collides, so the diagnostic is no longer dropped and both compilers agree on the order they report. | | 5.19.644 | Internal | After two declarations of the same name in one scope, a following reference points at the later declaration in the self-hosted compiler rather than the first. Nested scope shadowing and every recorded declaration are unchanged. | | 5.19.643 | Internal | Both compilers look inside a nested `using` statement when checking an imported module's initializer block, so a reference to a name declared later, in the resource value or in the body, reports `TPZ3018` instead of passing unnoticed. | | 5.19.642 | Internal | Duplicate fields in a structural record type or a record literal are rejected with `TPZ5022` by the self-hosted checker instead of being accepted. A record written in another field order still assigns to the same expected type. | | 5.19.641 | Internal | Built-in functions in the self-hosted checker carry their type and their call signature in one catalog, instead of two places describing the same parameter names, defaults, and parameter count. Built-in call behavior is unchanged. | | 5.19.640 | Internal | Checking a module unit in the Stage 0 compiler runs through one path, whether or not the caller keeps the typed results. Dependency order, entry point validation, exports, aliases, and protocol conformances are unchanged. | | 5.19.639 | Internal | Structural records in the self-hosted checker take their canonical field order in one place, so a record written in reverse order gets the same binding type as before. Where an exact record type is expected, a known field keeps its literal type. | | 5.19.638 | Internal | Type argument substitution in the self-hosted checker reads its bindings directly rather than rebuilding a pair of parallel lists on every call, and array sorting works out its element type once for both the capability decision and the message it prints. | | 5.19.637 | Internal | Exported generic type aliases substitute their arguments into `enum`, record, and `newtype` types, so fields, variant patterns, and methods reached through a namespace-qualified alias resolve instead of failing. Local aliases behave as before. | | 5.19.636 | Internal | Explicit type arguments and an expected result keep their literal types in the self-hosted checker, while ordinary arguments still widen, so a contextual call returning a generic with a literal type argument is accepted instead of rejected. | | 5.19.635 | Internal | Type traversal in the self-hosted checker belongs to two places, one for questions asked about a type and one for rewriting its parts, instead of seven routines each walking the same children. Every decision keeps its own rule. | | 5.19.634 | Internal | The Stage 0 compiler walks the parts of a type from one shared inventory, replacing repeated child lists spread across unknown detection, variable collection, projection detection, and named-type argument validation. Every decision keeps its own rule. | | 5.19.633 | Internal | Five capability checks in the self-hosted checker share one projection of a named type's children, so record, `enum`, and `newtype` lookup and generic substitution are written once. Which types are comparable, ordered, keyable, or JSON-capable is unchanged. | | 5.19.632 | Internal | JSON encode and decode capability checks in the Stage 0 compiler share one traversal while keeping their two real differences, that one built-in type can be encoded but never decoded, and that a recursive named type is rejected for decoding, which needs a finite schema. | | 5.19.631 | Internal | A protocol call in the Stage 0 compiler infers its receiver once and reuses that type for the dispatch check, instead of walking the receiver expression a second time. Conformance admission, rejection, and diagnostics are unchanged. | | 5.19.630 | Internal | The encodability check on a `JSON.stringify` call in the Stage 0 compiler reuses the argument type it already resolved and only falls back to a second probe when that type is still unknown. Which values encode is unchanged. | | 5.19.629 | Internal | Function bodies and receiver method bodies in the Stage 0 compiler share one lifecycle for return context, loop isolation, and omitted-return collection. Free functions still publish inferred signatures and methods still update the method catalog. | | 5.19.628 | Internal | One lexical scope in the Stage 0 compiler keeps its binding types, mutable names, pending returns, alias links, and callable data in a single frame instead of five parallel lists. Shadowing, mutability, and recursion diagnostics are unchanged. | | 5.19.627 | Internal | Receiver methods, manually written protocol methods, and conformances are looked up through nested catalogs in the Stage 0 compiler, and the self-hosted checker answers conformance questions from an index rather than scanning its list. Output order is unchanged. | | 5.19.626 | Internal | Editor completion and signature help resolve the bindings in your source before built-in names, so an array named `Map` offers array members and a function you wrote named `print` shows your own signature rather than the built-in one. | | 5.19.625 | Internal | Editor completion offers the full set of built-ins the checker accepts, including types such as `ByteBuffer` and `template`, static namespaces such as `Math`, `JSON`, and `Test`, protocols such as `Show`, and the `RoundingMode` values. | | 5.19.624 | Internal | A record pattern that names its type registers its field bindings in the Stage 0 resolver, so valid source in an imported module's initializer no longer draws a false `TPZ3018`. A typed subpattern inside such a pattern also points at its declaration. | | 5.19.623 | Internal | The top-level initialization check in the Stage 0 compiler collects names bound by a record pattern that names its type, so a block-local binding correctly shadows a later top-level name instead of drawing a false `TPZ5002`. | | 5.19.622 | Internal | The self-hosted checker source passes through the sealed Stage 2 compiler with no diagnostics. Values that a `match` arm ending in `return` had widened stay concrete, array lengths are checked before indexing, and typed helpers are entered only once a value is resolved. | | 5.19.621 | Internal | Deeply recursive programs reach the language recursion limit and report `TPZ5009` at the call site instead of exhausting the host stack. One 64 MiB execution stack is shared by target products and the embedded compiler images. | | 5.19.620 | Internal | The self-hosted checker builds its top-level binding index once per module rather than rescanning every top-level statement for each reference. Initialization order rules and the exact `TPZ5002` diagnostics are unchanged. | | 5.19.619 | Internal | Both compilers require the exact number of type arguments on every named type, from `int` and `string` to `Array`, `Map`, `Option`, and your own records, enums, and newtypes. Writing `File` as a type names the resource `open` returns, and the unit type is spelled `()` only. | | 5.19.618 | Internal | Writing `template` as the type of a value or of a return works in both compilers instead of failing with a mismatch between two types of the same name. Declaring a record, enum, or newtype called `template` is rejected with `TPZ5022`. | | 5.19.617 | Internal | A function parameter default written as a string with an interpolation is rejected by the self-hosted checker, which used to accept it. Only a string made of literal text counts as a constant expression, matching the Stage 0 compiler. | | 5.19.616 | Internal | Interpolating a `ByteBuffer`, or a value that holds one inside a record, union, enum payload, or newtype, is rejected with `TPZ5001` by the self-hosted checker in both ordinary strings and tagged templates. It used to pass unnoticed. | | 5.19.615 | Internal | When a protocol call is rejected because the receiver does not conform, the self-hosted checker keeps checking the remaining arguments, so a wrong argument type is reported alongside the conformance error rather than hidden behind it. | | 5.19.614 | Internal | The argument count in `TPZ5004` reports how many arguments the call site actually supplies, including a piped value and any overflow. A one-parameter call given two arguments now says found 2 rather than found 1. | | 5.19.613 | Internal | Duplicate float and unit keys in a map literal are no longer rejected at check time with `TPZ5602` by the self-hosted checker. Static duplicate detection stays with integer, boolean, and plain string keys, and a duplicate key found while running is still a `TPZ4601` fault. | | 5.19.612 | Internal | A lambda given to a context that expects a different number of parameters reports `TPZ5004` from the self-hosted checker before the type mismatch, matching the Stage 0 compiler. Inference continues, so the error that follows is still reported. | | 5.19.611 | Internal | A `newtype` constructor pattern written through a renamed import now matches the type that import actually selected, so both compilers accept it even when another module declares a type with the same name. | | 5.19.610 | Internal | Record patterns written through a selected import now resolve to the imported record itself, and `match` coverage tells apart same-named records from different modules instead of treating them as one. | | 5.19.609 | Internal | Derived protocol conformance now follows an imported type through its alias or its namespace qualified name, so a valid imported type is no longer rejected and conformance cannot leak between same-named types from different modules. Displayed type names are unchanged. | | 5.19.608 | Internal | A protocol declared with an imported type in its signature now sees the real imported type, so a matching implementation is accepted instead of being rejected as a mismatch. | | 5.19.607 | Internal | Private types reachable through a public value or alias now carry the identity of the module that declares them, so passing one module's record where a same-named record from another module is expected is rejected with `TPZ5001`. Topaz 5.19 behavior is unchanged. | | 5.19.606 | Internal | Lispex applications now finish under the self-hosted route instead of stopping with an incomplete evaluation, because values returned by the Lispex host are matched against the same type identities the checker approved. | | 5.19.605 | Internal | Internal checks were corrected to match the shipped rule that every command with a compiler choice uses Rust when nothing is given and compiles with the self-hosted compiler only on an explicit `--compiler self`. Product behavior is unchanged. | | 5.19.604 | Internal | An absolute entry file under an explicit absolute source root now keeps the root you gave, so modules are found at their real paths instead of a doubled directory. `--exports-json` also lists every resolved module, including modules that export nothing. | | 5.19.603 | Internal | Passing the entry file itself as `--root` is now rejected with `TPZ3002` and a message saying the source root must be the directory that contains the entry. Both compilers previously accepted it and produced different, wrong module identities. | | 5.19.602 | Internal | Module loading no longer makes a full copy of every source file while resolving imports, which removes one allocation per module. Parsing, module identities, diagnostics, and every later stage are unchanged. | | 5.19.601 | Internal | When two modules import the same missing module, the self-hosted compiler reports `TPZ3001` once rather than once per importer, matching what the Rust compiler already reported. | | 5.19.600 | Internal | A statement that fails to parse no longer closes the import section, so a later `import` is not reported as a misplaced late import. Both compilers now report only the real syntax error. | | 5.19.599 | Internal | Missing brace errors from the self-hosted compiler now name the exact token `{` or `}` instead of describing it in prose, so both compilers give the same wording across records, enums, protocols, function blocks, patterns, and `match` expressions. | | 5.19.598 | Internal | After a `>>` closes a generic list, the leftover `>` stays visible to the self-hosted parser, so an extra angle bracket in a type alias, a generic function, or a protocol implementation points at the same place and expects the same token in both compilers. | | 5.19.597 | Internal | A range with no endpoint after `..` now gets one clear message asking for the endpoint, pointed at the `..` itself, in the self-hosted compiler too. Cases at end of file, inside parentheses or an array, with a step, and in a `match` guard no longer add a second error. | | 5.19.596 | Internal | A `map` comprehension that ends without its key and value separator now gets the same message from both compilers, the one that names the missing token and shows the expected body shape. | | 5.19.595 | Internal | A parenthesized expression just before the arrow of a `match` guard or a comprehension clause is no longer misread as a lambda, so those forms check and run in the self-hosted compiler as they already did in the Rust compiler. Explicitly grouped lambdas still work. | | 5.19.594 | Internal | A malformed item inside `match` or `concurrent` is now reported once and parsing continues with the following case or arm, instead of unwinding the whole expression and adding errors that were not there. | | 5.19.593 | Internal | An empty `match` is now reported by the self-hosted compiler with the same message asking for at least one `case`, over the same brace span. It previously passed with no diagnostic at all. | | 5.19.592 | Internal | Cases in `match` and arms in `concurrent` now require a separator between them in the self-hosted compiler, and a missing one is reported where the next case or arm begins. Valid separators, a closing brace, and end of file are still accepted. | | 5.19.591 | Internal | Two statements in a block with no separator between them now produce a single message at the second statement in both compilers, instead of that message followed by two more errors at the closing brace. | | 5.19.590 | Internal | A stray closing brace at the top level of a program now produces the same pair of messages in both compilers, because the self-hosted parser leaves the brace to the next item the way the Rust compiler does. | | 5.19.589 | Internal | Malformed exported `enum`, `record`, and `newtype` declarations no longer crash the compiler. They are reported with the usual separator message at the keyword, and valid exported declarations are unchanged. | | 5.19.588 | Internal | `export` no longer accepts an `impl` block as a whole declaration in the self-hosted compiler, so the error now points at `impl` while parsing. Exporting individual methods inside an `impl` still works. | | 5.19.587 | Internal | A bare `self` parameter counts as the receiver only when it comes first and is not variadic. Writing it after another parameter or with a spread is now rejected in the self-hosted compiler as it already was in the Rust compiler. | | 5.19.586 | Internal | Writing `mut` in the wrong place, before `let` or after the binding name, now gives the guidance about `let mut` in the self-hosted compiler instead of a vague expression or equals sign error. | | 5.19.585 | Internal | A non function item inside an `impl` or `protocol` body is now reported with the dedicated message saying that those bodies hold only methods or only signatures, matching the Rust compiler. | | 5.19.584 | Internal | The words `enum`, `record`, `newtype`, `impl`, and `protocol` are read as ordinary names unless the exact declaration form follows. The self-hosted compiler now applies the same rule, so these words stay usable as identifiers and a malformed use is reported where the Rust compiler reports it. | | 5.19.583 | Internal | Empty payload parentheses on an enum variant are now rejected in the self-hosted compiler, which asks for the first type. A variant with no payload still omits the parentheses, and recovery after the failed declaration reports the same sequence as the Rust compiler. | | 5.19.582 | Internal | An empty type parameter list such as `<>` on a type or function declaration is now rejected in the self-hosted compiler, which asks for a type parameter at the closing bracket. Declarations without type parameters are unaffected. | | 5.19.581 | Internal | Duplicate local names in a selected import are now reported in the same order as the Rust compiler, following the position of the earlier name in each pair rather than the later one. | | 5.19.580 | Internal | A malformed `timeout` clause in `concurrent` now reports one message and recovers through the matching brackets, so the cascading errors that used to follow it are gone. Valid durations parse as before. | | 5.19.579 | Internal | `topaz check --compiler self` returns real parser diagnostics again from the installed self-hosted compiler. A rejected program previously surfaced as an internal mismatch instead of the `TPZ2001` messages it actually produced. | | 5.19.578 | Internal | A record update now requires a comma or a closing brace after its leading spread, and any second or later spread is rejected with one precise message that recovers at the matching brace. Ordinary record literals and valid leading spreads are unchanged. | | 5.19.577 | Internal | Diagnostic notes now survive the whole self-hosted pipeline, so the hint about dropping explicit type arguments reaches the editor and `diagnostics.jsonl` instead of being lost right after the message itself. | | 5.19.576 | Internal | Parentheses group exactly one expression in Topaz, and a comma inside them now produces one message at the comma in the self-hosted compiler, with recovery through the matching parenthesis instead of a second error. | | 5.19.575 | Internal | An expression such as `f()` is read as a comparison again in the self-hosted compiler rather than a generic call, so it reaches the checker and reports the ordering error `TPZ5007` exactly as the Rust compiler does. | | 5.19.574 | Internal | Explicit type arguments are recognized only after an identifier, a member, or an optional access, so `(identity)(1)` is no longer treated as a generic call in the self-hosted compiler and gives the same `TPZ5002` as the Rust compiler. | | 5.19.573 | Internal | A call to `JSON.parseAs` or `JSON.decode` with the wrong number of explicit type arguments now reports only the count message `TPZ5510`, without a follow-on type mismatch. Correct calls are unchanged. | | 5.19.572 | Internal | Explicit type arguments are used only when their count exactly matches the callee, so a call with the wrong number reports the count message alone and ordinary inference continues without extra mismatches. | | 5.19.571 | Internal | A type written on a binding inside a generic function body now binds to that function's own type parameter, and explicit type arguments are accepted on the standard generic constructors and receiver methods such as `Some`, `Ok`, `map`, and `reduce`. Optional access calls still reject them. | | 5.19.570 | Internal | Typed patterns now execute on the target backend with the meaning the checker gives them, covering primitives, literals, unions, structural records, `Option`, `Result`, `Array`, `Set`, `Map`, ranges, callable arity, and generic record, enum, and `newtype` types. | | 5.19.569 | Internal | The project's own build automation moved to the current major versions of its public GitHub actions and to an explicit Node 24 runtime. No product code, published release, or artifact changed. | | 5.19.568 | Internal | The target backend now matches list patterns with a rest segment, structural and named record patterns, and `newtype` constructors, and it requires the exact record and enum type. Names bound by a named record pattern also count as initialized. | | 5.19.567 | Internal | Loop control now works on the target backend. A `for` used as a statement accepts `break` and `continue`, `loop` returns the value carried by `break`, labeled control crosses a value producing `for`, and each iteration of `while`, `for`, and `loop` gets a fresh scope. | | 5.19.566 | Internal | `using File` now runs on the target backend. The handle closes automatically when the block ends normally or through a return, a `?` propagation, a `break`, or a `continue`, and reached `defer` blocks drain in reverse order before that close. | | 5.19.565 | Internal | `concurrent` now schedules its arms on the target backend. Arms take turns rather than running one to completion, an expired `timeout` clause abandons pending arms and evaluates `else`, and a fault inside an arm surfaces even past an earlier endless arm. | | 5.19.564 | Internal | The target backend now evaluates `null`, integer ranges, set literals, and array, set, and map comprehensions, expands an array spread in source order, and returns the body values of a value producing `for` in iteration order. | | 5.19.563 | Internal | Functions and lambdas now travel as values on the target backend with the scope they captured. Record fields named like builtin methods are called before those methods, higher order callbacks on `Array`, `Option`, `Result`, and `Map` run, and a `print` passed as a value still reaches the host. | | 5.19.562 | Internal | Calls now evaluate in written order on the target backend. The callee or receiver is evaluated once before its arguments, a pipeline evaluates its lead first, an optional call on `None` or `null` returns before any argument runs, and a spread expands at its own position. | | 5.19.561 | Internal | User calls on the target backend now bind arguments the way they are written. Named arguments land in their declared slots, omitted parameters take their defaults, and an explicit spread with the positional values after it becomes the variadic array. | | 5.19.560 | Internal | Methods written in an `impl` block for your own record, enum, or `newtype` are now checked by the self-hosted compiler and dispatched on the target backend. A manual protocol implementation takes precedence over derived `Show`, `Eq`, and `Order`. | | 5.19.559 | Internal | The self-hosted compiler now checks manual `impl Protocol` declarations, registering only a complete and exact implementation and keeping the existing diagnostics for a wrong target, a duplicate, a missing method, or a mismatched signature. | | 5.19.558 | Internal | Static protocol calls such as `P.m(...)` are now checked by the self-hosted compiler. A conforming receiver gets concrete parameter and result types, a nonconforming one reports `TPZ5522`, and named or spread arguments are still rejected. | | 5.19.557 | Internal | User protocol declarations are now checked by the self-hosted compiler, which rejects a builtin name, a colliding or duplicate name, a generic method, a missing or wrong receiver, a missing return type, a variadic, a default value, and a nested declaration. | | 5.19.556 | Internal | A valid protocol declaration no longer marks a self-hosted build as containing unsupported syntax. Protocol signatures are compile time information, so they add no runtime operations, while genuinely unsupported syntax outside a protocol is still reported. | | 5.19.555 | Internal | A default value on a protocol method parameter now reports the protocol rule with `TPZ5022` in the self-hosted compiler, instead of an unrelated unbound name error raised from inside the default expression. | | 5.19.554 | Internal | `FS.list` no longer turns a directory entry whose name is not valid Unicode into a replacement bearing string that could collide with another entry. Such a directory returns an error through the ordinary `Result` channel instead. | | 5.19.553 | Internal | `FS.readText` and `FS.open` now find files under a package root whose real directory name is not valid Unicode and that is reached through a Unicode symbolic link. Capability permissions, containment, and symbolic link escape rejection are unchanged. | | 5.19.552 | Internal | `topaz build --target python` no longer deletes a user file whose name is not valid Unicode while cleaning up caches left by older versions. Only entries with an exact Unicode generated cache name are removed. | | 5.19.551 | Internal | The development server now watches inputs whose modification time is earlier than 1970 and files under a directory whose name is not valid Unicode, so an atomic replacement that keeps the same timestamp still triggers a rebuild. | | 5.19.550 | Internal | The development web server no longer returns 404 when the package is reached through a Unicode symbolic link to a directory whose real name is not valid Unicode. The build and the server now use the same output directory. | | 5.19.549 | Internal | A build storage directory given through `TOPAZ_STORAGE_DIR` whose name is not valid Unicode is now refused up front with a message saying the path cannot be represented as Unicode, instead of quietly splitting the build across a replacement path. | | 5.19.548 | Internal | The bundles read by `topaz compiler validate` now require every member path to be exact Unicode, so two different files can no longer collapse to one name during intake or verification. An unrepresentable path is reported instead. | | 5.19.547 | Internal | A package containing a source file whose name is not valid Unicode is now refused before any hashing or vendoring happens, so `topaz vendor` can no longer overwrite an already vendored package and then fail. Ordinary package sources are unchanged. | | 5.19.546 | Internal | Assets and stylesheets declared under `web.styles` and `web.assets` now require exact Unicode paths, so one declared asset can no longer be silently dropped or emitted under a path that does not identify its source. | | 5.19.545 | Internal | `topaz test` now resolves a selected test file that is a Unicode symbolic link into a directory whose real name is not valid Unicode, and it labels the result with the path that was actually asked for. Paths outside the package or without a `.tpz` name are still rejected. | | 5.19.544 | Internal | Unsaved editor buffers now stay attached to the module they belong to when the workspace is reached through symbolic links into a directory whose real name is not valid Unicode. Imported modules read the open document again instead of the file on disk. | | 5.19.543 | Internal | On Linux the compiler now tells apart modules that live in different directories whose names contain bytes that are not valid UTF-8, even when they are reached through symbolic links. Two such modules are no longer mistaken for one file and rejected as a collision. | | 5.19.542 | Internal | A module import on Linux keeps resolving when its directory also holds an unrelated file whose name is not valid UTF-8. Directory listings now skip only the entries that cannot be written as Unicode text, instead of mangling them or giving up on the whole directory. | | 5.19.541 | Internal | Importing the same file twice through links or aliases is now reported as a module collision with `TPZ3004` rather than loaded as two separate modules. Imports of genuinely distinct files, root containment, and every other diagnostic are unaffected. | | 5.19.540 | Internal | Name checking no longer walks a type tree an extra time before recording its references. Diagnostics, scope facts, and compiler output are identical, and resolution simply does less redundant work. | | 5.19.539 | Internal | An unused file-existence obligation and its ten implementations were dropped from the compiler's file access layer. File reads, directory listings, package lookups, editor overlays, and diagnostics behave exactly as before. | | 5.19.538 | Internal | The 22 standard modules are now described in a single catalog, so module resolution and editor completion cannot disagree about which modules exist. Completion labels, module paths, and module sources are unchanged. | | 5.19.537 | Internal | An entry file whose name ends in a repeated source extension, such as `a.tpz.tpz`, now yields the same module name in both compilers. Only the final extension is removed, so the import chain shown in diagnostics matches the file you wrote. | | 5.19.536 | Internal | Initializer checking reads names straight from the source text and makes an owned copy only when a name must be kept or a violation is actually reported. The `TPZ3018` wording, spans, and all other results are unchanged. | | 5.19.535 | Internal | Top-level `let` and `const` declarations record their scope facts through the same explicit path every other declaration uses, instead of leaning on a silent skip. Shadowing, scope numbering, and diagnostics are unchanged. | | 5.19.534 | Internal | Three places in module resolution that quietly substituted a fallback for a lookup that cannot fail now index their tables directly. An internal inconsistency surfaces instead of becoming a wrong dependency order or a dropped export, and ordinary results are unchanged. | | 5.19.533 | Internal | Import graph discovery, dependency order, and cycle reporting in the self-hosted compiler read their arrays directly instead of substituting a value that could never be needed. Graph shape, cycle messages, and ordering are unchanged. | | 5.19.532 | Internal | Two unused fields were removed from import parsing, and diagnostics for a selected import read their exact spans directly rather than keeping an unreachable fallback to the whole import path. Alias binding, suggestions, and messages are unchanged. | | 5.19.531 | Internal | Local scope handling in the self-hosted compiler reads and updates the active scope directly instead of quietly ignoring a miss that cannot happen. Lexical lookup, shadowing, scope numbering, and diagnostics are unchanged. | | 5.19.530 | Internal | Module table lookups in the self-hosted compiler index the catalog directly, and an internal error about a missing module table that could never fire is gone. Import resolution, namespaces, and diagnostics are unchanged. | | 5.19.529 | Internal | Twelve lookups in the self-hosted compiler's module tables index their bindings directly instead of falling back to a wrong answer such as an unresolved reference or a missing diagnostic. Import resolution, namespaces, and diagnostics are unchanged. | | 5.19.528 | Internal | Two sorting loops in the self-hosted compiler read their arrays directly, so an internal bounds problem shows up as an error instead of quietly producing a differently ordered result. Query order, export order, and suggestions are unchanged. | | 5.19.527 | Internal | Diagnostic sorting, child selection, and member resolution in profile checking read their arrays directly, so a corrupted internal index raises an error rather than yielding a missing child or an unresolved name. Profile policies and diagnostics are unchanged. | | 5.19.526 | Internal | Unicode normalization and case folding in the self-hosted compiler assemble their output once instead of rebuilding the whole string for every character, so long names are processed far faster. The resulting text and collision reports are unchanged. | | 5.19.525 | Internal | Typo suggestions in the self-hosted compiler reject an over-long name by its byte length before splitting it into characters, so a huge identifier is no longer expanded in full only to be discarded. The suggestions and their wording are unchanged. | | 5.19.524 | Internal | The edit-distance ranker behind typo suggestions in the self-hosted compiler indexes its arrays directly instead of substituting an empty character or an invented distance for an out-of-range read. Suggestions, tie-breaking, and wording are unchanged. | | 5.19.523 | Internal | Typo suggestions in the Rust compiler collect at most 65 characters from a name and build the compared target once per query instead of rebuilding it for every candidate. An identifier several kilobytes long is no longer scanned in full. | | 5.19.522 | Internal | Typo suggestions in the self-hosted compiler reuse the character arrays they already built rather than creating them again for the distance calculation. Each candidate is expanded once, and the suggestions themselves are unchanged. | | 5.19.521 | Internal | Each candidate's distance is now computed once when the self-hosted compiler ranks typo suggestions, rather than being recomputed whenever a better spelling is found. Ranking, tie-breaking, and the suggestion wording are unchanged. | | 5.19.520 | Internal | The typo ranker in the self-hosted compiler reuses three row buffers for a whole comparison instead of allocating a new row for every character. Memory use stays proportional to the candidate name, and all suggestions are unchanged. | | 5.19.519 | Internal | The Rust compiler keeps three rows instead of building the whole distance matrix for every typo candidate. Memory use falls from the product of both name lengths to the candidate length alone, and every suggestion is unchanged. | | 5.19.518 | Internal | When two names are equally close to a misspelling, both compilers now suggest the alphabetically smaller one. The same typo gets the same suggestion on every run instead of shifting with internal container order. | | 5.19.517 | Internal | A typo in a direct call now draws suggestions only from names you can actually call. The self-hosted compiler no longer offers a local value that is not callable, or a builtin function hidden by a visible local of the same name. | | 5.19.516 | Internal | Suggestions for an unknown name in the self-hosted compiler search only the declarations of the module that used it, rather than every declaration in the program. Candidate order, ranking, and `TPZ5002` messages are unchanged. | | 5.19.515 | Internal | The self-hosted checker finds a call by its exact position instead of scanning the whole recorded call list several times per expression. Call order, `Map` constructor handling, result types, and diagnostics are unchanged. | | 5.19.514 | Internal | A stray loop exit added in the previous version made the Rust compiler reject the self-hosted checker source. Removing it restores the build, and the Lispex rule diagnostics keep their code, wording, and placement. | | 5.19.513 | Internal | Checking for indirect Lispex rule handles no longer rescans every call in the program for each rule reference. The `TPZ5500` code, wording, and location are unchanged, and large programs that use rules check faster. | | 5.19.512 | Internal | Closure capture analysis in the self-hosted compiler derives each declaration scope's ancestors once instead of re-walking the scope chain for every reference. Captured names, their order, and everything downstream are unchanged. | | 5.19.511 | Internal | Collecting closure captures in the Rust compiler derives each declaration scope's ancestors once rather than re-walking the scope chain for every reference. Captured names, spans, and Stage 1 output are unchanged. | | 5.19.510 | Internal | Capture emission in the Rust compiler groups captures by module in one pass instead of rescanning the whole capture list for each module. Capture order, spans, and Stage 1 output are unchanged. | | 5.19.509 | Internal | The Rust compiler no longer copies a call's target name just to test it against the three runtime prefixes, keeping a copy only for calls that really carry a runtime entry. Call plans and Stage 1 output are unchanged. | | 5.19.508 | Internal | Both paths that build Stage 1 in the Rust compiler now share one operation sort that indexes module order once, rather than rescanning the module list on both sides of every comparison. Operation order and Stage 1 output are unchanged. | | 5.19.507 | Internal | Comparing captures in the self-hosted compiler reads a module identity through the existing direct lookup instead of scanning the module list on both sides. Identity spelling, capture order, and Stage 1 output are unchanged. | | 5.19.506 | Internal | Sorting captures in the Rust compiler no longer copies each module and name into a temporary sort key and compares the existing values in the same order instead. Capture order, deduplication, and Stage 1 output are unchanged. | | 5.19.505 | Internal | Namespace member captures in the self-hosted compiler find their alias through an index built once, instead of scanning every resolved declaration for each reference. Capture types, order, and Stage 1 output are unchanged. | | 5.19.504 | Internal | The Rust compiler looks up a namespace alias for a member capture through an index built during the declaration pass, rather than scanning every resolved declaration. Alias behavior, capture order, and Stage 1 output are unchanged. | | 5.19.503 | Internal | Capture targets in the self-hosted compiler reuse the existing position index of typed nodes instead of scanning every node twice per reference. The chosen target, capture order, and Stage 1 output are unchanged. | | 5.19.502 | Internal | A capture's module is resolved through a single identity index in the Rust compiler, and the name is copied only into the final capture rather than for every enclosing closure. Capture content and Stage 1 output are unchanged. | | 5.19.501 | Internal | Capture target resolution in the Rust compiler uses position indexes built in one pass over typed nodes, rather than scanning them again for each reference. Lookup order, capture chains, and Stage 1 output are unchanged. | | 5.19.500 | Internal | Call enrichment in the Rust compiler groups references by file once and searches only the range that can contain the callee, instead of filtering the whole reference list for every call. Target identity and Stage 1 output are unchanged. | | 5.19.499 | Internal | Looking up a name reference in the Rust compiler is an exact position lookup instead of a linear scan of every reference for each identifier, member, and binding. Resolved targets and Stage 1 output are unchanged. | | 5.19.498 | Internal | Semantic types are no longer copied twice on the way to Stage 1 in the Rust compiler, which now uses the checker's own values and makes one copy at the end. Type spelling, operations, and Stage 1 output are unchanged. | | 5.19.497 | Internal | Building Stage 1 in the Rust compiler no longer copies every call into a second table, and one lookup per expression serves both the call plan and the runtime override. Calls, plans, and Stage 1 output are unchanged. | | 5.19.496 | Internal | Each function and lambda operation records its position as it is created, and a closure's parent is read from there. The copied identifier table and the fallback scan over all operations are gone, while parentage and Stage 1 output are unchanged. | | 5.19.495 | Internal | Closure parents in the self-hosted compiler are keyed by their two position numbers instead of a string formatted at every lookup. Parent resolution, capture insertion, and Stage 1 output are unchanged. | | 5.19.494 | Internal | Deferred `let` pattern operands in the self-hosted compiler carry only their positions and resolve their labels when they are flushed, instead of copying identifiers and labels up front. Operand order and Stage 1 output are unchanged. | | 5.19.493 | Internal | Ordinary operands are linked as each operation is admitted in the self-hosted compiler, and only `let` patterns are queued, which removes a full second pass over every operation. Operand order, labels, and Stage 1 output are unchanged. | | 5.19.492 | Internal | Operand labels in the self-hosted compiler live in one place and are reached through a small position index instead of being duplicated for every operation. An unread internal label is gone, and labels and Stage 1 output are unchanged. | | 5.19.491 | Internal | The self-hosted compiler checks for a comprehension or `for` parent before fetching an operand label, so children of those forms no longer copy labels that their own rebuild replaces. Operand order and Stage 1 output are unchanged. | | 5.19.490 | Internal | A node's semantic type and its runtime representation are derived together in one step in the self-hosted compiler, rather than recomputing the representation while assembling each operation. Every type mapping and Stage 1 output are unchanged. | | 5.19.489 | Internal | An operation's runtime entry now comes from the same table that already decides its kind and identifier in the self-hosted compiler, removing a second large dispatch over syntax kinds. Every runtime entry and Stage 1 output are unchanged. | | 5.19.488 | Internal | Control flow classification in the self-hosted compiler decides the control kind and whether an operation owns its target in one place. Branch, `match`, loop, `break`, `continue`, `return`, cleanup, propagation, and `concurrent` behavior are unchanged. | | 5.19.487 | Internal | Operation roles in the self-hosted compiler come from the same table as the operation kind and identifier, leaving only the protocol exclusion and the impl method requirement as separate rules. Roles, kinds, and Stage 1 output are unchanged. | | 5.19.486 | Internal | Two near-identical tables that mapped a syntax node to its operation kind and to its identifier suffix became one. Every operation kind, identifier, and Stage 1 output stays the same while the self-hosted compiler carries far less duplicated code. | | 5.19.485 | Internal | Operands for `for` and comprehension forms are built once by their own pass in the self-hosted compiler, instead of being filled in and then cleared and rebuilt. Operand order, pairing, and Stage 1 output are unchanged. | | 5.19.484 | Internal | Rebuilding comprehension operands no longer scans the whole module syntax tree for each comprehension, using child positions recorded during the first pass instead. Clause order, deduplication, and Stage 1 output are unchanged. | | 5.19.483 | Internal | Each operand label is built once from its parent's cached prefix plus its own segment, instead of walking the ancestor chain again for every operation. Label spelling, operand order, and Stage 1 output are unchanged. | | 5.19.482 | Internal | Name, mutability, storage, and declaration identity for a binding are produced together in the self-hosted compiler instead of being rediscovered while the operation is assembled. Patterns, parameters, `const` and `using` bindings are unchanged. | | 5.19.481 | Internal | The self-hosted compiler marks whether a syntax node sits inside a `protocol` when the node is admitted, so each child reads only its parent's flag instead of walking the ancestor chain. Protocol handling and Stage 1 output are unchanged. | | 5.19.480 | Internal | A binding's mutability is read from its own `let` parent rather than by walking upward from every named binding, and the two compilers are now compared on that field as well. Mutable `let`, parameters, and match bindings behave as before. | | 5.19.479 | Internal | Operands are appended in a single pass in the self-hosted compiler, with only each `let` pattern deferred, instead of traversing every operation twice. The value before pattern order for `let` and all other operand order are unchanged. | | 5.19.478 | Internal | Node admission records which comprehension and `for` nodes need a special operand rebuild, so the later pass no longer rescans the whole module syntax tree. Operand order and Stage 1 output are unchanged. | | 5.19.477 | Internal | Each typed node's kind and type are projected once into a single position keyed catalog in the self-hosted compiler, dropping the raw node array and two separate lookups. Type spelling, representation, and Stage 1 output are unchanged. | | 5.19.476 | Internal | Every reference is resolved to its final identity once and kept in a single position keyed catalog, instead of re-applying the same precedence rules at each lookup. The builtin fallback and Stage 1 output are unchanged. | | 5.19.475 | Internal | While decoding, the self-hosted compiler now prepares each typed call into a single plan instead of re-reading the same raw call for every node. The shape used when a call is missing, argument and evaluation order, and the lowered Stage 1 output are unchanged. | | 5.19.474 | Internal | Module captures are now distributed once into a list per module rather than picked out of one flat array while each module is lowered. Module order, capture order, the omission of captures outside the module range, and the lowered Stage 1 output are unchanged. | | 5.19.473 | Internal | Lambdas and method declarations now link their closures at the moment the node is admitted, so the extra pass over the whole syntax tree afterwards is gone. Closure keys, capture order and identity, the missing parent fault, and the lowered Stage 1 output are unchanged. | | 5.19.472 | Internal | Empty index and cleanup collections are now built directly where they are held instead of through two private helpers. Index keys and insertion order, first direct child selection, `defer` cleanup handling, and the lowered Stage 1 output are unchanged. | | 5.19.471 | Internal | Comprehension clauses now read their iterator, pattern, and condition from the child index that lowering already builds, rather than rescanning the whole syntax tree for each clause. Clause source order, `if` condition and body behavior, and the lowered Stage 1 output are unchanged. | | 5.19.470 | Internal | Top level function binding names and declaration identity now come from the existing child index instead of a full scan of the syntax tree at every lookup. Binding names, declaration source ranges, reference identity precedence, and the lowered Stage 1 output are unchanged. | | 5.19.469 | Internal | Identifier, import, and member spelling lookups now slice one UTF-8 buffer held per module instead of re-encoding the whole module source for every lookup. Spellings, the empty string used for an invalid range, profile rules, and profile results are unchanged. | | 5.19.468 | Internal | Two profile records that no other module imported are now private, and the three empty index maps are built directly instead of through a helper that inserted and removed a dummy entry. Profile rules, messages, and results are unchanged. | | 5.19.467 | Internal | Profile findings are now admitted in one pass that checks for a duplicate on the way to the insertion point, rather than scanning the output twice. Duplicate identity, canonical finding order, merge order, and profile results are unchanged. | | 5.19.466 | Internal | Each import now recovers its module path from the child index the profile already builds, instead of rescanning the syntax tree by kind and source range. Denied module and test framework import identity, diagnostics, and profile results are unchanged. | | 5.19.465 | Internal | Module dependency ordering now sorts module identities once and selects each module by index, replacing two hand written insertion sorts and repeated array shifting. Ready module selection, dependency order, cycle diagnostics, and name resolution output are unchanged. | | 5.19.464 | Internal | Each local scope now keeps a name map for the value and type namespaces, so an identifier is found directly instead of scanning every binding from the innermost scope outward. Shadowing, the first binding winner, diagnostics, and name resolution output are unchanged. | | 5.19.463 | Internal | Six working records in the name resolution module that no other module imported are now private, and it exports a single entry point. Admission and import surfaces, diagnostics, name facts, module initialization, and resolution output are unchanged. | | 5.19.462 | Internal | Type checks in an exported signature, imported initializer references, and the zero export check now read the binding index instead of rescanning every binding. A colliding binding and `export let mut` still do not count as exports, and diagnostics and runtime behavior are unchanged. | | 5.19.461 | Internal | Binding collisions are now found through the name index that name resolution already builds, which also supplies the first binding range for `TPZ3008`. Admission order, collision messages, ranges and order, and runtime behavior are unchanged. | | 5.19.460 | Internal | Admitted bindings now enter a general name index and, when exported, an exported name index, so module, local, selected, and namespace references no longer scan the ordered binding list. Selection rules, suggestions, diagnostics, and runtime behavior are unchanged. | | 5.19.459 | Internal | Module tables now carry an identity index beside the ordered list, so namespace calculation, selected and namespace imports, and import surface validation pick a table directly. Table order, module indexes, binding order, diagnostics, and runtime behavior are unchanged. | | 5.19.458 | Internal | Module discovery now tests membership in a set of loaded module identities, and the accepted and rejected results share one pass over public import edges instead of repeating linear scans per edge. Edge exclusion, duplicate edges, discovery order, and cycle diagnostics are unchanged. | | 5.19.457 | Internal | Marking every module of a cycle now uses one forward and one reverse reachable set per anchor instead of a fresh search for each candidate. Canonical cycle paths, the primary range, self import handling, diagnostic text and order, and dependency order are unchanged. | | 5.19.456 | Internal | Cycle diagnostics and dependency ordering now share one module graph of sorted identities and dependency sets, rather than rebuilding the same graph and copying path arrays at every branch. Canonical cycle paths, ranges, dependency order, and the cyclic fallback order are unchanged. | | 5.19.455 | Internal | Dependency ordering now judges each candidate through resolved and done sets and a map from module to its dependencies, instead of scanning the full edge list in every round. Duplicate edges stay irrelevant, unresolved targets stay excluded, and ordering and diagnostics are unchanged. | | 5.19.454 | Internal | Stage 0 name resolution now borrows module and edge identities through cycle search and ordering instead of copying them into several owned structures, and it finds each module by rank while sorting. Dependency order, cycle diagnostics and ranges, and runtime behavior are unchanged. | | 5.19.453 | Internal | The import chain shown in a module initialization error is now drawn by one shared component for Stage 0 and for generated Rust, and the Python module corpus gained an initialization case. Fault code, message, range, output, and files agree across the interpreter and both generated backends. | | 5.19.452 | Internal | Local scopes now borrow declaration names straight from the module source instead of copying each one, and only the lasting declaration fact keeps its own copy. Scope order, shadowing, declaration facts, diagnostics, reference targets, and runtime behavior are unchanged. | | 5.19.451 | Internal | The temporary module table now borrows module identities from the resolution output instead of copying each one. File identity, bindings, exports, diagnostics, reference targets, and runtime behavior are unchanged. | | 5.19.450 | Internal | The module table now holds the target file beside its bindings and exports, so name resolution no longer builds a second index just to recover the file for namespace and qualified type references. Diagnostics, reference targets, checking, and runtime behavior are unchanged. | | 5.19.449 | Internal | `TPZ3018` now fires in the self-hosted compiler when an imported module reads a later binding in an immediately evaluated initializer, as in Stage 0. The entry module and top level consts stay exempt, and delayed positions such as function, lambda and `concurrent` bodies are not reported. | | 5.19.448 | Internal | A module private type used in an exported public surface is now refused by the self-hosted compiler with `TPZ3014`, covering exported parameters and returns, nested type alias bodies, enum payloads, record fields, and `newtype` bases. An exported type stays valid in all of those shapes. | | 5.19.447 | Internal | Import diagnostics from the self-hosted compiler now match Stage 0 word for word. A module that exports nothing carries the same wording, and a missing selected import, namespace value, or qualified type again offers the closest exported name as a suggestion. | | 5.19.446 | Internal | A record field default may again read an immutable namespace private `let` from another module, which the self-hosted compiler used to refuse with `TPZ3009`. Outside a field default, and for a mutable binding inside one, the same reference is still refused. | | 5.19.445 | Internal | A `protocol` declaration no longer enters the binding or export table of the self-hosted compiler, so a module local `protocol` followed by a `let` of the same name is accepted. `export protocol` is refused with `TPZ2001` and the imported free statement `TPZ3007`, as Stage 0 already did. | | 5.19.444 | Internal | A name repeated inside one alternative of an or pattern is now reported as a collision by the self-hosted compiler. `let [x, x] \| [x, x] = [1, 2]` raises `TPZ3008` at each repeat, pointing back to the first binding, exactly as Stage 0 does. | | 5.19.443 | Internal | When a module both imports itself and takes part in a longer cycle, the self-hosted compiler now reports the multi module cycle rather than the self import. A module that only imports itself still reports exactly that. | | 5.19.442 | Internal | The import cycle diagnostic now points at the first edge of the cycle it actually reports, instead of an arbitrary edge of the same group. A module that imports itself still points at its own import. | | 5.19.441 | Internal | The self-hosted compiler now reports `TPZ5030` for a sibling module the manifest never declares as `extern`, not a missing file error. Invalid replay columns are refused with `TPZ5032`, the reserved `topaz.*` message gains its version boundary, and one cycle group yields one diagnostic. | | 5.19.440 | Internal | Module file name collisions are now compared with full Unicode case folding and canonical decomposition in the self-hosted compiler, so `straße.tpz` beside `strasse.tpz`, and a composed name beside its decomposed form, are reported as `TPZ3004` with both candidate names. | | 5.19.439 | Internal | A private top level `protocol` in an imported module is now accepted by both compilers instead of being reported as a free statement. An entry outside the project root stops at once, and an import is checked for containment and then segment by segment, reading source only after every segment passes. | | 5.19.438 | Internal | Path checks in the self-hosted compiler now compare whole path segments, so a file such as `main..tpz` is accepted while `root/../main.tpz` is refused with `TPZ3002` before any file is read. | | 5.19.437 | Internal | Both parsers now reserve a construct owned brace only at the outer expression level, so `if ready {}` keeps its empty body, a record update inside parentheses parses again, and a `match` case arrow survives before a lambda body. `make() {}` is refused with `TPZ2001`. | | 5.19.436 | Internal | The `agent-pack` profile now judges a free `assert` by resolved identity, so a user defined `assert` is accepted while the builtin one is still refused. A run with `--compiler self --profile agent-pack` reaches source checking again, and the embedded Lispex run record is checked at its current version. | | 5.19.435 | Internal | Mutators, Array and Map callbacks, `Option`, `Result`, and File methods now become first class receiver values in generated Rust as well, catching up with the checker and the interpreter. One reserved member name list also closes collisions such as `sortBy`, `update`, `set`, and `named`. | | 5.19.434 | Internal | Named `ByteBuffer.set`, `fill`, and `copy` pipes and first class `okOr` values in plain, optional, and pipe field form are now accepted by generated Rust, as the interpreter already ran them. Callback driven `okOrElse` stays call only, and evaluation order and faults are unchanged. | | 5.19.433 | Internal | Builtin receiver calls in direct, optional, and piped form now take named arguments in generated Rust, where `ByteBuffer.set`, `fill`, and `copy` are recognized too. Member lookup, dispatch, and code generation read one shared receiver catalog, and call shapes and faults are unchanged. | | 5.19.432 | Internal | A named `s` argument for `file.write` now works in generated Rust in direct, piped, optional, and optional pipe form, as the checker and the runtime already allowed. All three public signature pages show `file.write(s: string)`. | | 5.19.431 | Internal | The four generated Rust paths for resource `read`, `write`, and `close` now emit their builtin branch from one shared piece instead of four copies. Evaluation order, host effects and faults, short circuiting, source ranges, and the generated bytes are unchanged. | | 5.19.430 | Internal | Direct `sortBy` and `retain` now use the shared single callback argument reader, and all four callback paths in generated Rust share one record field shadow projection. Argument admission, the mutable root fault order, callback effects, ranges, and the generated bytes are unchanged. | | 5.19.429 | Internal | Direct `map`, `filter`, `sortedBy`, `mapValues`, and `flatMap` in generated Rust now share the callback argument reader and callback body writer that the optional paths already used. Collection behavior, callback laziness and faults, ranges, and the generated bytes are unchanged. | | 5.19.428 | Internal | Read only receiver calls now prepare their positional and named arguments through the same shared renderer as other calls in generated Rust, dropping a local rendering loop. Spread dispatch, order refusal, labels, the `ByteBuffer.get` fast path, ranges, and the generated bytes are unchanged. | | 5.19.427 | Internal | Direct and optional `okOrElse` in generated Rust now build their arguments once and share that result between the record field shadow call and the builtin branch. Short circuiting, pipe lead and argument order, placeholder behavior, callback laziness, ranges, and the generated bytes are unchanged. | | 5.19.426 | Internal | `Codec.zstdCompress` now goes through the common fixed parameter binder of generated Rust, which learned optional defaults, so each argument is prepared once instead of being copied up to three times. Positional, named, and default behavior, refusal messages, and the generated bytes are unchanged. | | 5.19.425 | Internal | Value calls, user receiver method calls, record field shadow calls, and the builtin fallback in generated Rust now prepare their arguments in a single source order pass instead of up to three. Order fault timing, spread extension, labels, precedence, ranges, and the generated bytes are unchanged. | | 5.19.424 | Internal | Receiver calls with a spread argument now render their arguments once in generated Rust and share that result between the value call and the arity fault branch, instead of walking the same list twice. Evaluation order, spread extension, faults, ranges, and the generated bytes are unchanged. | | 5.19.423 | Internal | The spread argument fault path for namespace and receiver calls is now written in one place in generated Rust, with each side supplying only its own temporary name and message. Order handling, evaluation order, spread ranges, the arity fault, and the generated bytes are unchanged. | | 5.19.422 | Internal | Piped receiver calls with a spread argument now prepare their argument list once in generated Rust and share it between the record field shadow branch and the arity fault branch. Placeholder handling, evaluation order, spread ranges, method gates, and the generated bytes are unchanged. | | 5.19.421 | Internal | The positional fast path of the optional `Map.update` call now uses the same placeholder test as the binding path in generated Rust, removing a second local scan. Routing, call shapes, the update and callback write back, faults, ranges, and the generated bytes are unchanged. | | 5.19.420 | Internal | Placeholder detection and the routing test for piped calls are now defined once in generated Rust and used by the binder and by the optional `reduce` and `Map.update` paths. Binding order, fast paths, labels, faults, ranges, and the generated bytes are unchanged. | | 5.19.419 | Internal | Static pipe arguments are now held once in generated Rust and referenced by index from both the parameter slots and the record field shadow projection, removing one copy of every written or inserted value. The generated spelling and bytes, binding, labels, and faults are unchanged. | | 5.19.418 | Internal | The optional pipe paths for `reduce` and `Map.update` now read the prepared argument text in place in generated Rust instead of copying it. Evaluation order, binding, labels, the fold and the update gates, callback write back, ranges, and the generated bytes are unchanged. | | 5.19.417 | Internal | Named argument calls to `reduce` and `Map.update` now share one argument preparation in generated Rust, keeping the refusal messages each method already had. Source order effects, reordered named binding, labels, fault order, and the generated behavior are unchanged. | | 5.19.416 | Internal | The Array callback mutators `sortBy` and `retain` are now written from one place in generated Rust for both direct and optional receivers. Fault order, field shadow dispatch, callback faults before write back, stable sorting, retained order, ranges, and the generated behavior are unchanged. | | 5.19.415 | Internal | Map `filter` and `mapValues` are now written by one shared piece in generated Rust, with the direct and optional paths choosing only which of the two they need. At run time `filter` still handles other iterable values while `mapValues` stays Map only, and behavior is unchanged. | | 5.19.414 | Internal | Or patterns and structural patterns now finish their arm through one shared step in generated Rust that writes the body and the optional guard in the extended binding scope. Binding visibility, first match wins, fallthrough, guard faults, and the generated behavior are unchanged. | | 5.19.413 | Internal | Single binding and binding free `match` arms now complete through one shared boundary in generated Rust that takes an optional static condition and guard. Guard visibility, first match wins, fallthrough on a false guard, and the generated behavior are unchanged. | | 5.19.412 | Internal | Wildcard arms and literal or range arms of `match` now share one completion path in generated Rust, where the absence of a static condition marks the irrefutable wildcard. Comparison meaning, guard order, first match wins, fallthrough, and the generated behavior are unchanged. | | 5.19.411 | Internal | All six structural pattern families are now handled by the same recursive matcher of generated Rust at the top level and when nested, so four separate adapters are gone. Temporary names in the generated code shift accordingly, which changes generated identifiers and not language behavior. | | 5.19.410 | Internal | The shared preparation and completion around the six structural pattern families are now managed in one place in generated Rust, and each family supplies only its own pattern step. Constructor admission, nominal identity, nested patterns, guards, ranges, and the generated behavior are unchanged. | | 5.19.409 | Internal | Catch all binding arms and typed single binding arms now share the scope extension, the body before guard order, and the completion they used to repeat in generated Rust. The bare enum variant route stays separate because it writes the same body in two scopes, and behavior is unchanged. | | 5.19.408 | Internal | Literal and range pattern conditions now have one owner each in generated Rust, and binding free arms share one completion. Interpolated string refusal, negative endpoints, inclusive and exclusive ranges, comparison faults, first match wins, and the generated behavior are unchanged. | | 5.19.407 | Internal | Top-level and nested or-patterns go through one shared preparation when the Rust backend lowers them, so alternative admission, binding name ordering, and first-match chaining follow a single rule. Binding order and scope, guards, diagnostics, and generated behavior are unchanged. | | 5.19.406 | Internal | A repeated binding name in a top-level or-pattern such as `case [x, x] \| [x, x]` is refused with the same message as the other pattern forms instead of producing invalid Rust. All five binding extraction routes in the Rust backend share one refusal and one tuple shape. | | 5.19.405 | Internal | Statement `for`, expression `for`, and comprehension clauses share one iteration-pattern path in the Rust backend, so pattern admission, duplicate binding refusal, and the pattern miss fault `TPZ5003` behave the same in all three. Loop control, ordering, and generated behavior are unchanged. | | 5.19.404 | Internal | List patterns compile to a single Rust slice match, which removes the index arithmetic behind prefix, rest, and suffix bindings and the panic it could reach in generated programs. Empty, fixed, rest, and nested list patterns, multiple rest refusal, and fault behavior are unchanged. | | 5.19.403 | Internal | The Rust backend inspects an `enum` value once instead of twice while matching and binds each payload through a checked lookup, removing the payload index panic the generated code could reach. The arity fault `TPZ5004` for a matching variant tag keeps its message, span, and order. | | 5.19.402 | Internal | Top-level and nested `newtype` patterns share one match in the Rust backend, which drops the two places where generated code took a value apart without checking it first. Declaration identity, one-argument shape admission, nested patterns, guards, and fault behavior are unchanged. | | 5.19.401 | Internal | The Rust backend looks up each record pattern field once instead of twice, and the single checked binding removes the panic the generated code could reach on a missing field. Open matching still accepts extra fields and declaration identity is still checked first. | | 5.19.400 | Internal | Nominal record spread validates the declared field inventory right after the spread is evaluated. A missing or duplicate field raises `TPZ5004` and an unknown field raises `TPZ5006`, so values handed in by a host program no longer crash the interpreter or disagree with generated Rust. | | 5.19.399 | Internal | Timeout handling for `concurrent` in the Rust backend keeps the checked millisecond value in one place instead of recovering it later, which removes an unchecked step from the compiler. Syntax, timeout and `else` behavior, diagnostics, and generated behavior are unchanged. | | 5.19.398 | Internal | A `concurrent` timeout is scaled to milliseconds and must fit an unsigned 64-bit value. The largest accepted forms are `18446744073709551615ms`, `18446744073709551s`, and `307445734561825m`, and the next value up is rejected with `TPZ5001` everywhere from the checker to both code generators. | | 5.19.397 | Internal | The interpreter keeps every suspendable piece of run state in one place, so a `concurrent` arm and a `defer` action each swap the whole context once on entry and once on exit instead of maintaining their own field lists. Behavior, scheduling, and diagnostics are unchanged. | | 5.19.396 | Internal | A `defer` action that faults no longer leaves anything behind. An outer comprehension keeps its full result, private module state stops being readable after a faulted record default, and a leaked type parameter no longer reaches the next `defer`. | | 5.19.395 | Internal | Sibling `concurrent` arms no longer disturb each other. Comprehensions running in parallel arms produce their full results, and an arm can no longer read a module value that is not exported through a suspended record default, which raises `TPZ5001` instead. | | 5.19.394 | Internal | When a question mark carries a fault out of an imported record default, the private access it was granted is withdrawn as the constructor unwinds. A later read of a value the module does not export raises `TPZ5001` instead of returning it. | | 5.19.393 | Internal | Every way of leaving a function in the interpreter goes through one exit, whether it is running off the end, an explicit `return`, a question mark, or a `case` arm. `defer` timing, the return type guard, and its fault message and span are unchanged. | | 5.19.392 | Internal | Closure calls in the interpreter finish checking arity, spread, and named slots before any live state moves, and every rejection returns to the caller environment through one exit. Fault precedence, messages, spans, and cross module behavior are unchanged. | | 5.19.391 | Internal | Named arguments are matched to parameters by one shared rule across builtin calls, closure calls, and `extern` calls in both engines, so a duplicate or unknown argument name is refused the same way everywhere. Arity, defaults, and type guards stay with each call form. | | 5.19.390 | Internal | When a `concurrent` deadline fires in generated Rust, the pending arms and their partial results are released before the `else` block runs, matching what the interpreter already did. Anything an arm was holding is therefore dropped before the `else` effects begin. | | 5.19.389 | Internal | All four ways of calling a closure value in generated Rust run through one path, so plain, spread, named, and spread with named calls share recursion limits, arity rules, default evaluation, and argument name faults. Behavior is unchanged. | | 5.19.388 | Internal | Named closure calls in generated Rust, with and without a spread, share one path for recursion limits, arity, slot binding, default evaluation, and argument name faults. A spread still requires a variadic parameter and still checks that every skipped fixed slot has a default. | | 5.19.387 | Internal | Named arguments to builtins are bound from one shared parameter catalog in both engines, and a receiver bound `map`, `filter`, or `reduce` correctly leaves out the parameter the receiver already fills. A spread with named arguments still reports naming faults before rejecting a non variadic spread. | | 5.19.386 | Internal | Composed functions in generated Rust complete through one path. Whatever call shape is used goes to the left function unchanged and the right function always receives exactly the one intermediate value, with argument binding, evaluation order, and fault spans unchanged. | | 5.19.385 | Internal | `Option.okOrElse` works from one shared definition in both engines. `Some` completes as `Ok` without calling the callback, `None` calls it once with no arguments and wraps the result in `Err`, and any other receiver reports the usual missing member fault. | | 5.19.384 | Internal | `Option.flatMap` and `Result.flatMap` share one definition with `map` in both engines. `Some` and `Ok` call the callback once and return its result directly, `None` and `Err` pass through untouched, and other receivers report the missing member fault in either engine. | | 5.19.383 | Internal | `Option.map` and `Result.map` are defined once for both engines. `Some` and `Ok` call the callback once and wrap the result again, `None` and `Err` pass through without calling it, and any other receiver falls through to array mapping as before. | | 5.19.382 | Internal | `Map.update` runs from one shared definition. An absent key stores the initial value without calling the function, a present key calls it once and writes the result back at the existing slot, and insertion order, alias visible mutation, and unhashable key faults are unchanged. | | 5.19.381 | Internal | `Map.filter` and `Map.mapValues` use one definition in both engines. `filter` receives the key and the value and keeps the original pair when it returns true, while `mapValues` receives the value and places the result at the same key, and pair order is preserved. | | 5.19.380 | Internal | Array `retain` runs from one shared definition in both engines. The predicate is called once per element from front to back, the kept elements are written back into the receiver where aliases can see them, and a faulting predicate leaves the array untouched. | | 5.19.379 | Internal | `sortedBy` and `sortBy` collect their keys through one shared definition. Every key callback runs front to back before anything is sorted, `sortedBy` returns a new array while `sortBy` writes back into the receiver, equal keys keep their order, and a fault leaves the receiver untouched. | | 5.19.378 | Internal | The interpreter runs `map`, `filter`, and `reduce` through the same machinery whether they are called as free functions or on an array receiver. Receiver, member, and argument evaluation order, named and spread behavior, callback effects, faults, and results are unchanged. | | 5.19.377 | Internal | Generated Rust calls `map`, `filter`, and `reduce` through one shared path instead of writing a loop out at every call site, covering free calls, array receivers, and the named, piped, and optional array forms. Evaluation order, callback effects, faults, spans, and results are unchanged. | | 5.19.376 | Internal | `map`, `filter`, and `reduce` called as plain functions have a single definition of argument checking, iteration order, and result assembly that the interpreter and generated Rust both drive. Empty inputs, callback order and effects, faults, spans, and accumulator behavior are unchanged. | | 5.19.375 | Internal | Thirty builtins that reach outside the program, including `print`, `input`, `open`, the `FS` operations, the `Test` assertions, and the Lispex operations, run from one shared definition in both engines. Effects, faults, spans, and named and spread binding are unchanged. | | 5.19.374 | Internal | The four conversion builtins `toInt`, `toIntRadix`, `fromCodePoint`, and `toFloat` join the definition shared by both engines, which now covers all 73 pure builtins that take no receiver. Conversion results, arity and type faults with their spans, and prelude shadowing are unchanged. | | 5.19.373 | Internal | Fifty more static builtins across `Bytes`, `ByteBuffer`, `Encoding`, `Codec`, `Hash`, `Cli`, `Path`, `Regex`, `CSV`, `TOML`, `URL`, `Date`, `BigInt`, and `Decimal` run from the definition shared by both engines, bringing it to 69. Default arguments, results, and faults are unchanged. | | 5.19.372 | Internal | Nineteen static builtins, including `Array.of`, `Map.new`, `Map.ofEntries`, `Set.of`, `JSON.stringify`, `JSON.parse`, and the `Math` members, run from one shared definition that the interpreter and generated Rust both call. Results, faults, spans, and namespace shadowing are unchanged. | | 5.19.371 | Internal | Generated Rust can take `Array.of`, `Map.new`, `Set.of`, `JSON.stringify`, `JSON.parse`, and the thirteen `Math` members as first class values, which the interpreter already allowed. Direct calls and shadowing by a local binding are unchanged. | | 5.19.370 | Internal | Namespace calls in the Rust backend keep one copy of each rendered argument instead of two. Named arguments are still evaluated in source order and passed in parameter order, and the count, name, duplicate, missing, spread, and positional after named faults are unchanged. | | 5.19.369 | Internal | Calling an `export` from generated Rust goes through one path whether the caller is synchronous or asynchronous. Export initialization, required member resolution, and dispatch have a single owner, and the public interfaces, lookup fault spans, and deadlines are unchanged. | | 5.19.368 | Internal | Generated Rust no longer builds program arguments and standard input just to hand them to initialization, which never used them. Export calls keep their public signatures, and entry selection, explicit `main` arguments, initialization order, and results are unchanged. | | 5.19.367 | Internal | The Rust backend no longer decides the shape of a generated program's initializer by searching the generated text for an await. Every generated program uses one asynchronous initializer, and initialization order, values, faults, exports, and results are unchanged. | | 5.19.366 | Internal | Method and protocol registration in generated programs stores its identity and method names as static text instead of allocating a copy for every entry. Registration order, last write wins for a duplicate method, lookup and fallback behavior, and results are unchanged. | | 5.19.365 | Internal | Protocol dispatch in generated programs no longer builds a lookup name for every call. Methods are stored under module, protocol, type, and method name, and dispatch finds them by borrowing those parts, with registration order, builtin fallback, and results unchanged. | | 5.19.364 | Internal | Registering several methods on the same type in a generated program allocates the type identity once instead of once per method. Registration order, last write wins for a duplicate, clearing at start, lookup, and results are unchanged. | | 5.19.363 | Internal | Method dispatch in generated programs looks a method up by type and then by name without copying either string, which removes two allocations from every receiver and protocol call. Registration order, duplicates, missing method behavior, and results are unchanged. | | 5.19.362 | Internal | The Rust backend stops copying the whole accumulated closure factory source before final formatting and renders it in place. Closure factory order and content, the generated source, and capture behavior are unchanged. | | 5.19.361 | Internal | The list of export names in generated Rust is rendered once as an array literal instead of being built from a temporary string for each name. Export declaration order, string escaping, the returned list, and runtime behavior are unchanged. | | 5.19.360 | Internal | Building the export inventory in the Rust backend borrows the module's canonical runtime export set instead of rebuilding it, and `extern` modules assemble theirs directly. Errors for a name that is not exported or an unavailable export, type only imports, and generated code are unchanged. | | 5.19.359 | Internal | Emitting a module body in the Rust backend reuses the export order already computed in the earlier pass instead of extracting every export name a second time. Type only declarations stay erased, and export ordering, generated code, and runtime behavior are unchanged. | | 5.19.358 | Internal | Resolving a record default that refers to the module's own runtime exports uses a direct lookup rather than a scan, and a missing export inventory is reported as a located compiler error instead of being assumed. Field order, deduplication, and runtime behavior are unchanged. | | 5.19.357 | Internal | The Rust backend finds each exported binding by direct lookup instead of scanning the module's bindings once per export. The ordered export list still decides record field order, and top level cell exports keep their labels and fault behavior. | | 5.19.356 | Internal | Selected imports resolve through set lookups in the entry module and in transitive modules alike, instead of scanning three growing lists, and both paths now share one decision. Declaration order in generated records, import faults, type only imports, and emitted code are unchanged. | | 5.19.355 | Internal | Assembling a module scope in the Rust backend moves its import and constant bindings into place instead of copying them. The rule that imports come before constants, redeclaration and lookup behavior, emitted code, and runtime behavior are unchanged. | | 5.19.354 | Internal | Across every host that runs the embedded Lispex evaluator, including the interpreter, generated Rust, and generated Python, the recorded behavior agrees. The `define-syntax` diagnostic `E120`, which had been attributed to the reading phase, is reported from the normalizing phase where it belongs. | | 5.19.353 | Internal | The embedded Lispex evaluator moves from 1.12.4 to 1.19.4. Programs may splice `begin` and may be empty, diagnostics use the stable public E codes, explicit output effects write to standard output, a guest semantic fault exits with code 1, and two retired resource profiles are refused outright. | | 5.19.352 | Internal | The plan for taking Lispex 1.19.4 into the product is recorded and pinned, covering which files are copied, which boundaries are left alone, and the order the work will follow. The embedded evaluator and product behavior are untouched until the next patch carries it out. | | 5.19.351 | Internal | Constant values in the Rust backend live in one keyed store, so looking a constant up by name or through a namespace no longer walks a list. Entry constants, module constants, imported record defaults, and scalar function defaults share it, and merge precedence stays as it was. | | 5.19.350 | Internal | Deciding whether a name refers to a constant in the Rust backend is a direct lookup rather than a search through the binding list. Entry constants, module constants, and imported record defaults share the same index, and emission and constant evaluation order are unchanged. | | 5.19.349 | Internal | Collecting a module's own runtime references for record defaults drops a duplicate name scan that could never find anything, because the single binding rule already guarantees the name is unique. Source order and reference identity are unchanged. | | 5.19.348 | Internal | The first occurrence of a self runtime reference inside a record default is recognized through a set of names already seen instead of scanning the growing list, and that set is reused across repeated record declarations. First occurrence ordering and hidden field identity are unchanged. | | 5.19.347 | Internal | The names a record default may refer to from earlier immutable bindings are kept in a set, so the collector tests membership rather than searching. A name becomes visible to later record defaults exactly as before, and no owned copy of it is made. | | 5.19.346 | Internal | Collecting selected import facts checks accepted constants and accepted runtime references through two name indexes instead of scanning the results for every import. First accepted wins in source order, alias handling, and export admission are unchanged. | | 5.19.345 | Internal | Namespace import facts are gathered in a single walk that looks up each target module once and produces the qualified constant values and runtime references together. Export name sorting and the behavior when a target module is missing are unchanged. | | 5.19.344 | Internal | Selected import constants and runtime references are collected in one source order pass, and the private runtime references that record defaults need are gathered during the pass that already walks records. A separate walk over the module is gone. | | 5.19.343 | Internal | Everything a module's defaults need, from constant values to record default bodies, is gathered in one forward pass over the module top level. Six separate walks over the whole source are gone and the generated result is unchanged. | | 5.19.342 | Internal | Deciding whether a record default may refer to a name no longer rescans the statements before and after it. One forward walk carries the remaining counts and the mutable names seen so far, and forward reference and mutable reference decisions are unchanged. | | 5.19.341 | Internal | Counting how many times a name is bound at module top level happens inside the declaration pass that already runs, instead of a separate scan afterwards. Type projection and record default collection receive the finished counts and behavior is unchanged. | | 5.19.340 | Internal | Module declaration handling in the Rust backend hands the type stage finished products instead of raw collections of aliases, schemas, nominal types, methods, and protocols. Generated code and runtime behavior are unchanged. | | 5.19.339 | Internal | The generated Rust backend finishes each module's alias resolution in a single step. Nested aliases inside blocks are walked, shadowed entries are dropped and colliding names are marked before the module's type information is assembled. | | 5.19.338 | Internal | Merging hidden runtime references that cross module boundaries is handled by one rule that sorts the merged list and removes duplicates. Building the type information of a program only sequences the steps. | | 5.19.337 | Internal | One step assembles a module's complete type information, from its local declarations through alias projection to runtime and record default facts. The nominal identity mode for 5.20 is decided once for the whole program instead of once per module. | | 5.19.336 | Internal | Collecting runtime values and record default facts became one step. Imported constants, own and imported runtime references, hidden references and deferred default expressions are gathered together before a module's type information is completed. | | 5.19.335 | Internal | A single pass in source order builds a module's effective aliases, its exported type surface and its type import bindings together and hands all three over as one result. | | 5.19.334 | Internal | A module's source, its runtime identity and whether it comes from the generated standard library are held as one fact instead of three separate fields. Typed JSON schema resolution, qualified type tests, runtime method selection and Lispex intrinsic admission all read that same fact. | | 5.19.333 | Internal | Exported constant values, the set of runtime export names and the set of immutable top level bindings are kept as one runtime value surface. Constant and reference collection for imports and record default analysis read the same surface. | | 5.19.332 | Internal | Six separate record default collections, covering constant values, imported and own runtime references, hidden references, deferred default expressions and references shared across modules, became one product. Record construction, default initialization and cross module propagation all work on it. | | 5.19.331 | Internal | A module's method definitions and its protocol names are held together as one local method declaration record. Alias collection reads both from it, while the effective receiver method candidate names stay with the alias projection. | | 5.19.330 | Internal | Namespace type imports and selected type imports are kept in one binding table. Typed JSON declaration lookup, namespace variant discovery, qualified type lookup and record default analysis all consult the same table. | | 5.19.329 | Internal | The plain alias table, the generic alias table and the set of aliases disabled by a name collision are held as one local resolution product. Module alias views, qualified alias admission and transitive qualified type detection all read it. | | 5.19.328 | Internal | Exported type names, the exported `enum`, `record` and `newtype` definitions and the exported receiver methods form one exported type surface. Import projection, qualified type tests, alias admission and variant discovery read from it. | | 5.19.327 | Internal | Local `enum`, `record` and `newtype` definitions and the four schema declaration tables that go with type aliases and those three forms are held as one local declaration product. Seven separate fields were removed, and alias resolution, schema lookup, type tests and export extraction read one place. | | 5.19.326 | Internal | Ten collections describing a module's effective types, schemas, receiver methods and schema origins were folded into one alias projection. The step that applies imports writes it directly and alias collection reads the same result, so the intermediate adapter is gone. | | 5.19.325 | Internal | The effective import projection and the schema origin maps are resolved during the import pass that already runs, so alias collection reuses the finished result instead of scanning every module a second time and rebuilding the origin maps. | | 5.19.324 | Internal | Module type information and alias collection share the three nominal definition tables, the four schema declaration tables and the receiver method candidate set instead of copying them. An import copies only the one collection that receives an exported item. | | 5.19.323 | Internal | Alias tables and the collision set are shared rather than deep copied, so every function, lambda and `defer` body view reuses the same tables. Only a view of an alias defined in another module builds the combined collision set it needs. | | 5.19.322 | Internal | Alias collection reuses the module information that was already built and only adds the import projection on top. A second declaration scan, a repeated walk for shadowed aliases and an unused language version field were removed. | | 5.19.321 | Internal | Top level methods, protocols, type aliases, records, enums and newtypes are collected in one pass over the source. Method identity is completed after every inherent method target is known, so it no longer depends on the order the declarations appear in. | | 5.19.320 | Internal | Top level type alias collection produces the schema references, the plain and generic resolution tables and the duplicate name marks in one pass. A structural walk then applies block local alias shadowing at every position that can hold a block. | | 5.19.319 | Internal | Protocol discovery moved into the single method collection pass, which seeds the builtin `Show`, `Eq` and `Order` protocols and gathers declared protocol names next to implementation targets and dispatch entries. Static and receiver dispatch share that one inventory. | | 5.19.318 | Internal | Schema declarations and the runtime definitions of enums, records and newtypes are built in one pass shared by alias collection and cross module type information. That pass fixes variant arity and order, record fields and the ownership of default sources. | | 5.19.317 | Internal | Whether a compilation unit declares any inherent or protocol methods is recorded while declarations are collected and read directly when items are emitted. The separate pass over the whole unit that decided this afterwards is gone. | | 5.19.316 | Internal | Cross module type information collects inherent method targets and exported receiver method candidates in one pass, then intersects the candidates with the finished set of exported enums, records and newtypes to fix the exported receiver methods. | | 5.19.315 | Internal | Alias collection builds inherent method targets, protocol qualified and inherent dispatch entries and receiver call candidates in one pass over the source. The check for whether a method registry is needed reads the recorded fact directly. | | 5.19.314 | Internal | The enum, record and newtype collectors share one run of inherent method target collection at both the alias boundary and the cross module boundary. Declaration identity and method identity are generated from that single set of inputs. | | 5.19.313 | Internal | Receiver methods brought in by an import are collected in the same pass that projects imported type definitions and schemas, so each import target and each selected name is resolved once. | | 5.19.312 | Internal | Selected imports no longer run four passes for type definitions and for record, enum and newtype schemas. One pass in source order resolves each import target once and reuses the exported definition it found when registering the schema. | | 5.19.311 | Internal | Dotted module paths are rendered by one shared builder used by all nine places that need them, including imported constants, entry import plans, receiver method imports, type definitions and schemas. Imports, aliases, defaults, diagnostics and generated output are unchanged. | | 5.19.310 | Internal | How many times each module top level name is bound is counted once per module instead of being rebuilt five times and scanned again at six uniqueness checks. Collision handling, record defaults, imports, diagnostics and generated output are unchanged. | | 5.19.309 | Internal | Six places that need the name of a simple binding, plain or typed, read it through one shared projection rather than building a throwaway list. Full pattern collection stays where several destructured names are needed, and export behavior and generated output are unchanged. | | 5.19.308 | Internal | A typed export such as `export let value: int = 41` now compiles through the generated Rust backend. It used to pass resolution, checking, the interpreter and generated Python and then stop at TPZ6001 for an unsupported typed pattern. Refusal of destructuring exports is unchanged. | | 5.19.307 | Internal | Naming a generic type without its type arguments is rejected the same way for local types and for types brought in by a selected import. The interpreter and the generated Rust backend both reach the declaration matcher and report the TPZ5099 arity fault instead of matching on identity alone. | | 5.19.306 | Internal | Matching a module qualified type checks export and type argument count the same way in the interpreter, the generated Rust backend and generated Python. A private type reaches the TPZ5001 fault for a type that is not exported, and a bare generic is no longer treated as non generic. | | 5.19.305 | Internal | The six paths that match a module qualified type compare the identity stored with the definition instead of building the identity text again at each site. Export admission, generic substitution, diagnostics and results are unchanged. | | 5.19.304 | Internal | Constructing an enum variant, a record or a newtype takes the method identity straight from the definition it already resolved and derives the declaration identity from it without a second lookup. Identity, dispatch, field and variant order and results are unchanged. | | 5.19.303 | Internal | Eight pattern and type match sites compare the identity held with the enum, record or newtype definition instead of looking the type up in the global tables again. Pattern fallback, aliases, generic substitution and diagnostics are unchanged. | | 5.19.302 | Internal | Construction of enum variants, records and newtypes reads the declaration identity and the method identity from one lookup. The tables used to be scanned twice for the two identities, and construction behavior, default order and results stay the same. | | 5.19.301 | Internal | In 5.20 mode, finding the variants of an enum value compares the identity each candidate definition already holds instead of querying the global tables once per candidate. Variant order and arity, aliases, patterns and results are unchanged. | | 5.19.300 | Internal | A selected import resolves its target module once and takes the runtime binding and the enum, record and newtype definitions from that single resolution. Aliased and unaliased names, type only exports and module initialization behave as before. | | 5.19.299 | Internal | Reading a member through a namespace resolves the module once and uses that one result for the private record default set, the environment lookup and export admission. Behavior, diagnostic codes, text and spans are unchanged. | | 5.19.298 | Internal | Module lookup, export admission, the optional runtime binding and the diagnostic for a name that is not exported are handled by one exported binding lookup shared by selected imports and namespace access. Type only exports and runtime namespace access keep their own diagnostics. | | 5.19.297 | Internal | Every accepted export declaration contributes at most one name, and registration records that single name directly instead of passing a list that was either empty or held one item. Which declarations export a name is unchanged. | | 5.19.296 | Internal | The interpreter rendered a dotted import path in two separate places, once while collecting module information and once while executing an import. Both use the same builder now, and path text, alias binding, missing module faults and initialization order are unchanged. | | 5.19.295 | Internal | Target module identities inside the typed JSON import scope are shared rather than copied for every import specification, and an already initialized module reuses its existing key. Import routing, cross module schema sources and results are unchanged. | | 5.19.294 | Internal | The runtime scope key that routes a module is created once and shared by the module registration, the current module identity, the module map and the source index. Module identity and order, routing, imports and exports keep their behavior. | | 5.19.293 | Internal | The declaration identity of the module being run is created once and shared, and an entry module carries an empty identity instead of a separate absent case. Export collection, external function dispatch and repeated execution behave as before. | | 5.19.292 | Internal | The module registration and the module type scope hold the same declaration identity instead of two identical copies, and typed JSON declaration resolution hands that identity back rather than copying the text. Protocol dispatch and namespace conformance read it as is. | | 5.19.291 | Internal | Collecting module information no longer writes into the global enum, record and newtype tables while a module is being prepared. The finished module owns its definitions and they are published to the global tables at the single point where the module is installed. | | 5.19.290 | Internal | Declaration identity and the runtime scope key travel together as one module identity owned by the prepared module, so preparation and installation no longer pass the two values side by side. Routing, imports, exports and repeated execution are unchanged. | | 5.19.289 | Internal | A module scope is no longer inserted as a placeholder before it is filled in. The finished module is built first, a new entry is written complete in one step, and an existing entry keeps its top level environment and runtime exports while only the program state is replaced. | | 5.19.288 | Internal | Preparing a module yields one finished result holding its type scope, the private values that record defaults may read and the plan for registering its callables. Installation takes that result apart at a single boundary. | | 5.19.287 | Internal | The plan for registering methods, protocol implementations and protocol declarations is built during preparation and records where each one appears in the source instead of copying declarations. The full scan that ran again at registration time is gone and registration order is unchanged. | | 5.19.286 | Internal | Preparing a module reads its top level once instead of six times. Typed JSON import routes, private immutable values, type aliases and the enum, record and newtype definitions are collected together, while the separate scan for method targets is kept because identities need the complete set. | | 5.19.285 | Internal | Program preparation assembles the module type scope locally and moves it into place once, before methods are registered and statements run. Imports, top level aliases, typed JSON routes and type definitions write straight into that one scope. | | 5.19.284 | Internal | Module scopes are created in one place for standalone programs, for resolved unit modules and for generated standard modules alike. Unit initialization still prepares the source, the environment and the identities before handing over, and initialization order and results are unchanged. | | 5.19.283 | Internal | Type information is held per module rather than per source, with a light index mapping a source to its module. Alias lookup, protocol dispatch and typed JSON routing read the module directly instead of passing through a source keyed wrapper. | | 5.19.282 | Internal | One module entry keyed by module identity owns both the runtime scope and the type scope, replacing two parallel maps. Import admission, exports, qualified type lookup and typed JSON routing read from that single entry. | | 5.19.281 | Internal | The identity of the module being run and whether it is an external module are held together as one current module context, set up once per module. External replay, export boundaries and initialization order are unchanged. | | 5.19.280 | Internal | The declaration qualifier and the runtime scope key of the current module are kept as one typed identity built once per module. An entry module keeps no declaration qualifier while still using its identity as the runtime scope key. | | 5.19.279 | Internal | The top level environment, the exported names and the private immutable values of a module are held in one runtime entry keyed by module identity, replacing three parallel maps. Import lookup, exported value checks and record default access read that entry. | | 5.19.278 | Internal | The defining source, the typed JSON import routes and the type definitions of a module are consolidated into one type entry keyed by module identity, replacing three parallel maps. Selected imports, qualified type lookup and schema routing read the same entry. | | 5.19.277 | Internal | The type state that belongs to a source, namely its module, its top level type aliases and its typed JSON declarations, is one entry instead of three parallel maps. Alias expansion and schema generation read it, and module identity survives a closure source swap as before. | | 5.19.276 | Internal | Each enum, record and newtype definition carries its defining source together with its identity and declaration, so three parallel routing maps for selected imports are gone. Local and qualified lookups read declaration spans straight from the definition. | | 5.19.275 | Internal | Method identity is computed once per type and stored with its definition, so the two parallel identity tables are gone. Method registration and identity lookup resolve straight from the type tables, and identity behavior in 5.20 and 5.19 is preserved. | | 5.19.274 | Internal | The full enum, record and newtype declarations sit with their runtime identity and payload in one descriptor, and four parallel declaration tables were removed. Module loading, selected imports and qualified lookup consume the same descriptor. | | 5.19.273 | Internal | Enum and record runtime identity and payload are unified under one descriptor table each, removing four parallel identity maps. Module loading, active definitions and selected imports share the descriptors directly. | | 5.19.272 | Internal | Variant tables and record field and default sequences are built once at module initialization and shared afterwards. A selected import shares them instead of copying the payload, and five copying routes now pass a reference, with order, arity and defaults unchanged. | | 5.19.271 | Internal | Generic type arguments are created once by the parser and shared with the checker, schema projection, and the Python backend, so generic conformance checks and alias expansion no longer copy those nodes. Nested generics, typed patterns, JSON schema output, and diagnostics are unchanged. | | 5.19.270 | Internal | Pipeline stages now reuse a single node created by the parser instead of copying the right side of each pipe while a program runs. Evaluation order, placeholder substitution, first argument insertion, field shorthand, and diagnostics behave exactly as before. | | 5.19.269 | Internal | Array, set, and `Map` comprehensions now share one body node created by the parser, so the interpreter carries it into each clause without copying. Clause order, scoping, key and value evaluation, duplicate handling, diagnostics, and results are identical. | | 5.19.268 | Internal | A `newtype` declaration is stored once and shared by nominal schemas, schema declarations, and JSON schema output instead of being copied three times while a program runs. Construction, unwrapping, pattern matching, generic parameters, and diagnostics stay the same. | | 5.19.267 | Internal | Record declarations and field default expressions are created once by the parser and shared by schema declarations, JSON schema output, and record construction, so they are no longer copied while a program runs. Field order, defaults, updates, derives, and diagnostics are unchanged. | | 5.19.266 | Internal | An `enum` declaration is held once and shared by nominal schemas, schema declarations, and JSON schema output rather than copied three times during execution. Variant order and arity, construction, pattern matching, derives, and results are unchanged. | | 5.19.265 | Internal | Type alias declarations and the type behind them are shared by the alias table, schema output, block scopes, and the Python backend instead of being copied. Alias and generic parameter resolution, type expansion, conformance, diagnostics, and results are unchanged. | | 5.19.264 | Internal | A `defer` action is registered by reference to the parsed expression instead of being copied, so deferred work carries less overhead at run time. Registration point, lexical scope, last in first out order, early exit unwinding, and fault handling are unchanged. | | 5.19.263 | Internal | Destructuring `let` bindings reuse the parsed pattern instead of copying it when the interpreter builds the binding step. Binding rules, mutability, pattern matching, diagnostics, and results are unchanged. | | 5.19.262 | Internal | String literals are shared rather than copied while interpolation and tagged templates run, so each string does less work. Escape decoding, tag lookup, part order, rendering, template parts, and results are unchanged. | | 5.19.261 | Internal | Assignments reuse the parsed target and value, including nested member and index paths and `??=`, instead of copying them for each step. Target validation, mutability checks, evaluation order, `??=` laziness, rejection of optional paths, and nested updates are unchanged. | | 5.19.260 | Internal | A `for` loop pattern is created once and reused whether the loop appears as a top level statement, inside a block, or as a value expression. Binding, iteration order, loop scopes, `break` and `continue`, collected values, and diagnostics are unchanged. | | 5.19.259 | Internal | A `while` condition, including one written with a binding pattern, is created once and reused on every iteration instead of being copied each time round the loop. Order of evaluation, scopes, `break` and `continue`, and loop results are unchanged. | | 5.19.258 | Internal | Each explicit field expression in a record literal or update is created once and shared by the checker, the backends, and record evaluation instead of being copied for nominal records. Source and evaluation order, spreads, defaults, and resulting records are unchanged. | | 5.19.257 | Internal | Building a nominal record with a spread reuses the parsed spread expression instead of copying it. Explicit field validation, duplicate and unknown field diagnostics, base first evaluation, defaults, and field order are unchanged. | | 5.19.256 | Internal | Member access holds the receiver in one shared slot, so builtin methods on collections, strings, `Option`, `Result`, JSON, bytes, paths, and dates no longer copy the whole receiver. Member resolution, mutability checks, missing member diagnostics, and dispatch are unchanged. | | 5.19.255 | Internal | `JSON.parseAs` and `JSON.decode` evaluate their argument directly at the call site, including through pipes and placeholders, instead of copying the expression first. Argument validation, diagnostics, scope, evaluation order, and decoding behave as before. | | 5.19.254 | Internal | Each arm of a `concurrent` expression is created once by the parser and shared with the checker and the backends, so starting an arm no longer copies its body. Arm order, isolated environments, scheduling, timeouts, and fault behavior are unchanged. | | 5.19.253 | Internal | A `concurrent` timeout `else` block is handed to the running expression directly instead of being copied. Timeout handling, arm scheduling, expiry, abandonment, and `else` execution are unchanged. | | 5.19.252 | Internal | Ordinary calls, calls to function values, builtin calls, and calls through a pipe evaluate the callee in place instead of copying it into a new step. Special call routes, evaluating the callee before the arguments, and short circuit behavior are unchanged. | | 5.19.251 | Internal | Interpolated strings and tagged templates create the literal once on entry and evaluate each interpolated piece in place, rather than copying the whole literal and the piece at every segment. Decoding, order, rendering, template parts, and values are unchanged. | | 5.19.250 | Internal | `match` sets up its scopes before guards, arm bodies, and `return` values run, and takes each of them straight from the shared list of cases rather than copying. Case order, binding scope, and return behavior are unchanged. | | 5.19.249 | Internal | `Some`, `Ok`, `Err`, and `newtype` constructors evaluate their argument in place, and `enum` and protocol calls walk their arguments left to right through one shared list instead of a reversed stack of copied nodes. Argument order, including a call reached through a pipe, is unchanged. | | 5.19.248 | Internal | Ordinary calls, method calls, and optional calls take each argument straight from the shared argument list and move finished values onto the stack rather than copying them. Evaluation order and argument order are unchanged. | | 5.19.247 | Internal | Array elements and spreads, set elements, `Map` keys and values, and record field values are evaluated straight from the shared list held by the surrounding literal instead of copying every child. Evaluation order and results are unchanged. | | 5.19.246 | Internal | The checker knows the expected result type of the surrounding call before it looks at arguments, so an `Ok` inside a `Result.flatMap` callback resolves to the concrete type. The false `TPZ5001` that reported `Result` where `Result` was expected is gone. | | 5.19.245 | Internal | Running a block for `if`, for a `concurrent` timeout `else`, or as a block expression reuses the parsed block instead of copying it. Evaluation behavior is unchanged. | | 5.19.244 | Internal | Statement execution evaluates child expressions in place, covering conditions, `return` and `break` values, expression statements, and binding initializers, instead of wrapping each one in a copied node. Evaluation order and behavior are unchanged. | | 5.19.243 | Internal | A running block keeps a reference to the block and the position it reached instead of copying the statements still to run, and top level statements and expressions are evaluated in place. Behavior is unchanged. | | 5.19.242 | Internal | Lexical scopes look bindings up by name through a direct map rather than an ordered tree, since declaration and evaluation order comes from the program itself. The self hosted checker finishes its own workload faster and behavior is unchanged. | | 5.19.241 | Internal | The checker applies a function declared result type only to real `return` checks and takes the expected type for a nested `None`, `Ok`, or `Err`, for standard wrapper calls, and for generated Lispex calls from the value being checked. False reports about nested values are gone. | | 5.19.240 | Internal | An expected type is attached to the exact expression it belongs to and reaches only the `if` and `match` branches and block endings that produce the surrounding result. Nested control flow and intermediate statements no longer draw false type errors. | | 5.19.239 | Internal | Commands that carry the compiler use the Rust Stage 0 build by default, so an out of date embedded checker no longer raises false diagnostics on everyday runs. A generic `Option` ambiguity was fixed, and each payload of a multi payload `enum` pattern keeps its own type. | | 5.19.238 | Internal | Lispex behavior matches across the interpreter, generated Rust, and generated Python, covering reader and normalizer diagnostics, the lookup order and span for an unbound name, short circuit evaluation, and output already written when a program faults. The capability manifest ships in full. | | 5.19.237 | Internal | The Python backend keeps the known length and the facts shared by every element of an array through `sort` and `sortBy`, while `retain` drops the known length because the result can be shorter. | | 5.19.236 | Internal | Static array operations such as `push`, `pop`, `reverse`, `insert`, `removeAt`, and `clear` update everything the Python backend knows about each slot and about the array length, and keep aliases of the same storage in step. | | 5.19.235 | Internal | The Python backend tells module level collections apart from local ones and gives every mutable binding its own identity, so a change through one alias updates exactly the bindings that share the collection and reassignment starts clean. | | 5.19.234 | Internal | The Python backend separates keys it knows are present from keys whose value it fully knows, so `Map.update` on a known key combines what the callback returns with the starting value. | | 5.19.233 | Internal | Collection changes in the Python backend are described as refresh, remove, or invalidate, so `Map.remove` with a known key drops only that key and leaves what is known about the rest of the `Map` in place. | | 5.19.232 | Internal | `Map.insert` with a known key records what was inserted, so the Python backend carries the value shape, its call form, and any wrapped contents into later lookups of that key. | | 5.19.231 | Internal | Array and `Map` changes go through one path in the Python backend, so a change made through any binding invalidates the other bindings that share the same collection in the same way. | | 5.19.230 | Internal | When the Python backend knows every key a `Map` holds and all of them carry the same kind of value, a lookup with a key computed at run time uses that shared knowledge. | | 5.19.229 | Internal | `Map.get` with a key known only at run time benefits from what the Python backend learned about an immutable `Map`, as long as every known entry carries the same kind of value. | | 5.19.228 | Internal | A `Map` built from known keys carries the full picture of each value in the Python backend, including its shape, how it can be called, and what it wraps, so matching on a `Map.get` result works from real information. | | 5.19.227 | Internal | The Python backend describes `Option` and `Result` contents by path instead of a fixed pair of fields, so values nested to any depth are tracked the same way. | | 5.19.226 | Internal | When every branch of an `if` or `match` produces the same shape, the Python backend keeps what it learned about the value, its call form, its nested `Option` contents, and its record fields, preferring what it observed over what was declared. | | 5.19.225 | Internal | A function that returns a callable inside `Option` or `Result` keeps that call form in the Python backend, and what the body actually returns is preferred over the declared fallback. | | 5.19.224 | Internal | Module level values carry their shape, their `Option` or `Result` contents, and their call form across the Python backend, so a selective import or a namespace member access sees the same information as the definition. | | 5.19.223 | Internal | Binding a value out of `Some` or `Ok` yields the same information in the Python backend whether it came from a constructor, a binding, a return, or an array being matched. | | 5.19.222 | Internal | The Python backend records the shape of each array slot and what its `Option` holds, and when every slot agrees it uses that for an index computed at run time or for a pattern match. | | 5.19.221 | Internal | Arrays of `Map` values carry per slot information through literals, spreads, and aliases in the Python backend, so reading a known index sees both the `Map` contents and the records inside it. | | 5.19.220 | Internal | Records stored in an array keep their whole field tree in the Python backend, so reading a known index reaches nested fields whether the record sits there directly or inside an `Option`, a `Result`, or a `Map` value. | | 5.19.219 | Internal | Replacing a record field clears and rebuilds what the Python backend knows about that field subtree in one step, shared by record updates and assignments through a path. | | 5.19.218 | Internal | Declared and observed record contents of array slots live in one place in the Python backend, so reading a field of an array element goes through a single lookup. | | 5.19.217 | Internal | Assigning through a nested record path replaces only the field being written in the Python backend, leaving what is known about sibling fields and other roots in place. | | 5.19.216 | Internal | Assigning a whole record into a field carries its entire nested contents in the Python backend, including value shapes, call forms, and callback targets. | | 5.19.215 | Internal | Record construction, record updates, and field assignments record what a field value is through one shared path in the Python backend, so all three agree. | | 5.19.214 | Internal | Record literals and record updates gather field information and file it under the same paths through one shared step in the Python backend. | | 5.19.213 | Internal | Default values of nominal records that hold a callable are described in one place in the Python backend, so module wide analysis and record updates read the same defaults. | | 5.19.212 | Internal | Updating a nominal record checks its callable defaults once and walks the value a single time instead of twice, and no longer copies the whole default table in the Python backend. | | 5.19.211 | Internal | Reading a record field in the Python backend resolves its shape, its call form, and any callback target through one shared rule instead of three separate paths. | | 5.19.210 | Internal | Record fields held inside an `Option` follow that same rule in the Python backend, and nested accesses build up one canonical path to the field. | | 5.19.209 | Internal | Records reached through a `Map` value, an `Option`, or an `Ok` are projected straight from the binding, namespace, return, or constructor they came from, without copying the whole table in the Python backend. | | 5.19.208 | Internal | Matching on a `Map.get` result resolves the `Map`, the key, the value shape, the call form, and nested records in one place in the Python backend. | | 5.19.207 | Internal | Array indexing in the Python backend finds the binding and decides whether the index is known ahead of time through one shared step, so every slot lookup follows the same path. | | 5.19.206 | Internal | Array literals and spreads are walked once in the Python backend to collect callback targets, known lengths, and call forms, and branches are joined the same way. | | 5.19.205 | Internal | Everything the Python backend knows about array elements, from slot shapes and `Option` contents to callback targets, call forms, and lengths, lives in a single structure. | | 5.19.204 | Internal | `Map` values carry their shape, their `Option` contents, and their call form in one structure for both local bindings and module level values in the Python backend, produced in a single pass. | | 5.19.203 | Internal | Nested record fields that hold cooperative callbacks keep their scheduling targets through field updates, branch joins, and pattern destructuring, with all three paths resolved by one shared lookup. | | 5.19.202 | Internal | Receiver shapes and call information for record members are gathered once from both the parser view and the checker view of a record, which removes a duplicated filtering step. | | 5.19.201 | Internal | Direct field access and wrapper paths read nested record receiver shapes and call signatures from the same source, so both routes agree on which member call is valid. | | 5.19.200 | Internal | A function returning a record wrapped in `Result` keeps the nested member information of that value in the Python backend, so calls written after `unwrap()` reach the right method. | | 5.19.199 | Internal | Values pulled from a `Map` that hold a record inside `Result` carry their nested member information, so an `unwrap()` chain after `Map.get` reaches the record methods in the Python backend. | | 5.19.198 | Internal | A value taken from a `Map` that holds a record inside `Option` no longer loses its nested member information, so optional chaining after `Map.get` resolves the record methods in the Python backend. | | 5.19.197 | Internal | Pattern bindings taken from `Map.get` know the receiver shape and call signature of the record they hold, so nested member calls on those bindings compile correctly for Python. | | 5.19.196 | Internal | Looking up a `Map` through a local alias with a fixed key reports the right receiver shape, including the shape held inside `Option` and `Result`, when Python is generated. | | 5.19.195 | Internal | Declared call signatures for callables stored in a `Map` reach the Python backend through local aliases, so calling one bound by a `Map.get` pattern passes arguments the declared way. | | 5.19.194 | Internal | Callables stored in an array keep their declared signatures after the array is registered as mutable and after later updates, so dynamic calls and pipe calls still dispatch correctly. | | 5.19.193 | Internal | Records reached through an `Option` alias expose the signatures and default values of their callable fields, so calling such a field through optional access uses the declared defaults. | | 5.19.192 | Internal | A binding declared as a record inside `Option` keeps its declared receiver shape when the variable is reassigned, because the declaration no longer shares a lifetime with the tracked value. | | 5.19.191 | Internal | Bindings and exported module values that hold a record inside `Option` carry the receiver shapes and call signatures of nested fields, and optional chaining resolves through them. | | 5.19.190 | Internal | A function returning a record wrapped in `Option` keeps the nested member information of its return value, so optional chaining on the call result resolves in the Python backend. | | 5.19.189 | Internal | Imported modules are put in a settled order before record return information and record field defaults are computed for Python, so both are worked out once imports and mutation facts are all registered. | | 5.19.188 | Internal | Default values of record fields are captured in the module where the record is declared, and an empty default stays empty, so a module that imports the record does not reinterpret it in its own namespace. | | 5.19.187 | Internal | Building a named record fills the call signatures of its callable fields from the declared field types first, and a default expression then overrides the parameter names, defaults, and cooperative targets. | | 5.19.186 | Internal | Receiver shapes and call signatures reach every level of a nested record, whether the type is written in place or comes from a type alias the checker resolved. | | 5.19.185 | Internal | A function with a declared structural record return type supplies the receiver shapes and call signatures of that record's direct fields, including when the value comes from the trailing expression of the body. | | 5.19.184 | Internal | Calls made through a namespace import resolve by a single rule that accounts for the import and for any local name that shadows it, instead of several lookups that could disagree. | | 5.19.183 | Internal | Every function in a module is registered before record return information is worked out, so a wrapper calling a function declared further down the file gets the same information as one calling upwards. | | 5.19.182 | Internal | A wrapper that forwards another function's result inherits that function's cooperative callback targets, so the targets survive any depth of forwarding. | | 5.19.181 | Internal | Return information for a function records the cooperative callback targets of record fields next to their receiver shapes and call signatures, so a member call on the returned record finds them. | | 5.19.180 | Internal | Updating a record starts from the declared information of the base value and replaces the signature, cooperative targets, and nested information only for the fields written in the update. | | 5.19.179 | Internal | A structural record update copies the receiver shapes of the base value and swaps in new shapes for the fields and nested fields that the update names. | | 5.19.178 | Internal | Code generation, dynamic default lookup, and binding information resolve the base of a named record construction the same way, so receiver shapes come out consistent whether they are read from the declaration or from the field values. | | 5.19.177 | Internal | Declared shapes for array elements, including the shape inside a nested `Option`, are kept apart from callback information gathered along the flow, so reassigning the array does not lose them. | | 5.19.176 | Internal | Indexing an array keeps the element shape the checker worked out, and plain names, parenthesised expressions, index access, and member access resolve to the same root through local aliases, mutating calls, and immediately invoked lambdas. | | 5.19.175 | Internal | `defer` bodies are analysed after the block they belong to, in reverse order of registration, so the Python backend sees deferred mutations and rebindings in the order they actually run. | | 5.19.174 | Internal | Mutation tracking treats the taken side and the skipped side of `&&`, `\|\|`, `??`, and optional receiver calls separately, so an outer alias touched on only one side is still accounted for. | | 5.19.173 | Internal | The bodies of `for`, `while`, and `loop`, comprehensions, and `concurrent` arms including the timeout `else` build their set of mutated roots by one growing rule. | | 5.19.172 | Internal | A local alias carries the set of parameter roots it can reach, branches of `if` and `match` merge those sets where they exit, and an alias declared inside a case shadows the outer one. | | 5.19.171 | Internal | Mutable local arrays are tracked as roots from their declaration through every reassignment, so the information attached to them is dropped and rebuilt at the right points. | | 5.19.170 | Internal | Programs that use mutable local aliases are exercised against the Python backend the way the compiler actually runs them, with the entry module and its imports resolved and checked together. | | 5.19.169 | Internal | The checker follows a mutable local alias back to the immutable array parameter it came from and treats that parameter as an effect root, using the types written in the source. | | 5.19.168 | Internal | Array parameters of a typed lambda stored in a variable are tracked inside that lambda, so mutations made directly or through a wrapper show up as effects of calling the stored value. | | 5.19.167 | Internal | When an `if` or an exhaustive `match` yields callables with matching signatures, the result keeps every array parameter that any reachable branch mutates rather than only the ones common to all. | | 5.19.166 | Internal | An exported callable keeps the record of which array parameters it mutates, and that record comes back when the value is reached through a selected import or a namespace call. | | 5.19.165 | Internal | Bindings that hold a callable carry which array parameters it mutates, and that set stays in step when an immutable alias is registered, refreshed, or cleared. | | 5.19.164 | Internal | Parameter mutation effects for local calls, selected imports, and namespace calls are worked out by growing from direct summaries until they settle, so the answer is the same however deep the wrappers go. | | 5.19.163 | Internal | Each function records which array parameters it mutates, so a call to a known mutating function drops the stale information about the arrays passed in right at the call site. | | 5.19.162 | Internal | Typed lambda parameters are registered as bindings, and a mutable array passed through a chain of aliases into an immediately invoked lambda has its outer information dropped as soon as the call returns. | | 5.19.161 | Internal | Mutations inside an immediately invoked lambda, including those through its parameters, count as effects of the surrounding expression, so information about captured outer values is dropped as soon as the lambda runs. | | 5.19.160 | Internal | Mutations that happen partway through an expression are accounted for while the expression is lowered into statements, so a later part of the same expression reads the array as it stands after the call. | | 5.19.159 | Internal | Outer values that a `concurrent` arm or its timeout `else` modifies are gathered before the bodies are generated, so no arm reads stale information and all of it is dropped at one shared boundary. | | 5.19.158 | Internal | Patterns, clauses, filters, and bodies of value-collecting `for` loops and comprehensions are generated inside their own scope, so a loop that never runs or a filter that never passes leaves no callback information behind. | | 5.19.157 | Internal | A call on an optional receiver is generated inside its own scope, so the arguments are not evaluated and no callback information escapes when the receiver turns out to be absent. | | 5.19.156 | Internal | Branches of `if`, short-circuit operators, `??`, and direct matches are generated inside their own scope, so a branch that does not run cannot leave callback assignments that change how later code is specialised. | | 5.19.155 | Internal | Fast guards in a direct match register their pattern bindings and the call information of values taken from `Map.get` in the scope of that arm, entering and leaving it without copying the surrounding context. | | 5.19.154 | Internal | Whether a case guard needs lowering is decided with the surrounding context in view, so a guard holding a cooperative comprehension or a higher order function takes the guarded path in both statement and value matches. | | 5.19.153 | Internal | Each iteration of a cooperative loop reaches its scheduler yield point right after the pattern guard and bindings and before the body runs, which keeps the interleaving between concurrent tasks deterministic. | | 5.19.152 | Internal | Statement `for` loops, value-collecting `for` loops, and comprehensions handle the item pattern through one shared routine, so the pattern logic and the information it updates no longer exist in three copies. | | 5.19.151 | Internal | A statement `for` loop follows the same order as a value-collecting one, taking the item, running the pattern guard, then binding into scope, so destructuring and the errors it raises behave alike in both. | | 5.19.150 | Internal | An `if` used as a statement lowers its condition through the shared expression boundary, so a condition containing a loop or a higher order function evaluates and branches exactly as it does on Stage 0. | | 5.19.149 | Internal | Array mutation analysis lists every kind of statement that can hold one, covering assignments, `while` loops, `using` blocks, `break`, and `return`, so a missing case fails the build instead of passing quietly. | | 5.19.148 | Internal | Array mutations are tracked inside string interpolations, the left side of a pipe, and the clauses and bodies of comprehensions as well, so local and module level bindings both read correctly after such a mutation. | | 5.19.147 | Internal | Deciding whether a call mutates an array now looks straight through parentheses around the receiver instead of rebuilding the expression, which takes work out of compilation. | | 5.19.146 | Internal | Assignment to a plain name, to an index, and to a record field are generated by separate paths, each with its own mutability check, operand evaluation, write, and information update. | | 5.19.145 | Internal | Every assignment finishes through one closing step that refreshes the information attached to the target, so mutable arrays and records are never left holding stale details. | | 5.19.144 | Internal | The right side of an assignment is fully evaluated just before the write and before the target is read again, so an assignment that refers to its own target produces the right value. | | 5.19.143 | Internal | Assignments to a simple name, to a direct index, and to a record path evaluate their parts in one common order, so the effects inside an assignment happen predictably. | | 5.19.142 | Internal | A member chain on the left of an assignment is walked once to pull out both the root binding and the field path, replacing repeated traversals. | | 5.19.141 | Internal | A destructuring binding funnels every outcome of its pattern guard through one closing step, so names enter scope the same way once the guard passes. | | 5.19.140 | Internal | Local, entry module, and imported top level bindings are generated by one shared path, so the three no longer drift apart in how they are produced. | | 5.19.139 | Internal | Compound assignments finish lowering the right side before the write and before the target is read back, so a compound update applies its steps in the intended order. | | 5.19.138 | Internal | `??=` leaves the right side unevaluated whenever the target already holds a value, so nothing on that side runs when the assignment does not take effect. | | 5.19.137 | Internal | Assigning to a record field evaluates the base value and the field path in a fixed order, which makes updates to deeply nested records behave consistently. | | 5.19.136 | Internal | Array index assignment evaluates its operands in a fixed order, so an index or a value that has effects of its own behaves the same as on Stage 0. | | 5.19.135 | Internal | The Python backend now classifies assignment targets through separate paths for plain names, index positions, and record field paths, keeping the evaluation of operands apart from the assignment itself. | | 5.19.134 | Internal | Destructuring bindings on the Python backend run through one path that decides when a name comes into scope, checks the pattern condition, and rejects duplicate declarations. | | 5.19.133 | Internal | Simple and destructuring `let` bindings share the same helper on the Python backend, so local and global registration stay in step with each other. | | 5.19.132 | Internal | Compound arithmetic assignments such as `+=` lower through the same arithmetic path as ordinary operators on the Python backend, keeping precision and evaluation order right for integer, floating point, and string operands. | | 5.19.131 | Internal | Assignments that start from an index and continue through further fields or indices are rebuilt from the innermost step outward on the Python backend. Plain, compound, and coalescing forms match the evaluation order and short circuiting of Stage 0. | | 5.19.130 | Internal | Nested record field assignments are rebuilt from the innermost field outward on the Python backend using the ordinary field read and record update steps, so plain, compound, and coalescing assignments follow Stage 0 evaluation order. | | 5.19.129 | Internal | Compound assignments into an index use the same arithmetic step as variables and record fields on the Python backend, so integer, floating point, and string addition behave the way they do on Stage 0. | | 5.19.128 | Internal | A compound assignment into an index reads the current value of that position before the right hand side runs on the Python backend. A program whose right hand side changes the same position now gives the Stage 0 result. | | 5.19.127 | Internal | Destructuring `let` follows one rule set in local scope, in the entry module, and in imported modules, covering value evaluation, the pattern condition, `TPZ5001` type guards, and rejection of duplicate bindings. | | 5.19.126 | Internal | Nested and local immutable typed `let` bindings evaluate the value first and check its type before the name is bound, so a type mismatch raises the same `TPZ5001` fault as Stage 0. | | 5.19.125 | Internal | Top level `const` and simple `let` are generated the same way in the entry module and in imported modules, and immutable typed bindings check their type while running and report `TPZ5001` exactly as Stage 0 does. | | 5.19.124 | Internal | External and ordinary imported modules hand back their exports and the name of their initializer in the same shape on the Python backend, so both kinds are gathered by a single path. | | 5.19.123 | Internal | Code generation for an ordinary imported module is driven from one place that prepares its inputs, emits the body, builds the namespace, and writes the initializer. | | 5.19.122 | Internal | The body of an ordinary imported module is emitted in source order by one dedicated step that also places exports, diagnostics, and default helpers. | | 5.19.121 | Internal | Preparation for an ordinary imported module is an explicit step that settles context, default values, protocol and receiver and function registration, initializer seeds, and import bindings before any code is written. | | 5.19.120 | Internal | Code generation for an external imported module happens inside one boundary that owns its function slots, namespace, initializer, and exported results. | | 5.19.119 | Internal | Exported statements are kept in source order while a module is analysed, so preparing the exports of an external module no longer walks over private statements. | | 5.19.118 | Internal | Top level import declarations are kept in source order during module analysis, and binding preparation for imported modules reads that ordered list directly instead of searching the source again. | | 5.19.117 | Internal | Top level function declarations are kept in source order during module analysis, and both function registration and return type information read only that shared record. | | 5.19.116 | Internal | Protocol names and their receiver implementations are kept in source order during module analysis, which removes three repeated passes over the source during registration and preparation. | | 5.19.115 | Internal | Top level `const` and `let` names are collected once into an ordered list, and receiver method tables are built from that list without another pass over the source. | | 5.19.114 | Internal | Name counts at module level and the bindings of each statement are gathered in one pass for record default scopes, so the same patterns are not taken apart twice. | | 5.19.113 | Internal | Default values of exported immutable `let` bindings in an imported module are derived straight from facts already gathered, without walking the parsed tree again or searching it item by item. | | 5.19.112 | Internal | Runtime and mutable `let` names for each statement are stored in source order, so moving between scopes no longer re-examines the parsed patterns. | | 5.19.111 | Internal | Counts of top level functions, `const`, and `let` are taken during the pass that already reads the module, which removes the separate preliminary passes helper scopes used to need. | | 5.19.110 | Internal | Deciding which names are live when a statement is entered and left is handled by the record default scope itself, and record, newtype, enum, and alias construction each moved into its own step. | | 5.19.109 | Internal | Records, newtypes, enums, JSON schema aliases, import scopes, and receiver implementations are all collected in source order by one pass over a module. | | 5.19.108 | Internal | Helper scopes for record defaults count runtime bindings once and gather earlier mutable names as they move forward, which leaves two straight passes. | | 5.19.107 | Internal | JSON schema type aliases and import scopes are collected together with named type definitions in one pass, so the parsed tree is not read again once the catalogue is complete. | | 5.19.106 | Internal | The catalogue of records, newtypes, and enums is shared without copying and is duplicated only when imported defaults are added to it. | | 5.19.105 | Internal | Declaration and receiver method identities are attached to records, newtypes, and enums while the catalogue is built, so each later user of the catalogue no longer repeats that work. | | 5.19.104 | Internal | Base tables for records, newtypes, and enums are built once per module, and the entry module and schema generation both derive from those same tables. | | 5.19.103 | Internal | Record fields, their default values, and helper identities are constructed once per module and reused by everything that needs them. | | 5.19.102 | Internal | Namespace member candidates for record defaults are collected per record in one input pass and cross checked after the catalogue is assembled, with no further reading of the parsed tree. | | 5.19.101 | Internal | Earlier immutable `let` bindings and the runtime defaults each record refers to are computed in one input pass and shared by entry, schema, and class generation. | | 5.19.100 | Internal | Own and exported `const` values are evaluated in source order within a single pass, and a lookup into another module reads the shared list of that module directly. | | 5.19.99 | Internal | Default import bindings and runtime default names are gathered by one pass over a module instead of two separate catalogue builds and repeated reads of the source. | | 5.19.98 | Internal | Immutable top level `let` names and exported `const` and `let` names are computed in one traversal, and both the runtime default and hidden reference analyses read that same result. | | 5.19.97 | Internal | Import identities, local names, and lookup indices are derived once per compilation unit, and the analyses share that catalogue with its source order preserved. | | 5.19.96 | Internal | Collecting defaults from selected imported `const` uses the shared check for uniquely bound names, so one rule decides how many times a top level name may be bound. | | 5.19.95 | Internal | How many times each top level name is bound is counted once per module, and both import handling and hidden reference analysis read the same counts. | | 5.19.94 | Internal | The set of exported `const` names and single binding `let` names is computed once per module, which stops the source from being scanned again for it. | | 5.19.93 | Internal | Top level immutable `let` names that survive to run time are computed once per module, and the hidden reference and runtime default analyses query that one shared set. | | 5.19.92 | Internal | Hidden runtime references in imported modules are followed through layered views over the existing name tables rather than through temporary copies of those tables. | | 5.19.91 | Internal | The runtime references made by the defaults of each record are computed once per module and shared by definition, schema, and class generation. | | 5.19.90 | Internal | Top level and exported `const` values needed by record defaults are evaluated once per module and shared with every later stage without copying. | | 5.19.89 | Internal | Imported and own `const` values are assembled into one ordered sequence for record defaults, which local definitions and export generation both read directly. | | 5.19.88 | Internal | Blocked name sets and pipe placeholders inside `concurrent` blocks are shared rather than copied, so entering a branch no longer duplicates those collections. | | 5.19.87 | Internal | The Python backend keeps one table per lexical scope and copies it only when that scope actually changes, instead of duplicating the whole stack in advance. | | 5.19.86 | Internal | Resolved module names are used directly for source and schema identity on the Python backend, so those names are no longer rebuilt each time a context is copied. | | 5.19.85 | Internal | Dispatch tables for functions, methods, protocols, and namespaces are shared on the Python backend and copied only when they change, which makes entering a branch cheaper. | | 5.19.84 | Internal | Temporary contexts for receivers and protocols on the Python backend reuse the module identity of the caller and the shared type tables, so a branch copy stays a light reference copy. | | 5.19.83 | Internal | Value name tables for the receiver methods of imported modules are shared instead of copied whole while modules are set up and methods are generated. | | 5.19.82 | Internal | Entry, external, and imported module contexts on the Python backend read the module alias tables held by the checker directly, so creating a context no longer copies those tables. | | 5.19.81 | Internal | Record definition tables move into the Python code generation context once, and default helper work lists refer to existing names and source expressions instead of copying whole trees. | | 5.19.80 | Internal | Lookups for generic specializations go through one index on the native path, and the Rust name generated for each specialization is remembered instead of being built again. | | 5.19.79 | Internal | Building the Rust name for a generic specialization no longer copies the type arguments or creates a temporary list to join them. | | 5.19.78 | Internal | Parameter names of native functions are taken straight from the source and shared, so generic specializations no longer copy the list of names. | | 5.19.77 | Internal | Generic type parameters and named call parameters are resolved from the source text and from the declaration itself, without rebuilding temporary lists of names. | | 5.19.76 | Internal | Generic specialization lookups read names and type arguments in place through a nested index, so no key strings or lists are built for each lookup. | | 5.19.75 | Internal | Ordinary and generic function signatures are held once and shared, so generating a function body or a call site no longer copies the whole signature. | | 5.19.74 | Internal | Fully native builds receive their own table of active functions while hybrid builds share one, which removes a copy of that table for every function. | | 5.19.73 | Internal | Hybrid functions read the per module information computed in advance, so module records are no longer filtered and copied again for each function. | | 5.19.72 | Internal | One index of typed local variables is shared by every context, so hybrid functions no longer each copy that index. | | 5.19.71 | Internal | Native lookups of typed local variables read the name in place from one index that points back at the source, so no string is built for each lookup. | | 5.19.70 | Internal | Results from the last pass that settles which functions qualify for hybrid compilation feed straight into the build plan, so the surviving candidates are not generated a second time. | | 5.19.69 | Internal | Hybrid candidates are collected in one pass into per module tables ordered by their place in the source, which removes repeated filtering and copying of the full candidate list. | | 5.19.68 | Internal | Hybrid build decisions for each function are recorded in one index keyed by module and source position, so reporting them no longer walks the parsed tree or rebuilds module keys. | | 5.19.67 | Internal | The Rust backend derives its selection report from the same plan it emits from and hands that plan to the emitter once, so the whole plan is no longer copied. | | 5.19.66 | Internal | Closure plans in the Rust backend are stored under a module, function, and span hierarchy, so selection and code generation look a plan up directly instead of assembling a text key on every lookup. | | 5.19.65 | Internal | The JSON IR reader indexes operations over the strings already parsed from the document, so required operation identifiers still reach the finished program without copying text for every lookup. | | 5.19.64 | Internal | JSON and compact IR operations are checked for their minimum operand shape while they are decoded, and index references are compared at that moment, so a malformed program is rejected during reading instead of on a later rescan after assembly. | | 5.19.63 | Internal | Whether a program needs host services is decided once while the IR is decoded, from the resolved identity of each builtin call. Operations are no longer rescanned, and a user method sharing a builtin name is not mistaken for one. | | 5.19.62 | Internal | Running a target with host access parses the IR payload once and shares that single decoded program between host effect routing and execution. | | 5.19.61 | Internal | Target products that run without a host and their canonical exports use scoped worker threads that read the IR payload, arguments, standard input, and adapter facts straight from the caller instead of receiving copies. | | 5.19.60 | Internal | The Stage 1 and Stage 2 compiler runtimes run on scoped threads that read the request bytes in place, so a fact round no longer copies the whole request. | | 5.19.59 | Internal | Fact rounds in the Stage 2 compiler read the cached prepared program in place and copy only the program handle, so wrapper identifiers are no longer duplicated. | | 5.19.58 | Internal | The self-compilation product is fixed once it is built and is read only through accessors, so readers no longer re-hash the same fields and raw response bytes are not kept alive. | | 5.19.57 | Internal | The self-compilation product no longer stores the language mode as its own text. Invocation identities and product manifests take the language version from the lowering request through one shared path. | | 5.19.56 | Internal | Schema fields carried on each self-compilation product were dropped. The encoder writes the canonical schema constants directly, and incoming values are still validated on the way in. | | 5.19.55 | Internal | Fallback fields were removed from the self-compilation product because the self-hosted compiler never falls back to a target compiler. Manifests and provenance records still state the fallback policy as an explicit constant. | | 5.19.54 | Internal | Status and fact round counts live in one place inside the self-compilation product, and manifests and other readers reach them through the same accessors. | | 5.19.53 | Internal | Generated Rust is held in a single place inside the self-compilation product and handed out by reference, which removes the checks that compared the product against its own duplicate copy. | | 5.19.52 | Internal | The duplicate top level copy of the frontend JSON was removed from the self-compilation product, leaving one authoritative place where it is stored. | | 5.19.51 | Internal | Typed results reuse the frontend document that was already parsed, so compiler previews from the command line and self-hosted product assembly no longer parse the frontend JSON again. | | 5.19.50 | Internal | Verified Stage 1 responses and frontend documents are kept on the finished result, so lowering and product assembly reuse them instead of parsing the serialized JSON again. | | 5.19.49 | Internal | Token, resolver, typed product, and Stage 1 control paths decode frontend responses through one shared field inventory and one JSON and schema reader, and byte inputs pass the same UTF-8 validation everywhere. | | 5.19.48 | Internal | Typed products decode Stage 1 responses with the same reader the lowered and generated routes use, covering UTF-8, JSON, root fields, and the exchange schema. | | 5.19.47 | Internal | Every route that drives the compiler advances a fact round the same way, covering query order, empty rounds, mounting, already answered queries, and fact supply. | | 5.19.46 | Internal | Stage 2 keeps re-encoding its fixed point record as the canonical way observations are written, and the public Stage 1 fact supply moved onto the shared response documents and the common fact round progression, so payloads are not copied. | | 5.19.45 | Internal | Admission of a generated compiler image checks its schema, fixed point status, fallback policy, and producer lineage together, recording whether the image came from the bootstrap compiler or from Stage 1. | | 5.19.44 | Internal | Manifest bytes, labels, and program images for both generated compilers are held in one inventory, so each stage manages its assets the same way. | | 5.19.43 | Internal | Schema, numeric, and digest resolution for both generated compiler manifests moved into one typed compiler image descriptor. | | 5.19.42 | Internal | Stage 1 and Stage 2 product encoders admit their producers by the same rules and share how the compiler file list, the runtime template, and target source fields are written. | | 5.19.41 | Internal | Runtime template names and their digests come from one place in the self-hosted frontend, and the encoder no longer takes a digest passed in by its caller. | | 5.19.40 | Internal | Stage 2 manifests and observations are written straight from the verified product, reading the language version and the generated Rust in place, so no intermediate result is rebuilt and the frontend JSON, responses, and sources are not copied. | | 5.19.39 | Internal | All self-hosted target runtimes, with or without a host, receive the admitted product, the IR payload, and adapter facts through one runtime input, and the standalone IR entry points that nothing used were removed. | | 5.19.38 | Internal | The self-hosted Rust facade takes the explicit `main` and the list of host callable exports from the entry module facts it already holds, instead of scanning the lowered program again to rebuild the same list. | | 5.19.37 | Internal | Adapter facts are computed once and reused by entry exports and the Rust facade on the self-hosted Web and service routes, and the Python target receives those facts together with the IR JSON as one runtime input. | | 5.19.36 | Internal | Structure, provenance, and completion status of an admitted self-compilation product are checked by one authority that target adapters and runtime payloads both use. | | 5.19.35 | Internal | Verifying the structure and provenance of a self-compilation product no longer goes through manifest encoding, so execution and observation stop building manifests they immediately throw away and the encoder stops parsing its own output. | | 5.19.34 | Internal | Typed, lowered, and generated runs agree on state, diagnostics, and output through one shared check, and the language mode, target source set, invocation, and result identities are derived from one explicit record. | | 5.19.33 | Internal | A target source set gets its identity from its fields read in canonical order without building intermediate string arrays, and module order, source contents, duplicate identities, and the single entry rule are checked where the sources are produced. | | 5.19.32 | Internal | Reading a final generated response is a separate step with its own input record, covering completion status, the unsupported list, generated Rust budgets, provenance, and how the result is assembled. | | 5.19.31 | Internal | A generated response decodes straight into a lowered result in one step, because building that result is no longer tangled with driving fact rounds. Requests are not re-encoded, no synthetic invocation is made, and the response is not copied. | | 5.19.30 | Internal | The lowered and generated Stage 1 routes share the same response fields, the same UTF-8, JSON, object, and schema decoding, and the same status and query envelopes, and JSON objects are handed over by reference so large arrays are not copied. | | 5.19.29 | Internal | The Stage 0 lowered route and the generated Stage 1 route move through the same state transitions for fact requests, handling empty, out of order, duplicate, and already answered queries alike, and record provenance from the same source. | | 5.19.28 | Internal | The Stage 1 exchange decoder inside Stage 0 was split into separate parts for modules, operation and string arrays, the unsupported list and status agreement, and provenance and source set identities. | | 5.19.27 | Internal | An unused public record was dropped from the self-hosted Stage 1 exchange, and the type imports that nested provenance fields still need were kept. | | 5.19.26 | Internal | The closed integer preview in the self-hosted emitter keeps one map from operation identifier to operation instead of a parallel array and index map. Selection rules and what the preview shows are unchanged. | | 5.19.25 | Internal | Spread and named calls in the generated runtime unpack the spread value into an array once and pass that same value to every call branch. A default parameter that cannot be filled in reports the standard missing parameter fault, and receiver checks in the interpreter follow one classification. | | 5.19.24 | Internal | Builtins used as values in generated Rust take arguments in the shared exact shapes, which removes duplicated arity pre-checks and wrapper layers. `ByteBuffer` and Zstd optional arguments convert per shape, and a mismatch returns a structured runtime fault. | | 5.19.23 | Internal | The interpreter and the generated runtime share one set of exact argument conversions. Higher order and namespace builtins take zero to three arguments directly, and `ByteBuffer` and Zstd optional arguments convert per shape, returning a structured runtime fault when a default is missing. | | 5.19.22 | Internal | Builtin member and host resource calls take zero to four arguments in invocation order and check the count while taking ownership in one step. `round` and `div` on `Decimal` convert their optional arguments for every supported shape, so dispatch has no reachable crash points. | | 5.19.21 | Internal | `Test` and Lispex calls check the argument count and take ownership in one step, and arity errors from Stage 1 callbacks and ISO date parsing are reported more precisely. The Rust and Python backends agree on entry module values, receiver and protocol methods, and lookups. | | 5.19.20 | Internal | Unicode identifier tables are generated as typed range records, so the lookup that decides whether a character may appear in an identifier is checked by type. The end of input marker is no longer counted as a single byte character. | | 5.19.19 | Internal | Closing tokens that the self-hosted lexer inserts while recovering from an error are marked as synthetic and keep that origin through the layout stream. Layout state indexes directly where the length is guaranteed instead of hiding a mistake behind a fallback. | | 5.19.18 | Internal | A triple quoted string nested inside a single line interpolation no longer escapes the newline rule. Both lexers close the nested string and interpolation at the first newline and report `TPZ0003` at the position where the outer string starts. | | 5.19.17 | Internal | Exported values, records, enums, and newtypes on the 5.20 module surface carry their full nominal shape as a canonical identity, so selected and namespace imports check members from that metadata without pulling unselected names into scope. | | 5.19.16 | Internal | Calling a builtin as a value checks its argument count against one table, so exact, optional, and variadic calls report the same fault. Reading an exported top level binding that is not yet set returns the standard unbound fault in generated Rust instead of a placeholder value. | | 5.19.15 | Internal | Both compilers trust the identity the resolver established, so a user record or function named like a member of `Test` or `FS` no longer triggers builtin namespace rules. Preview targets other than `ast`, `resolved`, and `typed` and unadmitted language modes are rejected before the source is read. | | 5.19.14 | Internal | Nominal records, enums, and newtypes in 5.20 keep public name, declaration identity, and receiver method identity apart across construction, equality, ordering, patterns, and type guards. Typed JSON schemas resolve aliases in the declaring module, so imported types behave the same on every backend. | | 5.19.13 | Internal | Language versions and keywords come from a single declaration that produces their order, spelling, and exact parsing, and the corpus selector uses that parser with an explicit 5.7 ceiling. `topaz run` keeps the frozen 5.1 single file route and the 5.2 and later module route on their own semantics. | | 5.19.12 | Internal | The public source snapshot materializes the exact tree of one commit with no exclusions and records commit, tree, and blob identities, executable modes, byte lengths, and SHA-256 digests in a manifest kept outside the payload. The root `LICENSE`, `NOTICE`, and `THIRD-PARTY-NOTICES.md` are verified. | | 5.19.11 | Internal | Topaz is published under Apache-2.0, with a `LICENSE` and `NOTICE` at the repository root. All 40 Topaz owned Cargo packages and 4 Node packages declare that license, the licenses of the 165 bundled Rust packages sit in a separate inventory, and every path in the tree is classified. | | 5.19.10 | Internal | Member completion in the editor offers `stringify`, `parseAs`, `decode`, and `parse` on the `JSON` namespace, which the previous list left out. Both checkers read one catalog of builtin namespaces, so callable static members and `RoundingMode` values are accepted with matching diagnostics. | | 5.19.9 | Internal | An alias of a callable carries its generic arity, parameter names, and defaults. Both compilers report the same diagnostics for integer division, remainder, negation, arithmetic overflow, and top level initialization order, and a statement that runs immediately rejects a binding defined later. | | 5.19.8 | Internal | Both compilers agree on return type inference when the return type is omitted, including `Result`, `Option`, empty array joins, and recursion, and on generic call binding, `using File` initialization, plain and `??=` and arithmetic assignment, and the expected type inside `if` and `match`. | | 5.19.7 | Internal | Both compilers agree on labeled and value producing loop control across functions, lambdas, and comprehensions, and on pattern coverage for wildcard, binding, literal, typed, constructor, list, record, and or-patterns, with impossible pattern diagnostics and closed domain `match` exhaustiveness. | | 5.19.6 | Internal | The Lispex interpreter covers all 205 capability rows from source, from generated Rust and Python, on the Web, and from an installed Topaz product. The same program matches on status, standard output bytes, diagnostics, warnings, exit status, values, and resource outcome across all five routes. | | 5.19.5 | Internal | The higher order Lispex procedures `all?`, `any?`, `filter`, `fold-left`, `fold-right`, `for-each`, `map`, `reduce`, `string-for-each`, `string-map`, `vector-for-each`, and `vector-map` are complete, which brings the interpreter to all 205 capability rows with none unsupported. | | 5.19.4 | Internal | Thirty six Lispex character, string, and output capabilities landed, taking support from 157 to 193 of 205 rows. Full string case conversion handles Greek final sigma and length changing mappings, and `display`, `write`, `newline`, and `println` interleave in the right order. | | 5.19.3 | Internal | Thirty nine Lispex list and search capabilities landed, the 28 composed `c...r` accessors plus `assoc`, `assq`, `assv`, `list-copy`, `list-first`, `list-ref`, `list-rest`, `list-tail`, `make-list`, `memv`, and `nth`. Support moves from 118 to 157 of 205 rows. | | 5.19.2 | Internal | Thirty four numeric Lispex capabilities landed, covering integer division variants, exact and inexact conversion, rounding, `gcd` and `lcm`, exponentiation, exact integer square root, and numeric predicates. Support moves from 84 to 118 of 205 rows. | | 5.19.1 | Internal | The self-hosted frontend was corrected in name resolution, Unicode identifiers, contextual typing, captures, and diagnostics. Stage 2 and the Playground WASM build generate once before their checker runs, and product descriptions and shared terminology were aligned in English, Korean, and Russian. | | 5.19.0 | Public | Published the `topaz/lispex-decision-application/2` contract with prepare-once/evaluate-many execution, application quotas, cancellation, concurrency isolation, consumer artifacts, and source-free native products. | ### 5.18 Established reusable Lispex decision rules in locked packages, with resource quotas and consumer artifacts that can be stored, verified, and replayed. The installed toolchain also gained an MCP server and a run tool that denies host capabilities. | Patch | Visibility | User-visible result | | ---------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 5.18.1 | Public | Published first-class Lispex decision applications, where a locked package binds named rules to fixed limits and quotas, and moved the MCP server into the installed toolchain with a fresh no-capability run tool. | | 5.18.0 | Public | Published the first-class Lispex decision-application profile. A locked package prepares a named rule once and evaluates many canonical values through the interpreter or an admitted native release target. Consumer-produced, unauthenticated artifacts and portable cores support storage, inspection, verification, and fresh-instance replay. | ### 5.17 Added the embedded Lispex evaluator with fixed resource limits and explicit errors, then developed reusable decision applications with quotas, cancellation, isolated concurrent calls, portable consumer artifacts, and native delivery. | Patch | Visibility | User-visible result | | ---------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 5.17.19 | Internal | Independently rederived the exact 5.18 activation inventory from the accepted sealed-corpus tree. The record-pinned inventory remains byte-exact. A canonical successor delta adds the newly discovered `topaz_lispex_h1_oracle` repository-inheritance surface, and a repository-relative Node walker discovers all current surfaces without an external ripgrep dependency. The read-only rehearsal constructed 83 candidate overlay files, retained 13 rebuild boundaries, passed 33 negative controls, and left the repository unchanged. No 5.18 activation occurred. Boundary T18-CP, T18-V. | | 5.17.18 | Internal | Fixed the sealed-corpus transport and Linux-preflight binding. A GitHub-hosted sealer validates the exact producer records, uploads one private one-day digest-closed artifact, then deletes all three raw artifacts. The candidate preflight has no sealed-payload access. One hosted consumer hashes the payload as opaque bytes, binds the unchanged Linux result, and can sign the binding through the fixed Sigstore authority. Repository access controls protect the sealed payload flow. Boundary T18-H2I. | | 5.17.17 | Internal | Repaired the dormant reusable-workflow authority defect before first invocation. Both backend-consuming jobs now select the literal retained H2H backend commit instead of caller-relative `github.workflow_sha`, while a successor authority pins the repaired workflow commit without rewriting the historical record. Boundary T18-H2H. | | 5.17.16 | Internal | Fixed the fresh-seed and candidate-blind producer infrastructure without invoking it. An immutable producer root generates exactly forty-eight direct-WASM cases across twelve fixed restricted-profile families from a post-freeze 256-bit seed. The isolated reusable workflow keeps candidate identity, repositories, network, ambient event state, and prior results outside the producer, then adds candidate identity only in a separate outer binder. Synthetic determinism and binder controls passed. Boundary T18-H2H. | | 5.17.15 | Internal | Fixed the candidate-freeze and remote-containment authority without invoking it. A fourteen-role denominator classifies every safe regular Git blob exactly once. The immutable trusted generator derives canonical role manifests and the complete closure from Git objects, the isolated hosted caller treats candidate bytes as inert data, and the offline verifier independently regenerates the closure before accepting only the pinned H2F signer and checked-in trust root. Ordinary provider branch movement is not an input. Nineteen focused source, Git, workflow, and verifier controls passed. Boundary T18-H2G. | | 5.17.14 | Internal | Fixed the hosted trusted-builder closure without invoking it. An earlier immutable commit contains the data-only containment processor and exact schemas, policy, Sigstore Public Good root, and signing configuration. Its child contains the reusable `workflow_call`-only GitHub-hosted signer, and the current verifier pins that signer commit, the fixed GitHub CLI release, and the checked-in root. Two deterministic synthetic builds and sixteen focused data, filesystem, Git, workflow, and verifier controls passed. Boundary T18-H2F. | | 5.17.13 | Internal | Fixed the Topaz-owned integration conformance authority over the retained exact component closure. A candidate-blind direct-component oracle, independent Node witness, twelve-row integration denominator, and non-overridable GitHub Actions OIDC and Sigstore policy replace the moving-provider dependency. H2B runs for an explicit owner-authorized versioned offer. This patch records Topaz application-integration conformance. Boundary T18-H2E. | | 5.17.12 | Internal | Rechecked the inputs after the Lispex `origin/develop` branch moved on to a direct repair commit. Its exact 23-path delta preserves an aborted recipient sample attempt and repair lineage. The canonical ledger contains no primary admission, cleanup completion signature, independent external attestation, or private-memory record. All nine required-input searches remain empty across eight retained remote branches. The provider records and unrelated public keys remain byte-identical; the discarded sample key and cleanup key are unrelated to Topaz owner authority. Boundary T18-H2. | | 5.17.11 | Internal | Rechecked the two external inputs after the Lispex `origin/develop` branch moved on. All nine exact input searches remain empty across eight retained remote branches, and the three retained F15 provider records remain byte-identical. The newly added Ed25519 key is bound only to a provider-local recipient-cleanup observation and is not a Topaz owner or verifier trust root. Boundary T18-H2. | | 5.17.10 | Internal | Added a candidate-neutral intake protocol for the two missing external inputs. Future provider and trust-root offers must stay outside the Topaz worktree and bind exact repository origins, commits, Git objects, byte counts, digests, roles, provider content keys, and distinct owner and verifier materials. The checker never executes offered code and can report only structural eligibility, not admission. One disposable synthetic pair passed and twenty-nine focused negative controls rejected malformed, aliased, mutable, candidate-controlled, wrong-scope, and secret-bearing inputs. No real provider module or trust root was received. Boundary T18-H2. | | 5.17.9 | Internal | Rechecked whether the two missing external inputs had arrived, against the Lispex `origin/develop` branch nine commits after the retained handoff, with all 12,250 tracked files in scope. The required self-contained provider semantic and tariff module and non-candidate-controlled owner and verifier trust root both remain unavailable. A repeatable watcher now verifies the retained bytes and can replay the exact provider Git evidence. Boundary T18-H2. | | 5.17.8 | Internal | Added a fresh candidate-only bounded-application user contract in English, Korean, and Russian. It pins all 17 maintained sample files, derives only the 5.18 manifest and root lock hash, and prepares 24 synchronized guide, discovery, status, release-note, and rollback operations. Seven unsupported routes stay explicit. Two isolated stagings were byte-identical and twenty-six negative controls passed. The full-profile evaluator is described as supplied and privately retained but not product-executable or admitted. | | 5.17.7 | Internal | Added synthetic-only opaque preservation for one future bounded-application admission bundle. An acyclic pack binds the exact bytes of the current single 45-role bundle, and an isolated forward-only release-manifest v5 member is permanently candidate-ineligible. The current v4 release scripts and workflow remain byte-unchanged. Two bundle generations, two pack assemblies, two atomic stagings, and three preserved copies matched exactly while forty-one negative controls passed. | | 5.17.6 | Reserved | Reserved for the separately versioned production held-out successor. It remains unissued because the exact self-contained provider semantic and tariff module and the non-candidate-controlled owner or verifier trust root are not available. Work may continue only on independent preparation patches. | | 5.17.5 | Internal | Added hermetic, synthetic-only infrastructure for the future fresh bounded-application admission. A closed 45-role inventory covers candidate and profile roots, the exact provider component, application evidence, five native verification suites and assets, future held-out roots, and governance. The generator hashes actual fixture bytes, emits only noncandidate records, and is checked by clean independent recomputation and fail-closed negative controls. It cannot accept a production candidate, trust root, or admission request. | | 5.17.4 | Internal | Added schema-validated, synthetic-only infrastructure for a future candidate-relative held-out suite. It covers candidate and suite closure shapes, restricted producer capabilities, a relational sealed-corpus lifecycle, generic comparison, and separate observation and disposition records. This first format cannot run a production suite and all external production paths reject. A separately versioned successor must bind self-contained provider semantics and tariffs plus a trust root the candidate cannot choose. No production candidate, seed, corpus, result, or reveal was created. Supported product behavior and the public release did not change. | | 5.17.3 | Internal | Added the dormant 5.18 profile as a known but unselectable exact inheritance of 5.17, bound the first-class Lispex application exclusively to the future 5.18 package and standard-library pair, and kept every current 5.17 first-class application route fail-closed. Generated the dormant authority, fixed the MCP reference-disposition policy, completed the atomic activation inventory, and passed a read-only activation rehearsal. This patch changed no supported product behavior. | | 5.17.2 | Internal | Fixed the 5.18 bounded-application release boundary and fresh-admission order, placed complete-profile integration at 5.19 and compiler-quality work at 5.20, and separated HTTP service and MCP from the required integration targets. Also synchronized the no-capability host's Lispex operation with its explicit deny policy. This patch changed no supported application behavior. | | 5.17.1 | Internal | Added application-wide quotas, queue backpressure, preemptive cancellation, concurrent-call isolation, and cleanup around reusable Lispex evaluation. Independently matched the bounded provider artifact and portable-core contract without product admission. Completed a profile-gated application API through the interpreter and self-hosted frontend while unsupported targets fail closed. Added checked consumer-artifact storage, unauthenticated portable-core inspection and verification, and byte-identical fresh-instance replay. Passed a maintained two-module application through normal npm installation, source and compiler removal, relocation, and a local native verification suite. Prevented localized page titles from breaking inside words. Corrected the separately versioned MCP reference boundary. | | 5.17.0 | Public | Added the installed Lispex evaluator under its own command namespace with an exact embedded component, fixed resource limits, explicit errors, and direct execution. | ### 5.16 Made the self-hosted compiler the default on supported routes and supplied MCP and LSP editor tools. Developed execution isolation and a metered Lispex evaluator for the installed binary. | Patch | Visibility | User-visible result | | ---------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 5.16.9 | Internal | Closed redistribution, revocation, evidence-closure, and release-admission records for the exact component, then prepared the cumulative five-platform 5.17 candidate without changing 5.16 language meaning. | | 5.16.8 | Internal | Qualified a normal checksum-installed, offline, source-free product and proved that decoy evaluators, selectors, repository access, network access, and historical LIT commands cannot replace the admitted component. | | 5.16.7 | Internal | Integrated the evaluator into the normal Topaz binary behind separate fresh prepare and evaluate instances, closed limits, atomic output publication, and explicit semantic, runtime, and admission identities. | | 5.16.6 | Internal | Used candidate-relative cases to align mutation and multiple-value behavior with the unchanged evaluator, then closed the corrected profile. | | 5.16.5 | Internal | Built a modular host–guest contract and a private vertical probe with exact version and digest refusal, no imports, no callbacks, no discovery, and no fallback. | | 5.16.4 | Internal | Classified the first Lispex semantic surface and observation channels, then accepted one exact import-free metered evaluator component without pinning a whole Lispex release. | | 5.16.3 | Internal | Added the no-capability host, target-scoped operating-system sandbox, and installed isolation suite needed before any future execution tool can be considered. | | 5.16.2 | Public tools | Published the three-tool check-only MCP server and the LSP-capable editor update as separately versioned tools, without adding remote execution or bundling a compiler into the editor. | | 5.16.1 | Internal | Separated observed differences from their normative disposition, generated exact evidence closure mechanically, and fixed immutable public-language and bootstrap profiles for later releases. | | 5.16.0 | Public | Published Self-hosted Default. The installed Stage 2 compiler is the default on supported current-mode routes, with explicit Rust recovery and no silent fallback. A separately versioned three-tool check-only MCP server and LSP editor update accompanied it, without remote execution or a bundled editor compiler. | ### 5.15 Developed the Supported Dual Toolchain toward a self-hosted default across 21 current-mode routes, with one prepared compiler image per process and explicit Rust recovery. | Patch | Visibility | User-visible result | | ---------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 5.15.6 | Internal | Produced fresh Stage 1 and Stage 2 observations and a new source-free native product, pinned complete recovery inputs, and fixed inactive 5.16 activation, cumulative release-candidate evidence, evidence reuse, and rollback ownership. | | 5.15.5 | Internal | Closed every observed self-default field blocker, verified one installed two-module default-self Golden Path through source-free native execution, and assigned bounded external risks to exact 5.16 candidate gates. | | 5.15.4 | Internal | Qualified one exact local npm installation for default-self check, selected test, managed Python packaging and source-free execution. Invalid self stays unhealthy and fail-closed while explicit Rust and compatibility remain independent. | | 5.15.3 | Internal | Made the installed Stage 2 compiler the omitted default across all 21 supported current-mode routes, while keeping explicit Rust recovery and deterministic Rust compatibility and recording every selection origin. | | 5.15.2 | Internal | Prepared and validated the exact immutable self-compiler image once per process, shared it across compiler and LSP workers, rejected corruption or identity drift, and met the bounded cold and LSP resource floor without caching target results. | | 5.15.1 | Internal | Preserved compiler intent until the effective package language mode is known, so older-mode packages select Rust compatibility deterministically and explicit `self` declines before compiler work. | | 5.15.0 | Public | Published the Supported Dual Toolchain. Rust remains the default and recovery compiler while the explicit current-mode self compiler is supported across its declared routes. | ### 5.14 Extended Stage 2 into a selectable Rust/self toolchain for command-line workflows, application builds, and editor support, with compiler provenance in managed artifacts and independent Rust recovery. The public verifier does not currently build a new compiler artifact from R1 under the declared `rust_build(R1)` step and execute that artifact to produce R2. We therefore do not claim a Stage-2 fixed point. See the the Stage-2 claim correction record. | Patch | Visibility | User-visible result | | ---------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | 5.14.8 | Internal | Closed dual-engine release readiness with source-free Golden Path, compiler/standard-library, LIT, image rendering, and isolated Lispex canaries, plus a complete support disposition and release-candidate gate map. | | 5.14.7 | Internal | Qualified one exact local npm product for repository-free Rust/self builds and source-free execution, independent Rust recovery after self-compiler image damage, bounded compiler/LSP resources, and product size. | | 5.14.6 | Internal | Added process-stable Rust/self selection to formatting, package documentation, every declared LSP request, and the official VS Code extension, with shared presentation and no compiler fallback. | | 5.14.5 | Internal | Added self-compiler-derived target facts and products for Python, Web, worker, Web application, HTTP service, and Web/service development loops without a Rust target-front-end fallback. | | 5.14.4 | Internal | Separated the target-only fixed-point IR runtime from compiler images, added self Rust emit and source-free native build, and sealed exact compiler provenance in managed artifacts. | | 5.14.3 | Internal | Added dual-compiler parse, AST, check, profile, export, run, test, bench, locked-package, and compiler-observation routes with exact bounded outcome and diagnostic agreement and no fallback. | | 5.14.2 | Internal | Added a complete versioned self-compilation product with product-owned profile diagnostics, deterministic source/result identities, generated Rust, provenance, and a fail-closed mechanical adapter. | | 5.14.1 | Internal | Added explicit `rust` and `self` compiler selection, a validated machine-readable support inventory, fail-closed preflight, and the first current-mode selected-entry check through the installed self compiler. | | 5.14.0 | Public | Published the Stage 2 Fixed Point. A separately executed self-hosted compiler reproduces canonical semantics and raw generated source while Rust Stage 0 remains default and recovery. | ### 5.13 Advanced the Stage 1 preview to a separately executed Stage 2 producer, with semantic and generated-source comparisons, producer provenance, installed workflows that do not require the compiler repository, and Rust Stage 0 recovery. | Patch | Visibility | User-visible result | | ---------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 5.13.7 | Internal | Closed the smallest Stage 2-bearing Application Loop, LIT, image rendering, and isolated Lispex regressions, and fixed the inactive 5.14 activation inventory and cumulative release-candidate gate map. | | 5.13.6 | Internal | Qualified the exact local npm product for installed target and source-free self-source Stage 2, reconstructed Rust Stage 0 offline with the Stage 2 inputs, and fixed the bounded resource method. | | 5.13.5 | Internal | Added bounded semantic, generated-source, provenance, producer, seed, runtime, missing-fact, and unsupported-version canaries that reject before completed output without fallback. | | 5.13.4 | Internal | Closed the semantic and raw-source fixed-point comparison for the exact compiler source set and positive and diagnostic corpus, kept producer provenance distinct, and tracked native binary comparison separately. | | 5.13.3 | Internal | Added the explicit Stage 2 producer and self-source routes, ran the separately cached compiler image to regenerate source, and rejected cross-stage producer selection without falling back. | | 5.13.2 | Internal | Regenerated the compiler source, bound those exact bytes to a separately validated next-stage manifest and program image, and assigned execution and fixed-point observation to the following patch. | | 5.13.1 | Internal | Separated producer and build provenance from canonical compiler-generated Rust, fixed explicit Stage 1/2 producer identities, and added a raw-byte fixed-point contract while preserving Stage 1 and the Rust Stage 0 default. | | 5.13.0 | Public | Published the explicit Stage 1 Compiler Preview. The generated compiler owns target lowering and Rust emission, records complete provenance, and fails without hidden Rust fallback. | ### 5.12 Extended the installed self-hosted frontend through lowering and Rust emission into a generated Stage 1 compiler. The installed toolchain records compiler provenance and compares compilation results for compiler sources and applications against Stage 0. | Patch | Visibility | User-visible result | | ---------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 5.12.7 | Internal | Closed release readiness with bounded Application Loop, LIT, byte-intensive image rendering, and isolated Lispex provider canaries, plus an exact activation inventory and cumulative candidate gate map. | | 5.12.6 | Internal | Exposed the installed source-free Stage 1 producer with complete provenance, fail-closed validation, independent Stage 0 recovery, and bounded release-mode wall time and memory. | | 5.12.5 | Internal | Separated Rust and generated-compiler semantic, diagnostic, generated-source, product, and provenance comparisons. Fixed per-producer determinism and rejected wrong-producer or damaged-product fallback. | | 5.12.4 | Internal | Built the generated compiler module, compiled an ordinary package through it, and reproduced the exact compiler source-set output byte for byte without replay or a Rust target-front-end fallback. | | 5.12.3 | Internal | Added the deterministic Topaz-authored Rust emitter, pinned its runtime-template registry, rejected target compiler dependencies, and compiled and ran a source-free generated-product canary. | | 5.12.2 | Internal | Completed Topaz-authored lowering for the declared Bootstrap and ordinary-program surface, including exact operation, operand, capture, rejection, and source-set dispositions without fallback. | | 5.12.1 | Internal | Fixed the first Stage 1 exchange, private lowering IR, and provenance contract, then matched Stage 0 operation identities and operand order on the smallest fail-closed Topaz-authored lowering basis. | | 5.12.0 | Public | Published the installed Self Front-end Preview through Typed, with semantic comparison of saved observations, explicit no-fallback behavior, and Rust Stage 0 as default and recovery. | ### 5.11 Developed the bootstrap foundations into a Topaz-written frontend covering Unicode lexing, layout, parsing, module resolution, and type checking. The installed tools compare its observations with Rust Stage 0 without requiring the compiler repository. | Patch | Visibility | User-visible result | | ---------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 5.11.6 | Internal | Completed the exact installed Self Front-end Preview: source-free validation and semantic comparison preserve explicit Rust Stage 0 recovery and fail without hidden fallback, while recovery assets pin the embedded sources. | | 5.11.5 | Internal | Closed the reviewed current-source corpus and complete front-end self-check: admitted and negative programs agree with Rust Stage 0, unsupported sources stay explicitly classified, and fixed wall-time and memory gates pass. | | 5.11.4 | Internal | Extended the installed preview through Topaz-authored static checking, canonical Typed nodes, calls and captures, protocol-bound and binding diagnostics, and exact agreement on focused compiler and locked-application cases. | | 5.11.3 | Internal | Extended the installed preview through logical import closure and name resolution for package, mounted, and standard modules, with exact scopes, bindings, references, exports, diagnostics, bounded source facts, and no Rust fallback. | | 5.11.2 | Internal | Extended the installed preview through the complete current parser, canonical AST, exact parse diagnostics and recovery, with bounded node and depth accounting and no Rust fallback. | | 5.11.1 | Internal | Added an installed Topaz-authored Unicode lexer and deterministic layout preview that emits a canonical token-terminal observation without changing or falling back from Rust Stage 0. | | 5.11.0 | Public | Published Bootstrap Foundations with canonical compiler observations, a deterministic bootstrap profile and workload, phase comparison, and recoverable Rust Stage 0. | ### 5.10 Combined the binary-media workflow with a Stage 0 compiler foundation for canonical frontend and lowering observations, phase comparisons, builds using the installed toolchain, and reproducible recovery. | Patch | Visibility | User-visible result | | ---------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 5.10.6 | Internal | Completed installed source-free observation workflows, exact build-input provenance, deterministic Stage 0 source recovery, and release-manifest v3 rehearsal without public mutation. | | 5.10.5 | Internal | Added installed phase-gated compiler observation comparison and a locked compiler-grade workload with deterministic relocation, bounded differences, and measured resource limits. | | 5.10.4 | Internal | Routed every checked Rust product through one source-free Lowered IR and extended canonical observations through lowering decisions and generated Rust source without changing Python. | | 5.10.3 | Internal | Added one-pass full typed compiler facts, canonical typed observations, and a machine-readable Bootstrap Profile for locked deterministic compiler-kernel packages. | | 5.10.2 | Internal | Added installed canonical front-end observation and offline validation with reusable raw-token, layout-token, AST, scope, declaration, reference, export, diagnostic, and provenance facts. | | 5.10.1 | Internal | Routed current standalone and package checks through the Stage 0 compiler-kernel facade with replayable source facts, deterministic limits, explicit provenance, and unchanged diagnostics. | | 5.10.0 | Public | Published the stock binary-media kernel loop with explainable native selection, exact byte operations, deterministic codec leaves, and maintained image-rendering evidence. | ### 5.9 Developed the HTTP service loop alongside native scalar and byte operations, deterministic compression and error-correction codecs, and checksums for binary-media applications. | Patch | Visibility | User-visible result | | ---------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 5.9.7 | Internal | Hardened the stock binary-media workflow with stable image renderer and proof-worker gates, source-free product checks, and a rehearsed dormant 5.10 activation boundary. | | 5.9.6 | Internal | Added one deterministic RFC 1950 zlib stream around the exact fixed-Huffman DEFLATE bytes, with bounded input and a loud Python target boundary. | | 5.9.5 | Internal | Added unsigned CRC-32/ISO-HDLC as a non-cryptographic integrity checksum shared by direct, Rust, Python, Web, Worker, and Playground routes. | | 5.9.4 | Internal | Added deterministic systematic RS(255,223) protection with exact multi-shard bytes, bounded input, shared Rust/Web execution, and a loud Python target boundary. | | 5.9.3 | Internal | Added deterministic fixed-Huffman raw DEFLATE for binary-media workloads, with exact Web/Rust bytes, bounded search and memory, and a loud Python target boundary. | | 5.9.2 | Internal | Added exact `Bytes` and `ByteBuffer` native hot paths, including checker-proven direct record fields, while preserving boxed fallback, bounds faults, snapshots, and atomic writes. | | 5.9.1 | Internal | Added a deterministic native-lowering report and a fail-closed hybrid path that specializes eligible top-level scalar functions while preserving the boxed application. | | 5.9.0 | Public | Published the HTTP Service Application Loop with explicit inbound authority, finite limits, structured operations, and source-free artifacts. | ### 5.8 Extended local text and durable package data into an HTTP service workflow sharing Data Lens's checked core with the browser application. Added request isolation, cancellation, resource limits, structured logs, and graceful lifecycle handling. | Patch | Visibility | User-visible result | | ---------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 5.8.4 | Internal | Proved the browser and service Data Lens products share one checked core, hardened realistic multilingual workflows, and prepared the next authority and read-only activation boundary. | | 5.8.3 | Internal | Added effective configuration inspection, versioned structured operation logs, graceful lifecycle events, restart evidence, and repeated concurrent multilingual service workflows. | | 5.8.2 | Internal | Hardened slow, malformed, disconnected, oversized, overloaded, faulting, and nonterminating request paths while proving request isolation and capacity recovery. | | 5.8.1 | Internal | Added the managed HTTP/1.1 service target, request-local cancellation and limits, source-free artifacts, and a Data Lens Service sharing the browser product's checked core. | | 5.8.0 | Public | Published local text import/export and bounded package-isolated durable state as the Local Data Application Loop. | ### 5.7 Extended the offline Data Lens and Markdown Live web applications with local text import and export, package-isolated durable state, and reload recovery. Improved multilingual file workflows, repeated operations, focus handling, and rendering performance. | Patch | Visibility | User-visible result | | ---------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | 5.7.4 | Internal | Froze the completed feature set, prepared the next language authority and version surfaces, and proved a read-only atomic activation rehearsal. | | 5.7.3 | Internal | Hardened Data Lens and Markdown Live with realistic multilingual files, repeated operations, exact exports, focus recovery, and a renderer performance repair. | | 5.7.2 | Internal | Added bounded package-isolated durable string state, explicit load/save/delete results, and reload recovery for maintained apps. | | 5.7.1 | Internal | Added capability-gated local UTF-8 file open and deterministic text download, exercised by Data Lens with real CSV and JSON. | | 5.7.0 | Public | Published the installed offline Web Application Loop with Data Lens and Markdown Live. | ### 5.6 Unified language and runtime delivery with managed artifacts and offline native/Python package builds. Added formatting checks, Web Application tooling, `ByteBuffer`, and the Data Lens and Markdown Live applications. | Patch | Visibility | User-visible result | | ---------- | ---------- | ---------------------------------------------------------------------------------------------------------------------- | | 5.6.8 | Internal | Shipped the maintained Data Lens and Markdown Live test products, repaired their reproduced checker/host blockers, and prepared the atomic 5.7 boundary. | | 5.6.7 | Internal | Added the checked Web Application lifecycle, safe browser host, web-app scaffold, loopback development command, and complete managed static bundle. | | 5.6.6 | Internal | Added fixed-length mutable `ByteBuffer` and repaired Web allocation and returned-memory ownership for large binary products. | | 5.6.5 | Public | Completed the installed Application Loop, `fmt --check`, localized guides, direct npm publication, and bounded field validation. | | 5.6.4 | Public | Added the installed multi-module Application Loop foundation, offline native/Python package builds, and machine-readable `agent-pack` checking. | | 5.6.3 | Public | Removed the private repository address from public npm metadata. | | 5.6.2 | Public | Refreshed documentation and npm presentation without changing behavior. | | 5.6.1 | Public | Moved build work into disposable storage, bounded Python cache growth, and added managed artifact identity, licensing, and package guarantees. | | 5.6.0 | Public | Unified mature language, runtime, package, WASM Playground, and distribution surfaces. | ### 5.5 Established Python as a supported backend and aligned its control flow, cleanup, JSON, containers, and higher-order operations with the interpreter and Rust, with source-mapped code generation diagnostics. | Patch | Visibility | User-visible result | | ---------- | ---------- | ------------------------------------------------------------------------------------------------------- | | 5.5.8 | Public | Aligned floating-point identity and key behavior for `Map`, `Set`, and related container operations. | | 5.5.7 | Public | Closed `Option`, `Result`, unit-value, and higher-order operation gaps in generated Python. | | 5.5.6 | Public | Made unsupported Python shapes fail through complete, source-mapped diagnostics instead of incomplete output. | | 5.5.5 | Public | Added bounded JSON server-boundary behavior and exact cross-backend number, escape, duplicate-key, and error handling. | | 5.5.4 | Public | Aligned Python cleanup and fault behavior so deferred work does not drain after an unrecoverable fault. | | 5.5.3 | Internal | Made every backend reject top-level `return` arms consistently while preserving function-local returns. | | 5.5.2 | Internal | Aligned final top-level `if` and `match` values across the interpreter, generated Rust, and generated Python. | | 5.5.1 | Internal | Added same-kind `Bytes` ordering and stable natural sorting for generated Python arrays. | | 5.5.0 | Public | Promoted Python to a supported backend and expanded interpreter/Rust/Python agreement. | ### 5.4 Standardized platform binaries and installation and added experimental Python generation alongside the interpreter and Rust. | Patch | Visibility | User-visible result | | ---------- | ---------- | ------------------------------------------------------------------------------------------------------- | | 5.4.1 | Public | Added the first experimental Python generation path alongside the existing interpreter and Rust output. | | 5.4.0 | Public | Standardized platform binaries and installation, then opened the Python generation track. | ### 5.3 Expanded the standard library and support for practical programs while keeping language behavior consistent across distribution channels. | Patch | Visibility | User-visible result | | ---------- | ---------- | ---------- | | 5.3.0 | Public | Expanded standard library capabilities and real-program support without splitting language meaning across distribution channels. | ### 5.2 Established one public v5 toolchain around the `import`/`export` module model. | Patch | Visibility | User-visible result | | ---------- | ---------- | ---------- | | 5.2.0 | Public | Locked the `import`/`export` module model and aligned the initial public v5 toolchain. | ### 5.1 Added structured loops and defined how layout, operators, patterns, concurrency, and templates work. | Patch | Visibility | User-visible result | | ---------- | ---------- | ---------- | | 5.1.0 | Public | Added structured loops and clarified layout, operators, patterns, concurrency, and templates. | ### 5.0 Defined the language around expressions, explicit `Result` error handling, and Unicode identifiers. | Patch | Visibility | User-visible result | | ---------- | ---------- | ---------- | | 5.0.0 | Public | Established the expression-oriented, `Result`-first, Unicode-friendly language direction. | ## Current baseline This manual describes the current Topaz product. Use [Toolchain Status](/en/docs/toolchain/status) for the exact installed identity and [Legacy syntax migration](/en/docs/migration/from-pre-v5) only when adapting genuinely old source forms. ## Lispex product and LIT evidence The installed product exposes `topaz lispex embed run` for one closed, evaluator request and `topaz lispex embed info --json` for its exact component and contract identity. The evaluator is fixed in the installed binary. Unsupported profiles and mismatched identities fail explicitly, and the command never discovers, downloads, or falls back to another evaluator. LIT remains separate. Its same-lineage checks exercise one Topaz-written Lispex interpreter across several Topaz routes. They remain useful regression and integration evidence, but they do not establish source independence or whole-language equivalence and do not make Lispex a Topaz backend. ## Related documentation - [Toolchain Status](/en/docs/toolchain/status) - [Classic](/en/docs/project/archive) --- route: https://topaz.ooo/v5.20/en/docs/toolchain/overview markdown: https://topaz.ooo/docs/toolchain/overview.md covers: §0, §21 # Toolchain Overview This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. The Topaz toolchain provides a single, verified path from source files and packages to a runnable product. You do not need to understand parser or backend internals to use it. ## The ordinary loop Use commands in this order: 1. `fmt --check` confirms that committed source code follows canonical formatting. 2. `check` resolves modules and reports syntax, name, and type diagnostics without executing the program. 3. `test` executes verified test entries on the deterministic test host. 4. `run` executes a command-line application directly through the interpreter. 5. `build` creates the single product target intended for delivery. For a locked package, the complete command sequence is: ```bash topaz fmt --check --root my-app topaz check --root my-app --locked topaz test --root my-app --locked topaz run --root my-app --locked topaz build --root my-app --locked --out-dir product ``` Web application and HTTP packages use `topaz dev` for observable loopback development instead of `topaz run`, then use the same package-mode `build`. ## Editor support The official Topaz extension is available from both the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=studiohaze.topaz) and the [Open VSX Registry](https://open-vsx.org/extension/studiohaze/topaz). Use the Marketplace listing for VS Code and the Open VSX listing for VSCodium or another compatible editor. Both stores provide the same official extension. It recognizes `.tpz` files, adds the Topaz file icon, highlights language syntax, configures comments and brackets, provides basic snippets, and starts the installed `topaz lsp` server for editor diagnostics. The default `topaz.compiler` setting follows the compiler policy of the installed Topaz product. Choose `rust` only for explicit comparison or recovery, or `self` to request the installed self-hosted compiler explicitly. The extension does not bundle a compiler. If it cannot find the configured Topaz executable, static highlighting and editing support remain available while the editor reports the missing executable. Use `topaz.executablePath` when Topaz is not on `PATH`. ## What success changes `fmt --check`, `check`, `test`, and `run` do not create a deployment product. `build` writes a managed target directory and records its files in `topaz-artifact.json`. Package commands read `topaz.toml`. `--locked` also requires `topaz.lock` to agree with the manifest and local dependency bytes. A non-zero exit code means the requested step did not complete. Fix the first Topaz diagnostic and repeat that step. If an external compiler is missing, choose a target whose prerequisites are installed or install the required toolchain. If a managed output directory has drifted, preserve it and use a new empty directory. ## Choose the next page by task - For command flags, diagnostics, exit behavior, and recovery, use [CLI & Diagnostics](/en/docs/toolchain/cli). - For package roots, locks, local dependencies, and vendoring, use [Packages & Distribution](/en/docs/toolchain/packages). - For native, Python, Web, and HTTP product contents and execution without the Topaz package source, use [Artifacts & Deployment](/en/docs/toolchain/artifacts-deployment). - For deciding among interpreter and build targets, use [Interpreter & Backends](/en/docs/toolchain/backends). --- route: https://topaz.ooo/v5.20/en/docs/toolchain/cli markdown: https://topaz.ooo/docs/toolchain/cli.md covers: §0, §13a, §17, §21 # CLI & Diagnostics This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. The CLI applies identical parsing, module resolution, and static checks to a single file or a package. The executed command determines downstream processing after this shared gate. ## Format before checking Use `fmt --check` in review and automation. It reports every file whose canonical formatting differs, exits nonzero, and writes nothing. ```bash topaz fmt --check --root my-app topaz fmt --root my-app ``` Run the second command to apply formatting, then repeat the first. Package mode skips vendored dependencies and build output. ## Check without running ```bash topaz check main.tpz topaz check --root my-app --locked topaz check --format json main.tpz ``` A clean check exits zero. Diagnostics include the source location, a stable `TPZ####` code, a summary, and relevant detail. JSON mode emits stable machine-readable diagnostics. No user code runs and no deployment artifact is written. ```bash topaz explain TPZ5021 topaz explain TPZ5021 --json ``` Fix the first diagnostic and rerun `check`. Later errors can be consequences of the first one. `explain` expands a known code without modifying source code. ## Check one usage profile `check` accepts `--profile` and applies the executable constraints of the selected usage profile. An ordinary check and a profile check answer different acceptance questions. ```bash topaz check --profile agent-pack main.tpz topaz check --profile test-profile tests/plan.tpz ``` `agent-pack` accepts only the constructs that belong in ordinary application code. It rejects the canonical free `assert` builtin (including first-class references), the `std.test` import, `Test.*` members, and `>>` function composition, treating them as test-only or specification-level forms rather than production code. A resolved user declaration named `assert` remains ordinary application code; profile policy follows identity rather than spelling. `test-profile` allows the canonical free `assert(...)` and rejects the same composition and test-framework surface. The Bootstrap Profile for compiler-kernel source is described in [Toolchain Status](/en/docs/toolchain/status). Human diagnostics carry the stable `TPZ5801` code and a rule note such as `agent-pack/no-composition`. With `--format json`, stderr holds one `topaz.profile-diagnostic/v1` object per diagnostic and stdout holds one `topaz.profile-check/v1` summary object. `--profile` belongs to `check` alone. It requires canonical current-mode Topaz and does not combine with `--exports-json`. ## Test and run ```bash topaz test --root my-app --locked topaz test tests/summary.tpz --root my-app --locked topaz run --root my-app --locked topaz run main.tpz -- arg1 arg2 ``` `test` uses the checked deterministic test host. A failed assertion, static diagnostic, or runtime fault exits nonzero. Selecting a test file with `--root` keeps the package manifest, lock file, modules, and dependencies. `run` executes a checked command entry through the interpreter. Arguments after `--` are passed to `main`. Web Application and HTTP packages use `topaz dev` for loopback development because their observable product is a browser app or service rather than a command entry. ## Build one delivery target ```bash topaz build main.tpz --out-dir native-product topaz build --target python --root my-app --locked --out-dir python-product topaz build --root my-app --locked --release --out-dir product ``` `build` performs checks first, then writes the requested managed product. Native, Web, and HTTP builds require their external Rust target. Python products require Python only when executed. Static errors halt execution prior to artifact creation. External tool errors are reported after the primary Topaz stage and still exit nonzero. If the output directory contains a different target, a malformed artifact manifest, or changed managed bytes, Topaz refuses to guess ownership. Keep the directory for inspection and select a new empty `--out-dir`. ## Package and machine boundaries Omit the entry file and pass `--root` to use the entry point defined in `topaz.toml`. `--locked` requires current manifest and local dependency contents to match `topaz.lock`. Program arguments always follow `--`. `--unchecked` is a compiler-investigation escape hatch, not a normal application command. It removes the static gate and must not be used to make a failed checked build appear successful. ## Related documentation - [Toolchain Overview](/en/docs/toolchain/overview) - [Packages & Distribution](/en/docs/toolchain/packages) - [Toolchain Status](/en/docs/toolchain/status) --- route: https://topaz.ooo/v5.20/en/docs/toolchain/backends markdown: https://topaz.ooo/docs/toolchain/backends.md covers: §0, §21 # Interpreter & Backends This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. Topaz features a single checked source language with multiple execution and delivery paths. Select the path for the environment that will host the finished program. ## Choose by product - Use `topaz run` for direct interpreter execution during development. - Use native `build` to produce a platform-specific executable. - Use `--target python` to generate a bundle that runs without `.tpz` source or the Topaz CLI on Python 3.11 or newer. - Use `--target web` when an existing ES-module host calls selected Topaz exports. - Use `--target web-worker` when that host requires generated Worker transport. - Use package target `web-app` when Topaz manages the browser lifecycle and UI. - Use package target `http-service` for an HTTP/1.1 handler with declared request and response budgets, overload behavior, loopback binding, and graceful shutdown. Raw Web and Worker are WASM targets. The managed Web Application includes the same checked Web facade inside a complete static product. ```bash topaz run main.tpz topaz build main.tpz --out-dir native-product topaz build --target python --root my-app --locked --out-dir python-product topaz build main.tpz --target web --out-dir web-product topaz build main.tpz --target web-worker --out-dir worker-product ``` `web-app` and `http-service` are package-only targets selected by `[build].target` in `topaz.toml`. ## Target and lowering are different choices `--target` selects the product. `--backend native` provides an optional lowering strategy inside Rust-based products. It first attempts eligible scalar work and the `Bytes` or `ByteBuffer` operations supported by native lowering. Other shapes use the general boxed path. Python and Web products retain their selected targets. When lowering decisions require inspection, a checked native `emit` or `build` can write `--native-report-json `. The report records which functions used native, hybrid, or boxed lowering without changing product bytes. ## Fail closed at the target boundary Targets share parsing, module resolution, and static checking. Each target defines its host operations explicitly. If a selected target cannot preserve an operation, code generation reports a source-located error and rejects before artifact output. For example, deterministic fixed-Huffman DEFLATE, fixed zlib, and RS(255,223) protection are available through the interpreter, generated Rust, Raw Web, Worker, and Playground, but generated Python rejects those operations before artifact creation. `Hash.crc32` is available on Python as well. Concurrent task ordering is intentionally unspecified. A program must not rely on a backend selecting a particular interleaving. ## Rust, Python, and LIT boundaries Generated Rust and Python are delivery artifacts, not extended Topaz syntax or stable APIs for manual editing. Their dedicated pages describe prerequisites, runtime files, and explicit exceptions symmetrically. LIT is a Lispex test and integration surface. It exercises a single Topaz-written interpreter source through direct execution, generated Rust, generated Python, and Web routes. LIT remains separate from shipped output backends, and its records identify the exact program, routes, inputs, and observed results. ## Related documentation - [Rust Backend](/en/docs/interop/rust) - [Python Backend](/en/docs/interop/python) - [Lispex Evaluator and LIT](/en/docs/interop/lit) - [Artifacts & Deployment](/en/docs/toolchain/artifacts-deployment) --- route: https://topaz.ooo/v5.20/en/docs/toolchain/artifacts-deployment markdown: https://topaz.ooo/docs/toolchain/artifacts-deployment.md covers: §17, §21 # Artifacts & Deployment This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. `emit` exposes replaceable generated source code for inspection or integration. `build` creates a managed delivery product. Deploy the `build` directory, not temporary compiler storage or selected generated files copied out of context. ## Build products ```bash topaz build main.tpz --release --out-dir native-product topaz build --target python --root my-app --locked --out-dir python-product topaz build main.tpz --target web --out-dir web-product topaz build main.tpz --target web-worker --out-dir worker-product topaz build --root my-app --locked --release --out-dir package-product ``` The final command reads `[build].target` from `topaz.toml`. Targets `web-app` and `http-service` are package-only. The `topaz.lock` file belongs to authoring and qualification. The finished managed product records its identity in `topaz-artifact.json`. ## Files common to every product Every successful build produces these files: ```text output LICENSE NOTICE GENERATED-OUTPUT-NOTICE.txt topaz-artifact.json ``` The artifact manifest records target, toolchain, and language identity, runtime requirements, invocation details, and each managed file's path, size, hash, and executable requirement. It is written last. ## Target-specific trees Native builds add one platform-specific file: ```text output target/debug/program[.exe] target/release/program[.exe] ``` Only the selected profile exists. On Windows, the filename ends in `.exe`. Python builds add: ```text output program.py topaz_py_rt.py ``` Raw Web builds add: ```text output topaz-web.js topaz-web.d.ts topaz-web.wasm ``` Web Worker builds add the Raw Web files plus: ```text output topaz-web-worker.js topaz-web-worker-client.js ``` A managed Web Application adds the Raw Web files plus: ```text output index.html topaz-app.js topaz-web-capabilities.json styles/app.css ``` Declared styles and assets keep their normalized relative paths, so the last line varies with the package. A HTTP service adds: ```text output target/release/program[.exe] topaz-service-config.json THIRD-PARTY-NOTICES.txt ``` This example captures a release build. A debug build uses `target/debug`. On Windows, `[.exe]` is present. ## Run without the Topaz package source Copy the complete product directory to a fresh location. Then run from its root: ```bash ./target/release/program python3 program.py ``` Use the command that belongs to the chosen native, HTTP, or Python product. Python requires version 3.11 or newer. Native and HTTP executables are specific to the platform where they were built. Serve a managed Web Application directory over HTTP and open `index.html`. Raw Web and Worker products require an ES-module host that imports the generated JavaScript API. None of the Web products should rely on `file://` module loading. Native and Python package applications resolve declared filesystem capability roots from the process working directory. Copy application `data/` and create writable output directories beside the managed product when the manifest requires them. Package data is not added automatically. Web styles and assets are different. Declared `[web]` inputs are copied into the managed Web product. ## Output-directory safety and recovery Rebuilding the same target first verifies existing managed bytes. Topaz rejects a target change, malformed manifest, changed managed file, symlink, or collision with a user-owned path. Files not listed in the artifact manifest are preserved. Do not erase an ambiguous directory to force a build. Keep it for inspection and choose a new empty `--out-dir`. If the old product must be restored, restore the complete known-good product rather than editing hashes or deleting individual managed files. ## Related documentation - [First Application](/en/docs/learn/first-application) - [Packages & Distribution](/en/docs/toolchain/packages) - [Interpreter & Backends](/en/docs/toolchain/backends) --- route: https://topaz.ooo/v5.20/en/docs/toolchain/wasm-playground markdown: https://topaz.ooo/docs/toolchain/wasm-playground.md covers: §0, §21 # WASM & Playground This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. Topaz uses WebAssembly in products with different ownership models and trust boundaries. Choose the product path before selecting a command. ## 1. Playground: first browser experiment The web Playground is an interactive authoring workspace for editing, checking, and running Topaz source without installing the CLI. The browser loads the compiler and interpreter WebAssembly inside a Worker. Build deployed managed applications with `topaz build --target web-app`; their product profile declares browser capabilities. Start with [First Program](/en/docs/getting-started) when you need the same experiment from an installed CLI. ## 2. Raw Web: an existing host calls Topaz Choose Raw Web when an existing JavaScript ES-module host owns the UI and calls selected exported Topaz functions. ```bash topaz build --target web main.tpz --release --out-dir web-product ``` The product contains `topaz-web.js`, `topaz-web.d.ts`, `topaz-web.wasm`, and the common artifact files. Import `instantiateTopaz` from the generated module and serve the directory over HTTP. The host must follow the generated typed ABI. ## 3. Worker: keep the same ABI off the main thread Choose Worker when the existing host also needs generated request/response transport around the Raw Web product. ```bash topaz build main.tpz --target web-worker --release --out-dir worker-product ``` This product adds `topaz-web-worker.js` and `topaz-web-worker-client.js`. This does not create a UI or grant extra capabilities. The JavaScript host still owns both. ## 4. Managed Web Application: Topaz owns the UI Choose `web-app` when Topaz owns the model, messages, update logic, view, and declared browser capabilities. ```bash topaz init --target web-app --root hello-web topaz lock --root hello-web topaz check --root hello-web --locked topaz test hello-web/tests/app.tpz --root hello-web --locked topaz dev --root hello-web --port 8000 topaz build --root hello-web --locked --release --out-dir web-app-product ``` This package-only target creates a complete static product with `index.html`, the checked `topaz-app.js` host, Raw Web files, capability metadata, and declared styles or assets. `topaz dev` is loopback-only and is not a production server. ## Requirements and recovery Raw Web, Worker, and managed Web builds require Rust with `wasm32-unknown-unknown`. The finished browser product does not require the Topaz CLI or Rust. Serve it over HTTP. Loading ES modules and WASM directly through `file://` is not a supported path. If the browser reports a host/runtime identity mismatch, deploy the complete directory from a single build instead of mixing old JavaScript with new WASM. If a managed output directory has changed, preserve it and build into a new empty directory. ## Related documentation - [First Program](/en/docs/getting-started) - [Application Loop](/en/docs/guides/application-loop) - [Artifacts & Deployment](/en/docs/toolchain/artifacts-deployment) --- route: https://topaz.ooo/v5.20/en/docs/toolchain/packages markdown: https://topaz.ooo/docs/toolchain/packages.md covers: §17, §21 # Packages & Distribution This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. A package provides Topaz modules with a single root, entry point, dependency graph, and build target. It introduces no new syntax and does not relax module visibility or cycle rules. ## 1. Create the package root ```bash topaz init --root my-app ``` The root contains `topaz.toml` and `src/main.tpz`. The manifest specifies the package name, language version, entry point, default build target, and dependencies. Commands executed with `--root my-app` without specifying an entry point use the entry point from that manifest. Web Application and HTTP scaffolds are selected upon creation: ```bash topaz init --target web-app --root my-web-app topaz init --target http-service --root my-service ``` ## 2. Lock a package without registry dependencies ```bash topaz lock --root my-app topaz fmt --check --root my-app topaz check --root my-app --locked topaz test --root my-app --locked topaz run --root my-app --locked ``` `topaz.lock` records the root manifest and resolved local dependency content. `--locked` aborts execution if the manifest, lockfile, or local bytes differ. Update the manifest intentionally, run `lock` again, review the diff, and then restore `--locked`. ## 3. Add local or registry dependencies A path dependency is added with its relative path and content identity: ```bash topaz add utility --path ../utility --root my-app topaz lock --root my-app ``` A registry dependency must exist in a local registry input before it can be locked: ```bash topaz add utility@1.2.0 --root my-app topaz vendor --root my-app --from ./registry topaz check --root my-app --locked ``` `vendor` verifies the selected package, copies it to `vendor/utility/1.2.0`, and writes the lockfile. Running `lock` before a registry package has a recorded hash or vendored content fails instead of contacting a network registry. `fetch` accepts the same explicit local `--from` boundary. Neither command performs ambient network resolution. ## 4. Build from local inputs ```bash topaz build --root my-app --locked --release --out-dir product ``` Package resolution uses only the root, path dependencies, locked vendored bytes, and bundled toolchain inputs. Native and Web compilation also invokes Cargo in its locked offline mode internally. No additional Topaz flag is required. The finished managed product is the distribution unit. It does not require `topaz.toml`, `topaz.lock`, `vendor`, package source, or the Topaz CLI at runtime. Copy the complete product directory and any application data required by declared filesystem capabilities. ## Recover without weakening the lock - If `--locked` reports manifest drift, decide whether the manifest change is intended, regenerate the lock, and review the diff. - If a registry package is missing, obtain the explicit local registry input and run `vendor` or `fetch`. Do not remove `--locked`. - If vendored content has changed, restore the recorded bytes or intentionally vendor and review the updated dependency. - If build output has drifted, use a new empty output directory rather than deleting unknown files. ## Related documentation - [First Application](/en/docs/learn/first-application) - [Modules & Visibility](/en/docs/language/modules-visibility) - [Artifacts & Deployment](/en/docs/toolchain/artifacts-deployment) --- route: https://topaz.ooo/v5.20/en/docs/toolchain/status markdown: https://topaz.ooo/docs/toolchain/status.md covers: §0, §21, §22 # Toolchain Status This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. **Topaz 5.20.2 is the current product.** The current language mode is `topaz-5.20`. The source of 5.20.2 is public at topaz.ooo/dl. Official releases are published on topaz.ooo and npm. Confirm an installation with: ```bash topaz version --verbose ``` The command reports the compiler, language mode, runtime, and Rust backend identity. Include its complete output when reporting an installation problem. ## What works today - The parser accepts every example published in this manual and on the landing page, and the site verifier executes them before the site is published. - The module resolver builds multi-file units. - The interpreter runs every program shown on the landing page. - The Rust backend emits Rust and builds native programs. - The Python backend emits Python together with its runtime support file. - User enums are nominal closed sums, and `match` over them is checked for complete coverage. ## Choose the compiler Topaz uses Rust Stage 0 as the default for every compiler-bearing command. The installed Stage 2 self compiler remains available when explicitly selected with `--compiler self` on its admitted current-mode routes: ```bash topaz check app/main.tpz topaz check app/main.tpz --compiler self topaz test --root app --locked --compiler self topaz emit app/main.tpz --compiler self topaz emit app/main.tpz --target python --compiler self --out-dir python-source topaz build app/main.tpz --compiler self --out-dir app-product topaz build app/main.tpz --target web --compiler self --out-dir web-product topaz dev --root web-app --locked --compiler self topaz fmt app/main.tpz --compiler self topaz lsp --compiler self topaz doc --root app --locked --compiler self --out-dir api-docs topaz compiler observe app/main.tpz --compiler self --out-dir observation topaz check app/main.tpz --compiler rust topaz compiler status --json ``` An omitted selection means that when `--compiler` is absent, Topaz runs Rust Stage 0 by default. Explicit self specifies `--compiler self` to invoke the installed Stage 2 self compiler on its admitted current-mode routes. Execution proceeds under the chosen compiler, with no automatic retry or silent fallback. The explicit self selection covers `parse`, `dump-ast`, `check`, current profiles, export reports, `run`, `test`, `bench`, locked packages, `compiler observe`, checked Rust and Python emit, native and Web-family builds, Web/service `dev`, formatting, LSP, and package documentation. One LSP process retains one compiler choice; restart the editor session after changing it. Add `--verbose` to a compiler-bearing command to print whether the resolved selection was explicit, the current default, or compatibility. Compiler intent is preserved until the effective language mode is known. For a package command, the CLI reads and validates only the root `topaz.toml` before choosing the compiler. Dependency resolution, lock verification, source loading, caches, and output work occur later. A package that selects an older language mode uses Rust when the selector is omitted or explicitly `rust`, while explicit `self` fails before that later work. This ordering keeps current-mode default selection and declared Rust compatibility as separate, observable decisions. The installed process validates and decodes the exact immutable self-hosted compiler image once, then shares that prepared program across self compiler and LSP worker threads. Its cache key includes the image, source set, schema, runtime template, and toolchain identities. A mismatch stops self work. Target source, diagnostics, generated source, and managed outputs are calculated fresh for each request and are never reused as prepared compiler state. This preparation remains within documented cold-start, LSP, and memory limits. `compiler status --json` is the machine-readable source of truth for the installed producer, program-image and source-set identities, supported routes, declines, each route's omitted choice, default, compatibility and recovery choices, and no-fallback policy. Managed artifacts also record whether the selection was explicit, the current default, or compatibility. ## Installed recovery boundary In the installed product, package checks, tests, builds, and source-free runs use the Rust Stage 0 default. Explicit self remains separately selectable by passing `--compiler self` on admitted current-mode routes. Rebuilding a managed output with Rust and self replaces compiler provenance atomically. The same installed default completes the two-module learning application through check, its selected test, native release packaging, source removal, and execution. The field ledger closes only resolved product problems. Broader compiler and integration campaigns remain release-candidate checks. The installed Stage 1 and Stage 2 paths can each produce a fresh validated observation from new source. An explicit self selection can then build and run a new source-free native product. The exact npm installation carries both compiler choices in one executable and does not require a repository checkout. Products built with either choice run after their Topaz source is removed. If the installed self-hosted compiler image is missing or damaged, a `self` request fails without retrying or reusing a Rust result. Rerun the original command with `--compiler rust` only when you explicitly choose recovery. Rust remains usable independently, and the published Stage 0 recovery kit is a separate offline reconstruction path rather than an automatic fallback. The installed self compiler manages a versioned, complete current-mode compilation product: ordered modules, tokens and AST, resolution and exports, typed profile diagnostics, lowering and runtime requirements, generated Rust, and exact invocation/result provenance. The host adapter validates and decodes those facts. It does not recreate a missing compiler decision. Shared package and runtime hosts consume the same validated product for admitted command routes. Self-hosted Rust emit retains the fixed-point IR table and adds only the ordinary application host facade. A target-only shared runtime executes that table. It contains no compiler image and cannot run a Rust parser, resolver, checker, lowerer, or emitter. Native artifact manifests record the selected compiler, producer, compiler and target source sets, compilation product, generated-source digest, and `targetCompilerFallback: false`. ## Available product paths - The CLI formats, checks, tests, runs, emits, and builds files or packages. - Native build creates a platform-specific executable. - Python build creates `program.py` with `topaz_py_rt.py` and requires Python 3.11 or newer at runtime. - Raw Web and Web Worker create WASM packages for an existing JavaScript host. - Web Application creates a complete managed static browser product. - The HTTP service creates a managed native HTTP/1.1 process with fixed listener, request, queue, deadline, log, and shutdown settings. - Package commands use manifests, locks, path dependencies, and verified local vendored dependencies. - `topaz lispex embed run` evaluates one Lispex request with declared resource limits using the evaluator built into the installed binary. `topaz lispex embed info --json` reports its component, profile, contract, runtime, and no-fallback identity. - The browser Playground provides check and interpreter execution without installing the native CLI. ## Run the Lispex evaluator The product takes four named regular files or paths. The output directory must not exist: ```bash topaz lispex embed run \ --source rule.lspx \ --input value.lpxvalue \ --limits limits.json \ --output lispex-result ``` A complete evaluation writes `result.lpxvalue` and `report.json`. Deterministic semantic failure or limit exhaustion writes only `report.json`. Request refusal, contract failure, or engine failure leaves no output directory. Provisional result and transcript bytes never cross the boundary. The evaluator, profile, runtime policy, and admission are fixed by the installed product. Evaluator and profile selectors, sidecar downloads, callbacks, imports, and fallback are absent. The report records Topaz execution and admission; the application artifact API produces portable Lispex-format cores. See [Lispex Evaluator and LIT](/en/docs/interop/lit) for the complete distinction. Topaz exposes the locked `std.lispex` complete-current-profile API through the interpreter and all five native release targets. Follow [Run Lispex Rules](/en/docs/guides/lispex-application) for the manifest, lock, build, artifact, and replay workflow. `generated-python`, `raw-web`, `worker-web`, `managed-web`, `http-service`, `no-capability`, and `mcp-empty-component-set` routes reject before artifact output or execution. Fallback is absent. ## Inspect compiler observations ### Capture an installed compiler observation The installed CLI can capture the current checked compiler pipeline through generated Rust source as one canonical managed bundle: ```bash topaz compiler observe --root my-app --locked --out-dir compiler-observation topaz compiler validate compiler-observation ``` Put the output outside `my-app`. Otherwise a later observation can see its own managed directory while collecting package-directory facts. The bundle contains exact source bytes plus source-set, raw and layout token, AST, resolution, structured type, call, closure-capture, source-free lowering, runtime-leaf, generated-Rust, diagnostic, request, response, and provenance records. Treat it with the same confidentiality as the source package. ### Validate the observation bundle `validate` is read-only. It checks canonical encoding, schemas, ordering, cross-references, completeness, sizes, and digests without recompiling or reading the original source tree. Provenance identifies the producer as Rust Stage 0. That fact describes this observation path. It does not identify the implementation language of other compiler stages. ### Understand the comparison boundary The comparison boundary keeps semantic observations, diagnostics, generated source, generated-product behavior, and provenance separate. Rust Stage 0 and the Stage 1 Preview agree on the declared multi-module and rejected diagnostic cases. For the declared comparison product, the linked and interpreted Stage 1 paths produce identical generated Rust, while Stage 0 and Stage 1 generated Rust differ. The compiled Stage 1 product prints exactly `42`. A wrong producer identity or damaged managed Stage 1 product is rejected instead of retrying the target through Stage 0. ### Compare without a repository clone The installed package can perform the exact comparison without a repository clone: ```bash topaz compiler observe main.tpz --terminal rust-source --out-dir rust-observation topaz compiler preview main.tpz --producer stage1 --terminal rust-source --out-dir stage1-observation topaz compiler validate rust-observation topaz compiler validate stage1-observation topaz compiler compare --layer semantic rust-observation stage1-observation ``` After the two managed observations are written, validation and comparison no longer read the original target source. If Stage 1 declines or faults, it fails without writing a replacement observation and prints the separate `compiler observe` recovery command. It never runs that command implicitly. Use `--compiler rust` on an ordinary `check` or build command when you explicitly want Rust Stage 0. ### Choose a comparison boundary Compare two complete observations by selecting the boundary that answers your question: ```bash topaz compiler compare --layer semantic observation-a observation-b topaz compiler compare --layer generated-source observation-a observation-b topaz compiler compare --layer provenance observation-a observation-b topaz compiler compare --layer native-binary program-a program-b ``` The command prints one canonical JSON record within the documented size limit and returns a failing exit status when that layer differs. Semantic comparison stops at the first different compiler phase. Generated source and provenance remain independent, so a producer identity change does not by itself become a language mismatch. ## Decision-relevant limits All targets share parsing, module resolution, and static checking, but host-dependent operations are not universally available. A target that cannot preserve an operation must reject it before writing a product. In particular, generated Python declines a program before writing an artifact when it uses the fixed-Huffman DEFLATE, fixed zlib, or RS(255,223) helpers, the `toFloat` and `toIntRadix` conversions, the Regex methods `find` and `findAll`, or any `Date`, `BigInt`, or `Decimal` member. The decline reports `TPZ6PY0001` with the source position, and the [Python Backend](/en/docs/interop/python) page lists the exact members. Consult the backend pages before choosing a target for binary media, calendar dates, or arbitrary-precision numbers. The HTTP product implements managed HTTP/1.1 with fixed listener, request, queue, deadline, log, and shutdown controls. TLS, HTTP/2, WebSocket, outgoing network access, ambient environment authority, shared mutable Topaz state, and a general Web framework are outside this product. The Web Application host uses declared browser capabilities. Raw Web and Worker products require an existing JavaScript host. Playground runs source in the browser; validate deployment with the generated product outside the source workspace. Concurrent task ordering is unspecified. Join tasks before ordered output. The installed evaluator handles `topaz lispex embed`, while Topaz target selection is separate and `--target lispex` rejects before artifact output. LIT is the Lispex test and integration surface from one source lineage and remains separate from the installed evaluator and output backends. ## Recommended workflow Start with `fmt --check`, `check`, and `test`. Use `run` for a command application or `dev` for a Web Application or HTTP service. Build only the target you intend to deliver and copy its complete managed directory. ## Related documentation - [Interpreter & Backends](/en/docs/toolchain/backends) - [Language and Compatibility](/en/docs/project/conformance) - [History](/en/docs/project/history) --- route: https://topaz.ooo/v5.20/en/docs/interop/overview markdown: https://topaz.ooo/docs/interop/overview.md covers: §0, §17, §20, §21 # Interop Boundaries This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. Interop is a build and product boundary, not a second language inside Topaz. Keep application logic in checked `.tpz` modules, select the product that fits the deployment environment, and review any surrounding host code separately. ## Choose the boundary | Need | Choose | What crosses the boundary | | ---------------------------------------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------- | | One executable for the build machine's OS and architecture | [Rust Backend](/en/docs/interop/rust) | A managed native product built through Cargo | | A product for an environment with Python 3.11 or newer | [Python Backend](/en/docs/interop/python) | Generated `program.py` with its bundled runtime | | A browser or Worker product | [WASM & Playground](/en/docs/toolchain/wasm-playground) | A managed Web artifact and its declared ABI | | A custom Rust, Python, or service wrapper | Host integration | Generated product plus separately owned host code | | A decision rule that runs inside your application | [Run Lispex Rules](/en/docs/guides/lispex-application) | Manifest, rules, limits, lock, typed results, and consumer evidence | | One Lispex evaluator request | [Lispex Evaluator and LIT](/en/docs/interop/lit) | Canonical rule, input, limits, result, and report | | Exercise Topaz with the LIT program | [Lispex Evaluator and LIT](/en/docs/interop/lit) | Lispex test and integration surface across four named routes | | Source conversion involving Lena Code | [Lena Code Interop](/en/docs/interop/lena-code) | A separately maintained conversion workflow | The installed evaluator is a Topaz command surface, while output targets use the separate `build --target` surface. LIT exercises the test and integration routes, and Lena Code owns source conversion. Each route is listed by its product boundary. ## What stays Topaz Modules, imports, expressions, types, patterns, and primary diagnostics remain Topaz. There is no `import rust`, inline Rust, Python, or JavaScript expression, implicit ABI, automatic ownership conversion, or automatic host-exception translation. Generated source is an artifact, not permission to place its tokens in a `.tpz` file. ## Verify only shared observables Check and run the Topaz package first. Build the selected target, then run the managed product with the same declared input. Compare the observable relevant to the application—such as stdout, an exit code, an artifact field, or a file result—rather than internal object layout or generated source structure. Treat the result as evidence scoped to that specific program, input, environment, and declared observable. Review the generated product and host integration again when the compiler or deployment target changes. ## Host ownership remains explicit A wrapper around a generated product is foreign code requiring its own dependency, ownership, error, capability, and security review. Importing a Topaz or standard module does not grant filesystem, network, process, environment, clock, or database authority. Unsupported target behavior must fail loudly instead of silently altering the checked program. Continue with [Rust Backend](/en/docs/interop/rust), [Python Backend](/en/docs/interop/python), or [Lispex Evaluator and LIT](/en/docs/interop/lit). --- route: https://topaz.ooo/v5.20/en/docs/interop/rust markdown: https://topaz.ooo/docs/interop/rust.md covers: §17, §20, §21 # Rust Backend This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. Choose the Rust path when the deliverable needs to be a native executable for the operating system and architecture of the build machine. Rust remains a build target. Rust tokens never become Topaz syntax. ## Prerequisites - Complete the `study-plan` package from [First Application](/en/docs/learn/first-application). - Keep its `topaz.toml`, `topaz.lock`, `src/plan.tpz`, `src/main.tpz`, and selected test together. - Install a compatible Rust toolchain with Cargo on the build machine. - Run the following commands from the `study-plan` directory. At runtime, the executable requires neither Topaz, Cargo, nor `.tpz` files. It remains specific to the OS and architecture for which it was built. ## Shared checked package The same two Topaz modules are used on the Python page. First, `src/plan.tpz`: ```topaz export record StudyTask { title: string, minutes: int, done: bool = false, } function parseMinutes(text: string) -> Result { match toInt(text) { case Some(value) if value > 0 => Ok(value) case _ => Err("minutes must be a positive integer") } } export function makeTask( title: string, minuteText: string, done: bool = false, ) -> Result { let minutes = parseMinutes(minuteText)? Ok(StudyTask { title: title, minutes: minutes, done: done }) } export function summarize(tasks: Array) -> string { let mut remaining = 0 for task in tasks { if !task.done { remaining = remaining + task.minutes } } "tasks={tasks.length}, remaining={remaining} minutes" } ``` The package entry `src/main.tpz`: ```topaz import src.plan { StudyTask, makeTask, summarize } export function main(args: Array, stdin: string) -> Result { let tasks: Array = [ makeTask("Run first program", "10", true)?, makeTask("Build application", "25", false)?, ] print(summarize(tasks)) Ok(0) } ``` ## Verify the Topaz source Check the package, run the selected test, and establish the reference output: ```bash topaz check --root . --locked topaz test tests/plan.tpz --root . --locked topaz run --root . --locked ``` The test reports `tests/plan.tpz: test-ok`. The run prints: ```text output tasks=2, remaining=25 minutes ``` ## Emit for inspection, build for delivery Use `emit` only when inspecting or integrating the generated Cargo source set: ```bash topaz emit --target rust --root . --locked --out-dir ../dist/rust-source ``` Use `build` for the managed native product: ```bash topaz build --target native --root . --locked --out-dir ../dist/native ``` `build --target rust` is not a valid command. `rust` designates an `emit` target, while `native` designates the deployable build target. ## Inspect the managed product A default debug build contains: ```text output tree dist/native/ ├── GENERATED-OUTPUT-NOTICE.txt ├── LICENSE ├── NOTICE ├── target │ └── debug │ └── program[.exe] └── topaz-artifact.json ``` `topaz-artifact.json` records the `native` target, OS and architecture runtime requirements, invocation details, and the size and SHA-256 hash of every managed file. Topaz writes the manifest last, rejecting modified managed files, unsafe collisions, or target mismatches in the same output directory. ## Run after the Topaz source is unavailable Because `dist` resides outside the package directory, move the source package aside and run only the product: ```bash cd .. mv study-plan source-unavailable ./dist/native/target/debug/program ``` On Windows, execute `Rename-Item study-plan source-unavailable` and run `.\dist\native\target\debug\program.exe`. The product exits with code 0 and prints the output shown above. ## Target-specific recovery - A missing or incompatible Cargo toolchain indicates an issue on the build machine. Resolve the toolchain installation and rebuild. - Specifying `--release` produces `target/release/program[.exe]`. This flag changes the build profile without altering Topaz semantics. - Default lowering uses the boxed Rust runtime. Optional `--backend native` specializes proven regions while keeping unsupported shapes boxed rather than guessing. - Do not copy a native executable to another OS or architecture and treat it as a supported deployment. ## Exact boundary Generated Rust code and its runtime closure function as replaceable artifacts rather than a handwritten extension API or source-level Rust FFI. Treat a successful execution as evidence of the program's output and exit code on this target. Review any user-written wrapper under its own host-code contract. If the package uses a complete-current-profile Lispex decision application, follow [Run Lispex Rules](/en/docs/guides/lispex-application) before building. All five native release targets carry that application profile; other routes reject before artifact output. Existing Topaz 5.18 packages use the embedded-core compatibility profile. Continue with [Interop Boundaries](/en/docs/interop/overview), [Python Backend](/en/docs/interop/python), [Interpreter & Backends](/en/docs/toolchain/backends), or [Artifacts & Deployment](/en/docs/toolchain/artifacts-deployment). --- route: https://topaz.ooo/v5.20/en/docs/interop/python markdown: https://topaz.ooo/docs/interop/python.md covers: §17, §20, §21 # Python Backend This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. Choose the Python path when the destination environment already provides Python 3.11 or newer and a readable generated product is useful. Python remains a build target. Python constructs never become Topaz syntax. ## Prerequisites - Complete the `study-plan` package from [First Application](/en/docs/learn/first-application). - Keep its `topaz.toml`, `topaz.lock`, `src/plan.tpz`, `src/main.tpz`, and selected test together. - Install Python 3.11 or newer on the destination machine. - Run the following commands from the `study-plan` directory. The product requires neither the Topaz compiler nor `.tpz` files at runtime. It includes generated Python source code and its support module, so it is not a native binary. ## Shared checked package The same two Topaz modules are used on the Rust page. First, `src/plan.tpz`: ```topaz export record StudyTask { title: string, minutes: int, done: bool = false, } function parseMinutes(text: string) -> Result { match toInt(text) { case Some(value) if value > 0 => Ok(value) case _ => Err("minutes must be a positive integer") } } export function makeTask( title: string, minuteText: string, done: bool = false, ) -> Result { let minutes = parseMinutes(minuteText)? Ok(StudyTask { title: title, minutes: minutes, done: done }) } export function summarize(tasks: Array) -> string { let mut remaining = 0 for task in tasks { if !task.done { remaining = remaining + task.minutes } } "tasks={tasks.length}, remaining={remaining} minutes" } ``` The package entry `src/main.tpz`: ```topaz import src.plan { StudyTask, makeTask, summarize } export function main(args: Array, stdin: string) -> Result { let tasks: Array = [ makeTask("Run first program", "10", true)?, makeTask("Build application", "25", false)?, ] print(summarize(tasks)) Ok(0) } ``` ## Verify the Topaz source Check the package, run the selected test, and establish the reference output: ```bash topaz check --root . --locked topaz test tests/plan.tpz --root . --locked topaz run --root . --locked ``` The test reports `tests/plan.tpz: test-ok`. The run prints: ```text output tasks=2, remaining=25 minutes ``` ## Emit for inspection, build for delivery Use `emit` only when you need to inspect or integrate the generated Python source: ```bash topaz emit --target python --root . --locked --out-dir ../dist/python-source ``` Use `build` for the complete managed Python product: ```bash topaz build --target python --root . --locked --out-dir ../dist/python ``` Run `program.py` explicitly after building it. Python builds reject `--run` and `--release`. Neither is a Python-product switch. Rust's `--backend` option is not valid for a Python build either. ## Inspect the managed product The product contains: ```text output tree dist/python/ ├── GENERATED-OUTPUT-NOTICE.txt ├── LICENSE ├── NOTICE ├── program.py ├── topaz-artifact.json └── topaz_py_rt.py ``` `topaz-artifact.json` records target `python`, the Python 3.11-or-newer runtime requirement, invocation details, and the size and SHA-256 hash of every managed file. Topaz writes the manifest last and rejects a modified managed file, an unsafe collision, or a different target in the same output directory. Ship the complete tree. `program.py` and `topaz_py_rt.py` belong together, and the notices, license, and manifest remain part of the managed product. ## Run after the Topaz source is unavailable Because `dist` is located outside the package, move the source package aside and run only the product: ```bash cd .. mv study-plan source-unavailable python3 dist/python/program.py ``` On Windows, use `Rename-Item study-plan source-unavailable`, then run `python dist\python\program.py`. The product exits with code 0 and prints the output shown above. The product reads standard input in full before the program starts. In an interactive terminal, redirect from `/dev/null` or end the input with Ctrl-D so the run does not wait. ## Target-specific recovery The Python backend reports unsupported standard-library operations before writing an artifact and points to the corresponding source location. In particular, `Codec.deflateFixedCompress`, `Codec.zlibFixedCompress`, and `Codec.reedSolomon255223Protect` are unavailable on this target. The same applies to the conversions `toFloat` and `toIntRadix`, to the `find` and `findAll` methods of a compiled `Regex`, and to every member of `Date`, `BigInt`, and `Decimal`. The decline is diagnostic `TPZ6PY0001` and names the file, line, and column. Keep those operations on a supported target or select another implementation before rebuilding. If `program.py` cannot import `topaz_py_rt.py`, restore the complete managed tree instead of copying a single generated file by itself. If Python is too old, install Python 3.11 or newer on the destination. ## Exact boundary Generated Python is a replaceable deployment artifact, not a stable handwritten extension API or native binary. A successful run establishes this program's output and exit code in the selected Python environment. It does not expose arbitrary Python libraries, exceptions, decorators, or package APIs to `.tpz` source code. The Lispex application profile is not a generated Python capability. Packages that reach it are refused before Python artifact output. Use [Run Lispex Rules](/en/docs/guides/lispex-application) to choose the interpreter or an admitted native target. Continue with [Interop Boundaries](/en/docs/interop/overview), [Rust Backend](/en/docs/interop/rust), [Interpreter & Backends](/en/docs/toolchain/backends), or [Artifacts & Deployment](/en/docs/toolchain/artifacts-deployment). --- route: https://topaz.ooo/v5.20/en/docs/interop/lit markdown: https://topaz.ooo/docs/interop/lit.md covers: §0, §20, §21 # Lispex Evaluator and LIT This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. Topaz provides two Lispex surfaces. `topaz lispex embed run` evaluates a single rule and writes its result without package configuration or a build step. LIT (Lispex-in-Topaz) is a Topaz-written interpreter source exercised through direct execution, generated Rust, generated Python, and Web routes as the Lispex test and integration surface. ## What Lispex is Lispex is a purpose-built Lisp for decision rules, designed close to Scheme. One input gives one deterministic answer, and the same rule with the same input always returns the same result. A request arrives as a list of named fields, and every answer, refusals included, comes back as a list. It is a language separate from Topaz, with its own documentation and its own version scheme. The [Lispex](https://www.lispex.com) site has the detail. ## Choose the right path - To embed Lispex rules inside an application, use [Run Lispex Rules](/en/docs/guides/lispex-application). - To evaluate a standalone rule without a package, use `topaz lispex embed run`. - To ship a Topaz application, choose one of the supported Topaz output targets. - To understand LIT, read it as evidence that a substantial Lispex program exercises several parts of the Topaz toolchain together. - To use Lispex as a product, follow the Lispex product documentation. This page describes the Topaz integration boundary. ## Run one request with declared resource limits Prepare three files. These include a standard Lispex rule file, an input in the canonical `lispex.embed-value/v1` encoding, and a limits file that sets every operational ceiling. Then choose an output directory that does not exist yet: ```bash topaz lispex embed run \ --source rule.lspx \ --input value.lpxvalue \ --limits limits.json \ --output lispex-result ``` The limits document follows this exact structure. Lower values are permitted, but fields cannot be omitted or added. ```json { "schema": "topaz.lispex-embed-limits/v1", "prepare": { "raw_source_bytes": 4096, "prepare_work": 1000000, "logical_allocation": 1000000, "syntax_depth": 64 }, "evaluate": { "canonical_input_bytes": 4096, "eval_work": 1000000, "logical_allocation": 1000000, "semantic_frames": 1000, "traversal_depth": 256, "output_bytes": 1000000, "diagnostic_bytes": 1000000, "transcript_bytes": 1000000, "transcript_events": 100, "result_bytes": 1000000 } } ``` On success, `lispex-result` contains `result.lpxvalue` and `report.json`. Deterministic semantic failure or limit exhaustion produces only the report. A rejected request or engine failure produces no output directory. Inspect the fixed component and no-fallback policy with: ```bash topaz lispex embed info --json ``` The command uses its fixed component, profile, runtime, and route behavior. It performs no component discovery, download, callback, or fallback. The report records Topaz execution and admission; the application artifact API produces portable Lispex-format cores. ## What the LIT evidence means LIT is written in Topaz, and a single canonical source lineage is exercised through direct execution, generated Rust, generated Python, and Web routes. This demanding program can expose defects in checking, lowering, runtime behavior, and host boundaries. A passing LIT record identifies the program, routes, inputs, and observed results. Unsupported operations return explicit errors. All four routes share one source lineage and expose checking, lowering, runtime, and host regressions across those routes. ## Product boundary The installed evaluator processes the documented component, profile, file inputs, resource limits, and output contract through `topaz lispex embed`. Topaz output-target selection remains separate: `--target lispex` and `--target lit` reject before artifact output. LIT resides in the repository test and integration system outside the installed package. ## Use Lispex inside a Topaz application A package binds named Lispex rules to the complete-current-profile evaluator, source, resource limits, and embedded component. A locked build prepares each rule once and generates `std.lispex` plus `std.lispex.rules`. Topaz code receives opaque rule and value types rather than raw Wasm handles. Every evaluation receives fresh guest memory, globals, meter, and transcript state, while application-wide quotas, cancellation, and cleanup surround the per-call limits. Use `evaluate` when the typed settlement is sufficient. Use `evaluateWithEvidence` when an eligible deterministic settlement also requires a consumer artifact. `consumerArtifactBytes` preserves it for storage, `consumerArtifactFromBytes` performs checked intake, and `inspectConsumerArtifact` or `verifyConsumerArtifact` reports its stable identity. `portableCoreBytes` extracts the exact Lispex-format core when one is present. `freshReplay` re-evaluates the admitted rule and input with the artifact's limits in a fresh guest instance and accepts only an identical artifact. The core and outer artifact are consumer-produced and unauthenticated. They carry no issuer, provider approval, signing authority, component admission, or permission to perform an external action. Local preflight refusal, cancellation, queue refusal, safety preemption, and engine faults create no portable core. The complete-current-profile application runs in the interpreter and generated native products on all five native release targets. Its installed package covers npm installation, source and compiler removal, relocation, multi-input execution, resource-limit refusal, deadline recovery, artifact verification, and fresh replay. Python, Web, HTTP service, no-capability, and empty-component MCP routes reject before artifact output or execution. The Topaz 5.18 compatibility route remains available through the embedded-core compatibility profile with its own component, profile, contract, and prepared-artifact identity. For the manifest, rule, resource-limit, lock, build, run, artifact, and replay workflow, use [Run Lispex Rules](/en/docs/guides/lispex-application). LIT is the test and integration surface; the guide builds a complete-current-profile Lispex decision application. ## Related documentation - [Interop Boundaries](/en/docs/interop/overview) - [Rust Backend](/en/docs/interop/rust) - [Python Backend](/en/docs/interop/python) --- route: https://topaz.ooo/v5.20/en/docs/interop/lena-code markdown: https://topaz.ooo/docs/interop/lena-code.md covers: §0, §20 # Lena Code Interop This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. Lena Code is an independent source-to-source conversion product. Topaz serves as an input or output language only when Lena Code's published support explicitly includes that exact language pair. The handoff boundary is source code, not a shared runtime or an extension of Topaz grammar. ## Before converting The current language-pair and program-shape list published by [Lena Code](https://lenacode.com) defines its convertible inputs. Programs outside that list use another conversion path. Record the Lena Code version so the conversion can be reproduced. ## When Topaz is the target A generated `.tpz` file is candidate Topaz source code, not certified output. Review its contents and diff, then validate it using the current Topaz toolchain: ```bash topaz parse converted.tpz topaz check converted.tpz topaz run converted.tpz topaz build converted.tpz --out-dir converted-build --run ``` After completing the parse, check, run, and build stages, compare application-relevant outputs, error conditions, data changes, API interactions, effects, and failure handling between the source and converted programs. ## When Topaz is the source When Topaz is the source language, Lena Code defines and manages the mapping to the destination language and host environment. Review destination APIs, data representation, effects, and failure handling as part of the conversion. ## Product and syntax boundary Topaz and Lena Code do not automatically share an ABI, package manager, runtime, or deployment bundle. Foreign-language source code belongs in clearly labeled interop examples and never defines canonical Topaz syntax. If a converter produces a form that current Topaz does not support, `topaz check` rejects it. ## Related documentation - [Interop Boundaries](/en/docs/interop/overview) - [Language and Compatibility](/en/docs/project/conformance) --- route: https://topaz.ooo/v5.20/en/docs/interop/ai-agents markdown: https://topaz.ooo/docs/interop/ai-agents.md covers: §20, §22 # AI Agents and MCP This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. **Outcome:** point a fetch-capable or tool-enabled assistant at this language. **Prerequisite:** none for the first section. The MCP server needs the toolchain installed as described in [First Program](/en/docs/getting-started). ## Start with one fetch For an assistant that can retrieve a URL but has no tools configured, the widest common surface is a single self-contained file. ```text https://topaz.ooo/agents.md ``` It contains the structure of a file, the forms, the host/profile boundary, and the section that matters most for a model familiar with other languages: an explicit list of non-forms. It warns against `Vec`, `async`, `try`, class syntax, and treating `null` as universal absence, while preserving the supported `T | null` data-boundary form. Naming those distinctions prevents confident guesses from becoming invalid source. Several machine surfaces sit beside it. `https://topaz.ooo/llms.txt` is the compact index, while `https://topaz.ooo/llms-full.txt` contains the complete English manual as plain Markdown. Every English page is also available at `https://topaz.ooo/docs/.md`, with its title, source route, and covered specification sections preserved in frontmatter. `https://topaz.ooo/version.json` states which version answers. If you are configuring an assistant for someone else, giving it that one URL completes the setup. ## The problem the tools solve The reference supplies canonical forms. `topaz_check` closes the authoring loop with exact compiler diagnostics, and `topaz_run` returns real execution output. An assistant can write, receive exact source-position diagnostics, and correct its program in the same loop. The reference, checker, and runner connect canonical syntax to observed compiler and runtime results. ## Four published tools | Tool | What it answers | | ---------------- | -------------------------------------------------------------------- | | `topaz_spec` | What is the exact form for this part of the language? | | `topaz_examples` | Show me verified code for this concept. | | `topaz_check` | Is what I just wrote correct, and if not, exactly where? | | `topaz_run` | What does this program return inside the no-capability run boundary? | `topaz_check` returns the compiler's own diagnostics, codes, and source positions without rewriting them. `topaz_run` first performs that check and then starts a fresh worker. Both tools report the exact installed toolchain and compiler selection that answered, so an assistant can correct its output from evidence rather than a paraphrase. `topaz_check` also accepts the optional input `profile: "agent-pack"`. A profiled call evaluates the same installed compiler with the Agent Pack rules, returns schema `topaz.mcp-check-result/v2` with `profile: "agent-pack"`, and identifies each profile denial in the diagnostic's `data.profileRule`. Omitting `profile` preserves the unprofiled `topaz.mcp-check-result/v1` response. ## Execution boundary Each `topaz_run` call starts the hidden worker in the same installed Topaz executable with a fresh no-capability host. It has no filesystem, network, discovery, callback, component, or fallback route. The server admits two active runs, queues none, applies a five-second wall limit and a one MiB response limit, and kills and reaps cancelled workers or workers exceeding those limits. Submitted source is sent through stdin and is neither logged nor retained. This authoring execution boundary is available on every supported native Topaz target inside the installed executable. It uses the no-capability host described above; operating-system isolation is outside this boundary. Submit trusted authoring code. ## Machine-readable descriptor Clients and crawlers can read the server description directly. ```text https://topaz.ooo/mcp.json ``` It lists the four tools, their inputs, the built-in command, resource boundary, and installed self-check values in structured form. ## Current status Topaz 5.18.1 includes the MCP server and worker in the native `topaz` executable. Installing Topaz installs the MCP server. There is no separate archive, checksum, server path, worker path, or admission path. ## Install and connect Install Topaz, then configure this local stdio command. ```json { "command": "topaz", "args": ["mcp", "serve"] } ``` After connecting, the client must discover exactly `topaz_spec`, `topaz_examples`, `topaz_check`, and `topaz_run`. The `verification` block in [`mcp.json`](https://topaz.ooo/mcp.json) provides a known reference section, example concept, clean source check, and pure run with value `42`. These let a client confirm the installed server instead of trusting configuration alone. ## Ready to continue when You can use exact reference material, repair a program from compiler diagnostics, and run pure source through the installed no-capability worker. For what a program can and cannot reach, see [Interop Boundaries](/en/docs/interop/overview). For the forms an assistant will be looking up, see [Syntax at a Glance](/en/docs/language-tour). --- route: https://topaz.ooo/v5.20/en/docs/project/conformance markdown: https://topaz.ooo/docs/project/conformance.md covers: §0, §21 # Language and Compatibility This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. Different parts of the Topaz product answer different questions. Use the language definition to determine what a program means, the checker to determine whether a source file is admitted, and execution evidence to determine what was observed in a given environment. ## Which authority answers which question? | Authority | Direct role | | ------------------------------ | ---------------------------------------------------------------------------------------------------- | | Language specification (SPEC) | Defines canonical syntax, types, semantics, and observable language boundaries | | Checker | Decides whether a source file parses, resolves, type-checks, and is admitted for the selected use | | Interpreter and output targets | Provide direct execution or a generated product; an unsupported route rejects before artifact output | | Canonical examples | Present supported forms and workflows with observable results | | Tests and verification records | Record observed results for named inputs, toolchains, environments, profiles, and resource limits | ## A practical verification path 1. Start from the current language pages and canonical examples. 2. Run `topaz check` to catch syntax, name, type, and selected-profile errors. 3. Run the program and its tests. Record the outputs, diagnostics, file changes, and exit status that matter to the application. 4. Build the intended output target and check its documented limits. A target that cannot preserve required behavior must fail clearly. 5. When reading a test result or record, keep its exact inputs, environment, and limits attached to the claim. The common command sequence is: ```bash topaz check main.tpz topaz run main.tpz topaz build main.tpz --out-dir build ``` ## Comparing compiler evidence Canonical compiler observations compare the named boundary behavior and inputs of two runs. Use `semantic` for the ordered source set through diagnostic and outcome phases, `generated-source` for exact emitted Rust, `provenance` for producer identity, and `native-binary` for exact same-target executable bytes. Each layer records its own compiler output, while a passing workload records the observed results for its named inputs and environment. ## Related documentation - [Toolchain Status](/en/docs/toolchain/status) - [Interpreter & Backends](/en/docs/toolchain/backends) - [Lispex Evaluator and LIT](/en/docs/interop/lit) - [Classic](/en/docs/project/archive) --- route: https://topaz.ooo/v5.20/en/docs/migration/from-pre-v5 markdown: https://topaz.ooo/docs/migration/from-pre-v5.md covers: §20 # Legacy Syntax Migration This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. This is the single migration guide for legacy Topaz source code written prior to `v5`. It is not a step-by-step release upgrade path. Compatible product changes do not require a separate migration process. Record the intended observable behavior first, preserve that behavior while rewriting legacy source code into current canonical grammar, and let the current checker surface all unresolved boundaries. ## 1. Inventory before rewriting Record entry files, module roots, inputs, expected outputs, runtime faults, host side effects, and verified output captures. Separate actual Topaz code from examples styled after Rust, JavaScript, or external frameworks. Do not infer behavior from legacy documentation simply because a syntax pattern looks familiar. Refer to the current manual and canonical examples instead. ## 2. Normalize the recurring forms - Rewrite `mut let` to `let mut`, `[T]` to `Array`, and legacy function-type syntax to `(T) -> U`. - Use `..rest` in list patterns, `...args: T` for variadic parameters, lambdas for anonymous functions, and `self` only where current method declarations permit. - Replace string indexing and `.length` with scalar APIs. Use typed registry templates and `{expr}` string interpolation. - Express recoverable failures in `Result`, missing values in `Option` or nullable types, runtime faults explicitly, and cleanup logic in `defer` or supported `using` statements. - Update module syntax to the current `import` and `export` forms described in [Modules & Visibility](/en/docs/language/modules-visibility). Foreign tokens belong at labeled build or conversion boundaries. ## 3. Keep historical code historical This is legacy syntax and must remain inside a `text` code fence, not a canonical Topaz fence: ```text mut let names: [string] = ["Ada"] let first = names[0] ``` The current syntax makes mutability, collection types, and bounds behavior explicit: ```topaz let mut names: Array = ["Ada"] let first: Option = names.get(0) print("{first}") ``` ```text output Some(Ada) ``` `names.get(0)` returns `Option`, displaying a successful lookup as `Some(Ada)` instead of relying on unchecked string indexing. ## 4. Check behavior, not just syntax ```bash topaz check from-pre-v5.tpz topaz run from-pre-v5.tpz ``` Once the checker succeeds, compare runtime output with the results recorded prior to rewriting. Reverify module initialization, iteration order, scalar string offsets, numeric faults, resource cleanup, and host-profile boundaries. Build the target environments designated for deployment and compare declared outputs where the rewritten source is supported. An unsupported form is rejected with a clear diagnostic before artifact output. ## 5. Know the automation boundary `topaz migrate --from --to ` handles only explicitly supported adjacent `v5` transitions. Across those declared boundaries, the tool checks compatible source code or updates package metadata. It does not translate pre-`v5` source code or infer legacy syntax semantics. Comprehensive migration guides may also include framework APIs or foreign grammar that automated tools cannot validate. Refer to [Classic](/en/docs/project/archive) solely for historical context, and manually adapt code to current language standards. ## Migration exit gate Complete this migration guide once the current checker passes, expected outputs are verified, all selected build targets match declared results, unsupported behavior emits explicit diagnostics, and no legacy syntax remains within canonical Topaz code fences. Proceed to [Syntax at a Glance](/en/docs/language-tour) and follow its references for detailed specifications in the current manual. ## Related documentation - [Syntax at a Glance](/en/docs/language-tour) - [Forbidden & Deferred Forms](/en/docs/language/forbidden-deferred) - [Modules & Visibility](/en/docs/language/modules-visibility) - [Interop Boundaries](/en/docs/interop/overview) --- route: https://topaz.ooo/v5.20/en/docs/project/archive markdown: https://topaz.ooo/docs/project/archive.md covers: §0 # Classic This page describes Topaz v5.20. Before treating it as current, fetch https://topaz.ooo/version.json and compare its version field with 5.20. If it differs, discard the cached page and refetch it through https://topaz.ooo/llms.txt. Topaz Classic organizes long-form guides and examples by topic. Open the collapsed **Classic** group near the bottom of the **Modern** menu to navigate directly to any of its 19 pages. ## Start with a locale - [English Classic](/archive/pre-overhaul-2026-07-12/en/docs/introduction) - [Korean Classic](/archive/pre-overhaul-2026-07-12/ko/docs/introduction) - [Russian Classic](/archive/pre-overhaul-2026-07-12/ru/docs/introduction) The sidebar remains available after opening a Classic page, and the **Classic** group expands automatically. Links inside the page continue through the Classic documentation. ## Keeping the long-form material Classic pages preserve detailed explanations and sequential code examples. Use Modern for the active learning path and language reference, and rely on Classic when historical context provides useful background. Classic documentation is not required to complete the current course. ## Search boundary Classic pages are excluded from current manual search results, but every page remains accessible directly from the sidebar menu. ## When to use it Use Classic when consulting historical tutorials for functions, types, control flow, collections, and practical projects. When applying an example to a new project, start with the [Learning path](/en/docs/learn), then consult [Introducing Topaz](/en/docs/introduction), [Syntax at a Glance](/en/docs/language-tour), [History](/en/docs/project/history), and [Legacy Syntax Migration](/en/docs/migration/from-pre-v5) where needed.