Skip to content

Release notes

These are the release notes for LatchAI, newest first.

The packaged desktop app updates itself. Since 0.7.2, a packaged build checks for an update when it launches and every six hours after that, downloads it in the background, and stages it. When a window is open you get a “LatchAI x.y.z is ready to install” prompt with Restart now and Later; with no window open there is no prompt at all and the update installs on the next quit. Background checks are silent — only a manual Check for Updates… (in the app menu and the tray menu) answers when there is nothing to install. Every updater event is appended to updater.log in the app’s data folder, so a failed update is diagnosable rather than mysterious. Builds before 0.7.2 could download an update but never install it, so a copy older than 0.7.2 has to be replaced once by hand.

LatchAI requires macOS 13 (Ventura) or newer. The floor arrived in 0.9.0, when the desktop shell moved to Electron 44 to get back inside Chromium’s security-support window; Electron 44 does not run on macOS 11 or 12. Releases before 0.9.0 ran on older systems.

(0.9.1 and 0.9.2 were never released — the sequence really does go 0.9.0 → 0.9.3.)

Two new workflow nodes: switch and transform. switch is the n-way branch — route one templated value against an ordered list of cases (equals, contains, startsWith or regex, optionally case-insensitive), first match wins, and each case’s label is what an outgoing edge names, with when: "default" for the nothing-matched branch. Four outcomes no longer mean four chained condition nodes. transform reshapes data with a JSONata expression and no model in the loop — pick fields, rename them, filter a list, add up a column — reading its $ from a templated input and any number of $name bindings, timeboxed at 5 seconds and depth-100 so a runaway expression cannot hang a run.

Chats can be renamed. A conversation still names itself after your opening line, but that name is now a starting point rather than a verdict: rename one from its tab or with /rename, and the name you chose sticks — the vault note’s heading follows it, and no later message quietly takes it back.

A per-node error policy, error edges, and an error workflow. Every node may now declare retry (up to 10 extra attempts, fixed or exponential backoff, each one announced as a node.retry event), timeoutMs (a wall-clock budget per attempt), and onError: fail as before, continue to hand the step’s output on as { ok: false, error, attempts } and let the run finish, or branch to redirect the run down the node’s when: "error" edges and skip every other one. A workflow may also name an errorWorkflow — another graph fired once when this one ends run.failed, with the failed run, node and message as its trigger payload. A run the user stopped never retries, never continues, and rings no alarm.

Four ways to lay out a vault graph. The Brain’s graph lens gains a layout picker beside the search box. Force is the picture as it was. By type gathers each note type into its own region while links still pull related notes across the boundary, so a person sitting at the edge of the people cluster next to the projects is telling you something. Around focus draws rings by hop distance from the note you click (the busiest note until you do), widening a crowded ring so a hub with nineteen neighbours is not a pile. Layered runs the links left to right. Nothing is stored: filters and search still never move a node, and switching layouts reframes the same graph.

A wikilink inside code is an example, not a link. A conventions guide’s [[wikilinks]] in backticks, or the note template inside its fenced block, was indexed as a real link, resolved to nothing, and drawn as a ghost node — a page on the to-write list nobody promised. Fenced blocks and inline code spans are now skipped when the vault is indexed, the way Obsidian reads the same file, and the Brain’s own scan of chat and memory bodies skips them too.

The phone says loading, not empty. A cold open over the relay used to print “No chats” and “No runs” as fact while the first round trips were still in flight. Lists now wait to be answered and show the spinner with a relay-aware subject, a refresh keeps the previous rows, the transcript waits for its first load and offers Retry when it fails, and a deep link into an approval no longer claims the item is gone before it has loaded.

The run log is complete when the CLI exits. latch run ended the process on an unflushed tail, so the JSONL on disk stopped several events short of what the same run had just printed and the boot sweep later called it interrupted. The log is drained before exit, in the CLI and in the server’s graceful stop.

Electron 44 — back inside the security window. The desktop shell now builds on Electron 44 (Chromium 152, Node 24 inside the packaged app), replacing Electron 35, whose upstream security support ended on 2025-09-02. Chromium and V8 patches reach the app again. The cost is a floor: LatchAI now requires macOS 13 (Ventura) or newer — Electron 44 does not run on macOS 11 or 12. No API the shell uses changed; node-pty is N-API and needs no rebuild.

Type colours, and refiling a note from the editor. A type in vault.json may now carry a color: — any CSS colour — and the Brain area draws that vault type in it, falling back to its own palette for the types that say nothing. A note can also change what it IS: give it a new type or a new name and it moves to that type’s folder, with type, updated, the date a date-prefixed type needs and a status the new type does not define rewritten for it — and its old file name kept as an alias, so every [[wikilink]] pointing at it still resolves. Like accepting a proposal, it is a human gesture with a commit behind it.

Making a vault is one call. Until now a vault meant hand-writing a vault.json, laying out the folders yourself, and then adding the mount separately. POST /api/vaults does the whole thing: it writes the FULL resolved schema (the built-in defaults with your overrides merged in, so the file on disk says everything the engine will enforce and nothing is left implicit — types, as always, replaces the default set rather than merging into it), creates the inbox and a folder for every type that has one, generates a vault-conventions.md from that schema, gives the vault its own git repo with a first commit, and mounts it — watcher re-armed, MCP servers reconnected, listed in the Brain area immediately. Point it at a folder that already holds a vault.json and it ADOPTS it instead: nothing written, no commit, just mounted. A folder created inside an existing repo gets no repo of its own and says why — git add -A in a subdirectory would stage the whole enclosing repo, so triage commits stay off unless the vault root is itself a repository toplevel. Each vault is its own knowledge base: wikilinks never resolve across vaults, and an agent binds to exactly one.

New vault, from the Brain area. “+ New vault” in the Notes toolbar, in the empty state, and at the bottom of the Graph lens’s scope picker opens a form: a name, a folder (prefilled beside your existing vault), a schema (the standard set, a copy of another vault’s, or a minimal notes-only layout), and whether to start a git repository. The new vault is selected the moment it exists, becomes the graph’s scope when you started there, and a note about git (the folder sits inside another repository, or git is not installed) is shown once and can be dismissed.

Accept and reject where you are reading. Filing an inbox note used to mean finding it again in the Inbox lens: opening it from the graph or the note tree showed the editor and nothing else. A note still in the inbox now carries its triage bar in the editor header on both the Notes lens and the graph’s side panel: the folder picker, Reject, and Accept into its folder. Unsaved edits are saved first, the editor follows the note to its new folder, the graph clears the pending ring, and a filing that could not be committed says so.

Six fixes from the September review, all of them the kind that lose work or take the daemon down.

  • A bad URL no longer exits the daemon. A malformed percent-escape in any request path — something a web page can send with an image tag — threw out of the request listener and the engine died; the desktop shell gave up respawning after five exits a minute. Every decode now answers 400, the route dispatch answers 500 rather than throwing, and the daemon keeps process-level nets that log an unexpected error with its stack and stay up.

  • Workflow and run ids are fenced. Saving or deleting a workflow whose id was ..%2Fsomething wrote or removed a file beside the home. Ids with a path separator, a NUL or a leading dot are refused on every route, and the schema carries the same rule. Every existing workflow id still validates.

  • Edits keep dollar signs. The edit tools spliced with a string replacement, so a shell $$, a Makefile $$(…) or bash $'\n' an agent wrote landed on disk mangled. Splices are literal now, in edit_file, multi_edit, the memory tool and the MCP argv substitution.

  • Request bodies decode UTF-8 once, so a non-ASCII character split across two chunks arrives intact instead of as U+FFFD.

  • A malformed relay pair frame is a mismatch, not a crash. While a pairing secret was open, one bad frame from the relay took the daemon down.

  • The workbench reconnects. After a daemon restart — or first run, where onboarding relocates the home and the engine exits by design — the window sat on “engine offline” for good. The socket now retries with backoff and every reopen catches up on what it missed. The status bar says “connecting…” until an attempt has actually failed, and the Runs sidebar says it is loading instead of “No runs yet.” over a run in progress.

  • A crash saves your buffers. A render error in one bad dashboard document or chart used to unmount the whole workbench and lose every unsaved buffer with it. The workbench now writes the same hot-exit backup a close does before showing a crash screen, a throwing tab or widget fails in its own slot while everything around it keeps working, and a restore whose file read fails keeps the unsaved text and marks it for review instead of dropping the tab.

  • An agent with no model of its own follows the pinned default, live. The chat picker used to start on the first provider in latchai.config.json — not the one pinned in Models — and saved that name into every new session, so an agent left on “(default provider)” kept running on the first entry after the default moved. The picker now leads with Default · <model>, sends the literal default so the engine resolves the pin on every turn, and sessions the old seeding pinned to that first entry are pointed back at the default once.

  • load_skill takes a URL. Give it url instead of name and it loads a raw SKILL.md over http(s), fetched fresh on every call — nothing is cached, so a skill maintained elsewhere is always read as it is now. The same address floor as web_fetch applies and is re-checked on every redirect, an HTML page is refused with a pointer to the raw file, and the text comes back framed with where and when it was fetched. Loading from a new host asks first, the way opening a new site in the browser does, and “always allow” silences it per host.

The engine half of these takes effect on the next daemon restart.

Brain: one place for what you know. The Vault and the provenance graph are one rail entry now, with three lenses — Graph, Notes, Inbox. A vault is any mounted folder of markdown notes with a vault.json at its root. The Graph lens draws either the workspace’s provenance graph or a vault’s wikilink network: nodes coloured by note type, a dashed ring on notes still waiting in the inbox, and ghost nodes for links to notes nobody has written yet — click one and the new-note form opens, prefilled. Clicking any note opens the real editor in a resizable panel beside the graph rather than leaving it, and a half-written note survives whatever else the panel shows.

Agents get exactly one vault tool — search, read, links, propose (into the inbox only, after a mandatory duplicate check over names and aliases) and append to a note’s Log section. There is no general write. Accepting or rejecting an inbox note is the human gesture, and it is the one place the daemon commits the vault’s repo; agents never run git there. An agent opts into a vault with vault: in its frontmatter and gets the index in its prompt.

Quick capture. Tell the vault a sentence — a call you just had, three facts about a new client — and a capture pass files it as inbox proposals, appending to notes that already exist and proposing the ones that do not. It runs with only the vault tool, so the worst it can do is stage a note for you to reject. The box stays after each pass, empty and focused, so the next note is a keystroke away; the notes each pass wrote accumulate as chips you can open, and the Inbox lens and its badge count them the moment they land — from the graph panel, the Notes lens, or the palette. (An earlier build opened nothing when you clicked a chip, and the Inbox did not notice a capture made from the graph.)

The Code area, Cursor-class. Search across the workspace and every mount (⌘⇧F), with results in place of the tree while a query is present. Git status colours tree rows and tabs with letter badges and folder roll-up dots, and the editor shows a change gutter. The explorer gained multi-select (⌘/shift), drag-and-drop moves with a batch confirm, Copy, Duplicate and Paste, and Finder drops. The editor gained sticky scroll and a Problems panel. Tab autocomplete is on by default, with a toggle and its own model slot in Settings ▸ Editor. The rail regrouped into Development, Automation, Audit and Intelligence. Apply-from-chat is described below.

Agents drive your own browser. A Chromium extension (apps/browser-extension, loaded unpacked for now) connects to the engine over a loopback-only socket, paired with a rotatable token in Settings ▸ Browser. Agents see it as ordinary browser_* tools — tabs, navigate, snapshot, click, type, screenshot, evaluate — behind a floor on which sites an agent may open, and every action is a row in the chat and a browser.action event in the run. The phone renders those rows too.

Smaller things: the tool picker groups built-ins by family (Files, Browser, Work items…) instead of one flat list; chat images open in a full-window Lightbox with fit and 1:1 views; a tool call the model once emitted with unparseable arguments no longer poisons every later request on vLLM’s Qwen templates (historical calls are re-serialized before replay); the phone’s workflow folders collapse like the desktop sidebar.

Apply from chat. Every code block the assistant writes now has an Apply button beside Copy, and it says where the code is going before you press it — the file named in the fence, else a file named in the message, else the tab you have open, else it asks. Apply merges the snippet into that file and opens a proposed diff: before and after, side by side, with Accept and Reject. Nothing is written to disk either way — Accept puts the merged text into the editor buffer and marks it dirty, so you still save it yourself, and Reject just closes the tab.

The merge runs in two tiers. First a free, exact splice inside the daemon: it handles a whole-file rewrite, elision markers (// ... existing code ...), and fragments whose surrounding lines pin down exactly one place in the file — and it re-indents a snippet the model wrote at the wrong column. It declines rather than guesses: anything ambiguous falls through to the second tier, which asks a model to integrate the change and hand back the whole file. That model is the one in Settings ▸ Editor (the same slot tab-completion uses), and if you have not configured one, Apply says so quietly beside the block instead of interrupting the conversation. The button appears in chat only — the markdown file preview and the Usage transcripts are untouched.

The phone app, rebuilt. apps/mobile was a prototype that polled seven endpoints every three seconds and could not see a permission prompt open; it is now the same event-driven app the desktop is, on a five-tab shell, reachable from anywhere.

  • Relay mode streams. A paired phone subscribes to the daemon’s own event feed through the blind relay and reduces it with the same reducer the workbench runs — so a chat reply arrives token by token, a permission prompt appears the moment it opens, and a run’s nodes tick over live. The old three-second poll is gone; what is left of the request path is a catch-up on wake, on reconnect, and a slow safety-net heartbeat. Payloads over ~160 KB are now chunked inside the seal, so a long transcript no longer drops the link and loops. The deployed relay needed no redeploy for any of it.
  • Web push — alerts that reach a closed app (RFC 8291/8292, on node:crypto, no new dependency). A blocked agent or a failed run lights your lock screen and a tap lands on the right screen, even from a cold start. The payload carries a title, a kind and a deep link — never a body, tool arguments or run output. A subscription belongs to a phone, not to the home, so un-pairing one device takes its alerts with it. iPhone: add LatchAI to the Home Screen, then Settings ▸ Notifications ▸ Turn alerts on.
  • A real shell. Five tabs (Chat, Workflows, Dashboards, Inbox, More) on a hash router with a working back stack and deep links, so a notification can send you straight to #/approvals/:id. Approvals stopped being a tab that hijacked the screen mid-sentence: they are a pinned “Needs you” section of the Inbox plus a quiet banner. The app never switches tabs on its own.
  • It opens instantly, offline. The last lists are cached in IndexedDB and render immediately over a cold start; the top bar’s pill says whether you are looking at live data or how old the cached rows are. The blocking “Reconnecting…” screen is gone.
  • Chat parity with the desktop: streamed replies, a collapsible Thinking row, tool and shell rows that expand, an inline permission card in the transcript, a queued follow-up while the agent is busy, photos from the camera or library (resized on the phone to fit the wire), a searchable conversation sheet, long-press to Copy or send again, and a send failure you can retry.
  • Workflows: every graph, grouped into the same folders as the desktop, with whether each one is running right now. A workflow opens as an ordered step list with live per-node status and streaming output, its triggers, and its run history — and a run opens as a transcript.
  • Dashboards: every board you built on the Mac, drawn in one column, filling in as an agent collects. Charts reuse the workbench’s own Chart.js renderer, loaded only if you look at a chart.
  • Usage: the daemon’s own numbers for today, 7 days or 30 — runs, chats, tokens in and out, spend, tokens-by-day bars, and the top workflows, models and agents. Cost appears only when the daemon reports one, because an instance running local models spent hardware, not dollars.
  • Inbox: grouped by day, swipe to dismiss with a 5-second undo, and a run failure opens to the full error tail from the run’s own log — not the clipped 500 characters the notification carried.
  • Pairing is one scan. An in-app QR scanner (the system Camera app opens the blob in Safari, which an installed PWA cannot read), a durable session so a relaunch reconnects with no re-pair, and a relay blip that no longer sends you back to a QR code. Un-pairing from the Mac tells the phone so, in those words, and clears everything it had cached.
  • Settings: which Mac and which relay you are paired to, transport, theme (system/dark/light), alerts, version, and Unpair behind a confirm.
  • Fixed: /api/mobile/* demanded the token even from the relay bridge, so the phone’s pending-approvals digest had been answering 401 over the relay.

The auto-update release. Every build to date had a bug that silently prevented updates from ever installing: the “ready to install” dialog had no parent window, which on macOS is a synchronous native modal — it froze the main process at the exact moment the updater hands the staged update to macOS’s installer. Downloads succeeded; installs never happened; “Restart Now” quit without updating.

  • Auto-update actually installs: all dialogs now guarantee a parent window (truly async), the install prompt is deferred off the updater’s critical path, and with no window open the update installs silently on quit. Proven with a complete local update cycle through both install paths.
  • Updater diagnostics: the app writes updater.log in its data folder — every updater and native-installer event, so a failed update is diagnosable instead of silent.
  • Single-instance lock: launching LatchAI twice now focuses the running app instead of starting a second copy.
  • Clean shutdown: the engine child is verifiably stopped (with a kill fallback) before an update installs or the app quits.

If you are on 0.7.1 or older: install this version once manually — the bug lives in the running copy, so it cannot update itself. From 0.7.2 onward, updates apply automatically.

56 commits since 0.7.0, one weekend later. The headline: the notification inbox, a hardened workflow engine, and a test suite that grew from ~1,700 to ~2,730 tests (line coverage 51% → 71%), largely written by the app’s own Test Coverage workflow.

  • A bell on the activity rail with a live unread badge. Run failures, work-item lane changes, and runs waiting on your approval each mint a notification — deduped, persisted, bounded — and clicking one jumps to its subject: the run’s transcript, the board item, or the waiting gate. The silent overnight failure now has somewhere to land.
  • Model-call retry: transient 429/5xx/network failures retry with a fixed ~5s pause between attempts (3 attempts, Retry-After honored, stopped runs fail fast, usage never double-counted), with a model.retry event in the transcript.
  • Command node: stateKey stores the collected JSON document when outputFile is set, and outputFile is best-effort when failOnNonZero: false — a red test suite becomes a branchable result instead of a dead run.
  • Agents may edit work-item bodies via work_update — durable description and acceptance criteria amendments, with the same activity trace human edits leave.
  • Concurrent runs each animate their own canvas: live node state is scoped per run, so two running workflows no longer fight over one highlight.
  • Canvas node cards contain long content — one-line command rows with the exit status pinned, clamped output previews.
  • A built-in projects skill ships in the bundle so the assistant can answer what the Projects area is for, when to use it, and how boards, items, custom fields, and the per-item weave work.

The workbench shell and the settings page were decomposed into per-concern hooks and components, each individually tested, and ~40 test suites landed from the autonomous coverage workflow — whole-tree line coverage 51% → 71%.

86 commits since 0.6.0 (2026-08-16). The headline: a Projects area with native work-item boards, a rebuilt workflow engine on format v2 (breaking — see below), a deterministic command node, a Database area, honest Usage accounting, and a test suite that grew past 1,700 tests — much of it written by LatchAI’s own Test Coverage workflow running unattended.

⚠ Breaking: workflow format v1 is removed

Section titled “⚠ Breaking: workflow format v1 is removed”

formatVersion: 2 is now the only workflow format. There is no migrator: a v1 file is rejected at load — it stays visible in the sidebar (and can be filed into a folder for the record) but cannot run, and every save path refuses to write it. Rebuild old workflows in the editor or ask the chat to rebuild them; the v2 authoring surface (below) is substantially stronger than what v1 could express. If you update and your workflows show as rejected, this is why.

Projects — native work-item boards (new area)

Section titled “Projects — native work-item boards (new area)”
  • The spine: projects live at workspace/work/<project>/ as plain markdown — a board.md (name, key prefix, lanes, kinds, repos, custom field declarations, per-kind body templates) plus one file per item with fixed core fields, flat custom fields, free markdown body, and an append-only ## Activity log. Five work_* tools give agents the same board humans use; key allocation is atomic and never reuses a number; hand-edited files are first-class (unknown fields are preserved verbatim, never validated away).
  • The board UI: a full-page kanban per project — drag between lanes, item pages with body editing and activity, archive/unarchive. UI mutations broadcast live but never pollute run logs.
  • The weave: every item shows the runs that touched it, what they cost, the commits that reference its key (git log --grep across the board’s declared repos, mount-contained), and its place in the brain graph. Run↔item linking is implicit — an agent updating an item ties the run to it.

Workflows — format v2 and the new engine

Section titled “Workflows — format v2 and the new engine”
  • formatVersion 2: structured node outputs and dot-path templating ({{node.field.sub}}).
  • Run state document: declared state keys with merge policies (last/append/merge/sum), real join semantics (all/any), and a concurrent wave scheduler.
  • Cycles and fan-out: loops with step budgets, and the map node for per-item fan-out.
  • Durable runs: stop, resume, boot sweep of orphaned runs, persistent gates, hardened triggers.
  • Multiple triggers per workflow with a visual schedule builder and per-trigger pause: pause one cron/webhook door while the rest of the graph carries on — disabled still closes the whole building.
  • The command node: a deterministic shell step — no agent, no tokens. Runs through the exact same guarded shell core agents get (scrubbed env, git ceiling, denylist, output clipping), optionally collects a JSON outputFile as structured output, and exposes { exitCode, ok, durationMs, stdout, json } to downstream templates. Born from a real diagnosis: an agent was spending 14 minutes babysitting a 9-second test run. Deterministic work needs no agent.
  • Schema-first agent outputs: declare an output schema and the reply template is generated and appended automatically; {{ in any config field opens an autocomplete of everything upstream nodes and state offer.
  • Node palette: drag node types from a docked palette onto the canvas, with per-type icons and category color stripes shared between palette cards and canvas nodes. The keyboard picker remains.
  • Sidebar folders for workflows, human-readable run labels, and graceful rejected-file handling.
  • Compat spine: a warnings channel, node catalog, SDK builder parity, and updated authoring skills so chat-built workflows always target v2.
  • The area: SQLite and Postgres connections, a schema browser, query tabs, result grids with exports, and AI query tools gated behind explicit approval — reads free, mutations prompted.
  • Promoted to a first-class activity-rail view, with a native OS file picker for SQLite files in the desktop app and a polish pass over the grid, docks, and editing. See Database.

Usage — honest accounting (renamed from Reporting)

Section titled “Usage — honest accounting (renamed from Reporting)”
  • Count what actually runs: chat-freeze data loss fixed, compaction/gateway/one-shot calls now counted (previously ~a third of real spend was invisible), on a windowed and paged query layer that no longer freezes the page.
  • The Usage page: ranged, charted, clickable through to transcripts.
  • Usage now opens as a preview tab — glance and it’s replaced; double-click pins.
  • Memory rework: a single memory tool (Anthropic pattern) with per-agent and shared scopes, a consolidation pass, and a Settings surface to review and manage memories. Same change: prompt-cache token reporting for OpenAI-compatible providers, and per-agent generation parameters (temperature, top_p, reasoning effort) with model-aware knob dropping.
  • Context-window-relative compaction with a visible fill ring and compaction markers in the transcript.
  • Tool-result hygiene: a per-tool result-cap registry with honest truncation markers, paged grep/glob/web results, and ambiguity-aware file path resolution — reads redirect with a note when unambiguous, writes never redirect.
  • Parallel read-only tool batches with seam-level argument validation, deduped repair turns and prompt layering.
  • Live permission mode: changing the mode applies to the running turn, and “Allow all tools” releases prompts already on screen.
  • Chat context digests for file and database tabs: the assistant sees real data about what you’re looking at, not just a tab name.
  • Attended prompts for delete-class and MCP tools, plus a crash guard.
  • The HTTP router decomposed: server.ts shrank from ~2,800 to ~300 lines; one route module per area behind a single tested seam.
  • UI internals decomposed: App/useEngine logic extracted into pure, individually tested modules.
  • rg and git can no longer hang the daemon.
  • Home relocation keeps models/; daemon install resolves the code root; local providers survive Settings saves and report their real context window.
  • Dashboards: scheduled refreshes can be paused and resumed.
  • Hand-rolled SVG icon set for the activity rail — thirteen 16×16 stroke icons replacing font-fallback Unicode glyphs; every area finally has a distinct silhouette that respects hover/active states.
  • Collapsible sidebar: visible chevron, ⌘B, or re-click the active rail item; collapse is transient — arriving back at a workflow re-expands it.
  • Tier-2 polish: command palette, event feed, badges, theme details, hot exit. Rail order made deliberate.
  • Whole-tree coverage adopted: vitest measures the entire source universe, so untested modules report 0% instead of hiding; thresholds ratchet upward as tests land.
  • Roughly 40 of this release’s commits are test coverage — the executor core, the shell/term surfaces, the atlas pipeline, the database grid, the work board, the markdown renderer, and more. A large share were written and committed by LatchAI’s own Test Coverage workflow running on a cron: measure, pick the worst file, write tests, verify, commit.
  • The release gate: 127 test files, 1,716 tests, all passing, engine and UI typechecks clean.

Releases 0.2.0 through 0.6.0 (2026-07-30 to 2026-08-16) are not listed here. They predate these notes, and the workflow format they wrote is no longer loadable — see the breaking change in 0.7.0.