Repository navigation
Releases
Releases · ynishi/htl
0.19.0 - 2026-10-25
Release Notes
Added
- generate a typed async client for a Teal module from
[rust] exports(#538) - htl rs writes one Rust file of #[derive(TealRecord)] structs and unit enums from the Teal records and enums of the modules listed in htl.toml [rust] modules, htl rs --check says when the committed file is stale, and htl-core gains rsgen, the pure lowering behind it
- call a Teal module's functions from async Rust with htl::run (#538)
- keep a function's signature in the type index (#553)
- add htl::teal_types!() macro for build-time Rust record generation
- htl-core gains project::own_sources, which walks the project's own .tl sources and checks each with its type index kept, and verdict::fails_on_errors, so the commands that lower the project's own modules share one walk and one check
Fixed
- resolve package.path runtime behind a model declaration (#530)
- alias_in matches a module's file through TypeIndex::file instead of its first candidate, and ref_table carries each source's aliases so htl rs and htl shapes read through an alias of a module that declares no record or enum
- htl lsp names a file in another place by the URI the client knows it by, not by its symlink-resolved path, so definition, references, workspace symbol, call hierarchy and a diagnostic's relatedInformation no longer open a second buffer for a project opened through a symlink
- keep program-scoped settings per program state, not in the checker's globals (#538)
Other
- describe the Teal-owned boundary in the README and fix stale statements
- narrow README CLI table to one row per subcommand (#541)
- cache lshape-version verdict in the check store (#531)
- route proc-macro checker through project::Scope::new and project::checker
- three tests that fail on macOS for reasons in the tests themselves now pass there or are skipped with a reason: pin_cli's cargo-update test uses /usr/bin/false, htl-std's cwd_is_honoured canonicalizes the expected path, and the non-UTF-8 name test is ignored on macOS
- wait for host call entry before cancelling in executor.rs tests
Install htl-cli 0.19.0
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/ynishi/htl/releases/download/v0.19.0/htl-cli-installer.sh | shInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/ynishi/htl/releases/download/v0.19.0/htl-cli-installer.ps1 | iex"Install prebuilt binaries via Homebrew
brew install ynishi/tap/htl-cliDownload htl-cli 0.19.0
| File | Platform | Checksum |
|---|---|---|
| htl-cli-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| htl-cli-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| htl-cli-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| htl-cli-aarch64-unknown-linux-gnu.tar.xz | ARM64 Linux | checksum |
| htl-cli-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |
0.18.0 - 2026-10-22
Release Notes
Added
- htl check reports lshape-version when the lshape the project supplies is not the one its lshape declaration is written for, or its version cannot be read
- lshape's declaration states the lshape it is written for on its second line, -- lshape: 0.3, and htl::shapes reads it, reads the runtime's _VERSION without running it, and gives the verdict as a lshape-version finding
Install htl-cli 0.18.0
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/ynishi/htl/releases/download/v0.18.0/htl-cli-installer.sh | shInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/ynishi/htl/releases/download/v0.18.0/htl-cli-installer.ps1 | iex"Install prebuilt binaries via Homebrew
brew install ynishi/tap/htl-cliDownload htl-cli 0.18.0
| File | Platform | Checksum |
|---|---|---|
| htl-cli-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| htl-cli-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| htl-cli-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| htl-cli-aarch64-unknown-linux-gnu.tar.xz | ARM64 Linux | checksum |
| htl-cli-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |
0.17.0 - 2026-10-22
Release Notes
Added
- lshape's declaration covers lshape 0.3's whole public surface, partial, discriminated, pattern, ref, assert_dev, reflect and luacats included, checked against the vendored lshape's exports in both directions
- lshape's declaration takes a container of shapes as lshape.AnySchema, so a hand-buil 10BC8 t T.shape({ name = T.string }) type-checks against what htl shapes ships
- htl shapes lowers a record with a where clause that tags it, its tag field written as T.literal, and the index carries the clause as TypeInfo::Record's where_clause: Option
Fixed
- lshape.AnySchema chains any number of modifiers, and T.array_of / T.map_of take one, as every other container of shapes does
Install htl-cli 0.17.0
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/ynishi/htl/releases/download/v0.17.0/htl-cli-installer.sh | shInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/ynishi/htl/releases/download/v0.17.0/htl-cli-installer.ps1 | iex"Install prebuilt binaries via Homebrew
brew install ynishi/tap/htl-cliDownload htl-cli 0.17.0
| File | Platform | Checksum |
|---|---|---|
| htl-cli-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| htl-cli-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| htl-cli-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| htl-cli-aarch64-unknown-linux-gnu.tar.xz | ARM64 Linux | checksum |
| htl-cli-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |
0.16.0 - 2026-10-21
Release Notes
Added
- htl shapes writes a run-time shape and a typed Schema declaration from each record, so a value from outside is narrowed once at the boundary instead of cast at every read
- htl lsp traces call hierarchies, the callers and the callees of a function across the project, from the uses the references index resolves
- htl lsp answers textDocument/references from the uses each file of the project resolves, kept current as the symbol index is, so an agent finds every caller rather than every word
- htl lsp answers the workspace-symbol request from an index of the project's declarations, kept current by checks, watched files and a stat of each file, so an agent finds a declaration by name
- htl lsp answers textDocument/documentSymbol with the declaration tree of an open file, so an agent lists a module's API without searching text
- htl lsp re-checks the open files when htl.toml, the package manifests, a declaration under types/ or the Rust htl dts reads changes, and the README says how an editor starts it
- htl lsp answers hover with the type tl checked and go-to-definition with the declaration, and a require's module with the file htl resolves it to
- htl lsp formats a buffer as htl fmt does, and offers htl fix's fixes as quick fixes and a fix-all action, only for the text the check read
- htl lsp, a language server on stdio whose diagnostics are htl check's, on open and on save, for the open files and the ones that require a saved one
- the bin scaffold names tokio's time beside rt and signal, the timer the deadline of a question the host asks Teal waits on
- TealRecord reads serde's skip, default, skip_serializing_if, transparent and flatten, in the declaration and the conversion from one reading, and htl dts writes the same declaration
- a Teal loop refuses a question with req:reject(reason), and the host reads it as AskError::Rejected, on mlua-isle 0.10
- the type index carries each declaration's type as a structure, so a tool that lowers a record reads tl's type object and not its printed form
- a declaration in the type index says which code is its own function body, so a call is attributed to the function it is written in
- TypeIndex::uses lists every name a checked file uses with the declaration it resolves to and whether it is a call, the reverse go-to-definition is built from
- Htl::declarations_of collects the declarations of a file it does not check, from a parse alone, as the type index's tree without the types
- the type index keeps the declarations a file makes as a tree, with their kind, range, name and type, and typeindex::Decl reads them back
- Htl::check_indexed keeps, beside a check, the type and the declaration tl's report gives each position, and CheckInfo::type_at reads them back
- htl::fix names what htl fix decides about one fix and applies one fix's edits as htl fix does, and Htl::format_text formats a text as htl fmt formats its file
- project::check_file is the per-file check htl check's walk asks, with the checker's answer from its store or from disk
- TealFields::write_fields takes the path read_fields takes, so a serde-crossed field inside a flattened struct or a tag payload fails to write under the name it fails to read under
- every serde word TealRecord refuses sits in one table with its escape, #[teal(serde)] crosses a field through serde under a serde feature, #[teal(skip)] leaves one out, and a derive refusal is one error again
- a data enum declares and crosses the way serde tags it — tag, tag + content, or untagged — and one serde would write externally tagged says so instead of disagreeing with the wire
- TealRecord reads serde's names — rename_all, rename, alias, rename_all_fields — so a field's Teal name and Lua key are the ones the JSON carries, and a serde word it does not read stops the build instead of being ignored
- htl re-exports serde and serde_json, and a TealRecord field may say the Teal type it crosses as with #[teal(as = "..")]
- task.answer(req, f) answers a question with what f returns and rejects it with the error f raised, so a bug in the decision reaches the host as a reason rather than a timeout
- htl::task::Ask asks the Teal loop a typed question with a deadline, and names the three ways it fails, so a host writes the wait and the safe default once rather than per kind
Other
- every command assembles its checker through checker_for, which goes through the library's project::checker over project::Scope::new, so run, build, gen, shapes, resolve and fix resolve a require the way check does
- the embed example's Call is a serde type crossing as serde names it, the README says a serde type is written once, and the dts doc's serde material reads as one section
- examples/embed --ask shows a host asking Teal through Ask, with the kind's deadline and the deny default in one place, and the README says how a host asks
- the type index splits its rows with as_chunks, which the stable toolchain's clippy asks for in place of chunks_exact with a constant size
- the TealRecord derive doc lists the #[teal(..)] words and links to htl::dts for what each serde word does, instead of stating the rules a second time
- an end-to-end case adds one question to a scaffolded bin host and runs it, so the host htl new writes is shown to carry Ask without a change to its template
- std.proc's timed tests bound a prompt return at 3 s, the middle of the gap to the 5 s child they rule out, not the 1 s a loaded machine crosses
Install htl-cli 0.16.0
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/ynishi/htl/releases/download/v0.16.0/htl-cli-installer.sh | shInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/ynishi/htl/releases/download/v0.16.0/htl-cli-installer.ps1 | iex"Install prebuilt binaries via Homebrew
brew install ynishi/tap/htl-cliDownload htl-cli 0.16.0
| File | Platform | Checksum |
|---|---|---|
| htl-cli-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| htl-cli-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| htl-cli-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| htl-cli-aarch64-unknown-linux-gnu.tar.xz | ARM64 Linux | checksum |
| htl-cli-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |
0.15.0 - 2026-10-11
Release Notes
Added
- a scaffolded bin host runs its program on the executor, as htl run does
- a scaffolded host depends on htl-std and installs it, and htl pin moves its line with htl's
- htl run, test, check, resolve and build carry std.fs and std.proc, with no host
- htl-std, htl's asynchronous I/O for Teal, starting with std.fs
- a #[host_module] name may be dotted; the require key is the whole name and the record is its last segment
- a binary registers the libraries it adds to every state htl-core builds
- std.proc.run, a child process run to completion as one await
Other
- the bin scaffold test derives the htl-std line from a release pin as the scaffold writes it
- the scaffold's unit tests expect the executor host and the async feature
- std.* is htl's re-export of Rust libraries, and the I/O slice is htl-std's
Install htl-cli 0.15.0
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/ynishi/htl/releases/download/v0.15.0/htl-cli-installer.sh | shInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/ynishi/htl/releases/download/v0.15.0/htl-cli-installer.ps1 | iex"Install prebuilt binaries via Homebrew
brew install ynishi/tap/htl-cliDownload htl-cli 0.15.0
| File | Platform | Checksum |
|---|---|---|
| htl-cli-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| htl-cli-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| htl-cli-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| htl-cli-aarch64-unknown-linux-gnu.tar.xz | ARM64 Linux | checksum |
| htl-cli-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |
0.14.0 - 2026-10-09
Release Notes
Added
- htl pin <release | main | path:> moves the htl a scaffolded project depends on, and --htl release names the release this CLI is
- htl check --statistics prints how many findings each rule produced, instead of the findings, and the JSON check document carries the same rows
- unmarked-sealed reports a record built and cast only in its own file and carrying no ---@Sealed, and htl adopt's ---@Sealed row counts those records as applicable
- unmarked-struct reports a record built whole at every construction site and carrying no ---@struct, and htl adopt's ---@struct row counts those records as applicable
- htl adopt reports how many declarations carry each of htl's nine markers, and where; the checker's census rides in the cache so an unchanged project replays it
- include_tl! and include_bundle! rewrite the crate's host_module .d.tl declarations before checking, so the order of mod declarations no longer matters
- records = [geom::Point] nests a record declared in another file of the crate, read through its mod declaration
- a uses entry names a module path,
uses = [Mode, task = "htl.task"], so a .d.tl imports another library's types under the name it uses them by - type-guard, a lint whose fix rewrites type(v) == "" on an any or union variable to v is T, the spelling Teal narrows under, and drops the casts the guard made redundant
- a #[host_module] method takes &Lua, filled from the closure's own handle and left out of the .d.tl
Fixed
- await on a literal or a unary expression is refused as "applies to a call", whatever the literal's kind
- htl fmt keeps an await or async keyword written on a line of its own inside its block, and indents the line that completes it
- a statement-level await that opens a block keeps its indentation under htl fmt
- await-missing reads the async keyword at the callee's declaration, not anywhere on its line
- an await inside an if checks: the node walk descends into if_blocks, the plain array Teal keeps an if's branches in
- struct-fields holds a literal only to the fields the record's own body declares, and a nested record's same-named field no longer takes the outer field's ---@optional
- the resolver tests build their scratch directory canonical, so a requirer they never write places under the same roots on macOS
Other
- Merge remote-tracking branch 'origin/main' into feat/unmarked-sealed
- every test temp directory is a TempDir that removes itself when the test passes, and the helpers are named tempdir for what they make
- the checker prelude and the runtime state load one chunk for what both need, instead of each carrying a copy
- Htl::from_lua names with_checker_lua as the route for a state that only runs, and the crate doc shows it on mlua-isle's AsyncIsle
- Htl::new and with_checker_lua say why the state is unsafe_new (debug), not that a safe state refuses bundles
- the channel test orders its sender after events() by a handshake, not a 50 ms sleep
- a_child_left_sleeping_is_dropped_before_the_run_returns checks the drop, not the clock
Install htl-cli 0.14.0
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/ynishi/htl/releases/download/v0.14.0/htl-cli-installer.sh | shInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/ynishi/htl/releases/download/v0.14.0/htl-cli-installer.ps1 | iex"Install prebuilt binaries via Homebrew
brew install ynishi/tap/htl-cliDownload htl-cli 0.14.0
| File | Platform | Checksum |
|---|---|---|
| htl-cli-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| htl-cli-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| htl-cli-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| htl-cli-aarch64-unknown-linux-gnu.tar.xz | ARM64 Linux | checksum |
| htl-cli-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |
0.13.0 - 2026-10-07
Release Notes
Added
- htl.task forwards channel, after, ticker, select and select_raw, typed in task.d.tl, and a task's case taken by select is its one await
- htl::task::RecvChannel / SendChannel / Request, which a #[host_module] declares as htl.task's types in a .d.tl that imports it
- htl-core builds on mlua 0.12.2 and mlua-isle 0.9, whose task library has channels, timers and select
Other
- the README's Tasks section and the htl crate doc name channels, timers, select and the typed host channels
Install htl-cli 0.13.0
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/ynishi/htl/releases/download/v0.13.0/htl-cli-installer.sh | shInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/ynishi/htl/releases/download/v0.13.0/htl-cli-installer.ps1 | iex"Install prebuilt binaries via Homebrew
brew install ynishi/tap/htl-cliDownload htl-cli 0.13.0
| File | Platform | Checksum |
|---|---|---|
| htl-cli-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| htl-cli-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| htl-cli-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| htl-cli-aarch64-unknown-linux-gnu.tar.xz | ARM64 Linux | checksum |
| htl-cli-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |
0.12.0 - 2026-09-30
Release Notes
Added
- htl test --allow-empty passes a run that found no test file, and --format json prints its document whether or not the flag is given
- a cache entry hashes and probes the module's require closure, so an edit two requires away is not replayed over
- a comment-only line of a .tl survives generation at its own line, so the --- doc reaches the Lua htl gen and include_tl! write
Fixed
- the stand-in a Teal require of a declared-only name gets stays out of package.loaded, so a plain Lua pcall(require) after it is still false
- a -- line inside a block comment stays empty under generation, and the comment lines after a .tl's last line of code reach the Lua at their own numbers
Other
- the htl-cli tests patch a scaffolded project the way e2e does, so the window test checks under the packaged gate when the pinned release is not on crates.io yet
Install htl-cli 0.12.0
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/ynishi/htl/releases/download/v0.12.0/htl-cli-installer.sh | shInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/ynishi/htl/releases/download/v0.12.0/htl-cli-installer.ps1 | iex"Install prebuilt binaries via Homebrew
brew install ynishi/tap/htl-cliDownload htl-cli 0.12.0
| File | Platform | Checksum |
|---|---|---|
| htl-cli-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| htl-cli-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| htl-cli-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| htl-cli-aarch64-unknown-linux-gnu.tar.xz | ARM64 Linux | checksum |
| htl-cli-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |
0.11.0 - 2026-09-29
Release Notes
Added
- a check with findings names htl check --explain once per rule before its summary, and the design prose above four rules points at the registry instead of restating it
- htl check --explain prints why a rule exists and how to decide its cases, from an explanation every rule of the registry carries
Fixed
- htl test's store never records or preloads a .d.tl as a module, so a Lua module typed by a declaration beside it survives the second run
Install htl-cli 0.11.0
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/ynishi/htl/releases/download/v0.11.0/htl-cli-installer.sh | shInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/ynishi/htl/releases/download/v0.11.0/htl-cli-installer.ps1 | iex"Install prebuilt binaries via Homebrew
brew install ynishi/tap/htl-cliDownload htl-cli 0.11.0
| File | Platform | Checksum |
|---|---|---|
| htl-cli-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| htl-cli-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| htl-cli-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| htl-cli-aarch64-unknown-linux-gnu.tar.xz | ARM64 Linux | checksum |
| htl-cli-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |
0.10.0 - 2026-09-28
Release Notes
Added
- a require the generator erases is marked in the census and left out of the link, so a bundle that imports a record of types asks the host for nothing
- htl-mq declares its game table as record Game with ---@noyield fields, #[teal(noyield)] on a TealRecord field writes the marker, and the window scaffold binds the table to mq.Game
- #[host_module] and htl dts write ---@noyield for a sync fn's Function parameters, and #[teal(yields)] / #[teal(noyield)] on a parameter override it
- async-as-sync-callback reads ---@noyield on a host's declaration, the parameters and record fields a host calls from C, so a host's own boundary is reported like table.sort's
- async-as-sync-callback, the fifth async rule: an async function handed to table.sort, string.gsub, xpcall's handler or a __tostring, where a C call cannot yield
- the four async lints (await-missing, await-outside-async, await-non-async, task-escape), and ---@async on a host async fn's declaration
- async / await syntax under [lang] async: local async function, await on a call, async local as a child task, on Teal's own lines
- htl.task, the task library a program on the executor spawns and awaits with, preloaded and declared like htl.test
- htl run and htl test run the program as a root on mlua-isle's executor, so a host's async fn is callable from Teal and Ctrl-C cancels with a grace
- one owner for a state's debug hook, so Interrupt, coverage and a host callback register instead of replacing each other
- global-redeclaration reports one global name declared at two sites, and the crate doc says the three ways a host value gets a type
- a declaration is never served, so the chain reaches the module behind it, and nothing is built in the checker's state for a program state to receive
Fixed
- bundle_info counts the bytes the file has, not 72, so the test does not fail with the length of the version string
- a global travels the require chain, so a module served from the store hands its requirer what its own requires would have declared
- a module that declares a global is walked once per run, and its globals are handed to each env that requires it
- require-cycle reads the graph that runs, so a loop of type-only requires the generator erases is not reported
Other
- the README's Async section in one place, and the embed example runs the issue's pair through a host's async fn
Install htl-cli 0.10.0
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/ynishi/htl/releases/download/v0.10.0/htl-cli-installer.sh | shInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/ynishi/htl/releases/download/v0.10.0/htl-cli-installer.ps1 | iex"Install prebuilt binaries via Homebrew
brew install ynishi/tap/htl-cliDownload htl-cli 0.10.0
| File | Platform | Checksum |
|---|---|---|
| htl-cli-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| htl-cli-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| htl-cli-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| htl-cli-aarch64-unknown-linux-gnu.tar.xz | ARM64 Linux | checksum |
| htl-cli-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |