Skip to content

zig: the 0.17 switch, scaffolded with marked stand-ins - #437

Merged
foxnne merged 1 commit into
zig/0.17from
zig/017-scaffold
Oct 16, 2026
Merged

foxnne merged 1 commit into
zig/0.17from
zig/017-scaffold

Conversation

@foxnne

@foxnne foxnne commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator

Part of fizzyedit/plans#5 (plans/ZIG_0_17_PLAN.md, step 2; the spike's findings are in fizzyedit/plans#12)

The first PR into the zig/0.17 integration branch. It opens the branch green on 0.17, so each PR after it is a reviewable migration with CI on every platform, not a long red stretch.

What changes

  • Toolchain: minimum_zig_version = "0.17.0" everywhere; ZIG_VERSION 0.17.0 in ci.yml, web.yml, release.yml, plugin-loop.yml.
  • Forks, pinned by commit on a zig-0.17 branch in each (tagged when zig/0.17 merges to main, per docs/DEPENDENCIES.md rule 2):
    • foxnne/dvui-dev 2c899d18: fizzy-dev's 8 patches rebased onto dvui's main (41f6703), one-line conflict; passes dvui's tests on 0.17.
    • foxnne/zig-objc f90394b: @typeInfo, **; passes its tests.
    • fizzyedit/md4zig eb5241c: @cImport → translate-c module; passes its tests.
    • foxnne/assetpack 91eb632: one line.
    • fizzyedit/sdl_zig 404f499: five config values 0.17 rejects (still SDL 3.4.16).
    • zf and nightwatch repinned to their 0.17 commits.
  • scripts/zig-0.17/rewrite.sh: the mechanical half, re-run after every merge from main, deleted at the end. zig fmt's upgrades, the optimize-mode, dupeZ/bufPrintZ, EnumSet, std.meta.Int renames, the @typeInfo field loops, and the zon stand-in below.
  • Hand edits from the spike: the build API renames, **, @bitCast to extern structs, fingerprint.zig translated loop for loop, Zoir/Ast.parse options, dvui's per-pointer capture (dvui#1014), and others.
  • 51 ZIG017 stand-ins in 33 files, each holding the place of a migration a later PR does properly:
    • the build system (custom makeFn steps, configure-time install paths, --libc, the MSVC shim, Velopack's addPathDir);
    • std.zon.parse: results now come from the caller's arena. The stand-in parses into a short-lived arena and deep-copies into the old allocator (zig017ZonDupe), with a file-local zig017ZonFree, so ownership is unchanged and nothing leaks;
    • the SDK's settings and command schemas reading field records (sdk/src/zig017_fields.zig);
    • Velopack's @cImport.
  • recorded_sdk_shape_fingerprint moved (dvui's shape since 2026-10-02). It is released with the merge to main.

Diff size: 156 files, +2165/−870. Most of it is the script's output and the stand-ins' helpers. Read the script, not its output.

SDK impact

  • None
  • Core-only or additive: reaches plugins at the next SDK release
  • Fingerprint moved: recorded in sdk/src/version.zig, sdk_version untouched, PR labelled sdk
  • SDK release: bumps sdk_version, lists the sdk PRs since the last sdk-v* tag

Verified

  • macOS (arm64, 0.17.0): scripts/check-fmt.sh; zig build; zig build test (91/91 steps); zig build test-integration (77/77 steps, 523/523 tests); zig build check-web; zig build test-sdk-version; zig build check-tests-reached. The spike also launched the app (window 1.1 s after main).
  • Windows: CI's cross-compile (not run locally).
  • Linux: CI (not run locally).
  • Web: zig build check-web.

Example repos: scripts/check-examples.sh now builds the examples' zig-0.17 branches (fizzyedit/example-app 533c38b, example-plugin 34c94ec; back to main at the merge, marked ZIG017). It also now gives example-app this tree's SDK everywhere it pins one: its nested plugins/shader, and its bundled example-plugin (hello), whose pinned commit carried SDK 0.2.18. It stops macOS tar from adding AppleDouble ._* files, which the backend took for SDL patches when the script ran on a Mac. All 8 example builds pass locally on macOS. The plugin dev install is done for real here (moved down from #438), since a stand-in that skipped it broke the plugin rebuild benchmark: sdk/plugin_sdk_install.zig is a program the plugin's build runs, reading FIZZY_PROFILE/HOME itself at make time, and FIZZY_PLUGIN_USE_LLVM becomes -Dplugin-use-llvm (0.17 caches the configuration without regard to the environment), which scripts/plugin-loop/bench.py passes.

Follow-ups

The next PRs into zig/0.17: the build system (next), zon arenas, @typeInfo with a fingerprint-equality test, Velopack translate-c, the example repos.

🤖 Generated with Claude Code

@foxnne foxnne added the sdk Moves the plugin boundary's fingerprint; ships at the next SDK release label Oct 15, 2026
foxnne added a commit that referenced this pull request Oct 15, 2026
## What changes

`sdk_version` goes from 0.2.26 to 0.2.27. Merging this tags
`sdk-v0.2.27` and publishes its tarball.

**Why now:** the agent plugin's `stroke` step (fizzyedit/agent#11, on
agent `main`) uses `tape.Script.StrokePoint` from #431, which no
released SDK has, so agent's new CI (fizzyedit/agent#13) can't pass
against its pinned SDK until it repins to a release that carries it.

## SDK impact

- [ ] None
- [ ] Core-only or additive: reaches plugins at the next SDK release
- [ ] Fingerprint moved: recorded in `sdk/src/version.zig`,
`sdk_version` untouched, PR labelled `sdk`
- [x] SDK release: bumps `sdk_version`, lists the `sdk` PRs since the
last `sdk-v*` tag

**`sdk` PRs since `sdk-v0.2.26`:**
- **#434:** two optional document vtable members, `historyLine` (a
document's undo history as a line of steps, with a `version`) and
`historyGoTo` (undo or redo to a step), with `Plugin.HistoryLine`. An
owner without them has no timeline; undo and redo are unchanged. The
fingerprint moved, so the repin automation opens a repin PR in each
store plugin.

**Reaching plugins, no fingerprint move:**
- **#431:** `tape`: a stroke carries the pointer in straight lines
(`Script.StrokePoint`).
- **#432:** `tape`: a caption follows where the pointer reaches, not a
pen's path.
- **#433:** `replay`: a tape's pointer gives way to a cursor the app
draws itself.
- **#427:** `core`: the bubble spinner keeps its bubbles near one size
at any scale.

Open `sdk`-labelled #437 (the Zig 0.17 switch) is a draft and not in
this release.

## Verified

- [x] macOS:
- On this head, `zig build test-sdk-version`: 12/12 steps succeeded.
`scripts/pack-sdk.sh` packs `fizzy-sdk-v0.2.27.tar.gz`.
- fizzyedit/agent (main plus fizzyedit/agent#14) builds against this
`sdk/` (`--fork`) for x86_64-windows, x86_64-linux-gnu and
aarch64-macos, and `zig build test` passes.
- [ ] Windows:
- [ ] Linux: CI.
- [ ] Web:

## Follow-ups

- The repin automation opens `sdk: repin to fizzy SDK 0.2.27` PRs in the
store plugins whose build needs it. agent repins too, and then
fizzyedit/agent#13's CI can go green.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
foxnne added a commit that referenced this pull request Oct 15, 2026
Part of fizzyedit/plans#13 (step 1).

## What changes

An app built on fizzy chooses which of fizzy's plugins it links.
`fizzy.plugins.{workbench,text,image,archive,markdown,devtools}` are
entries in the list `fizzy.buildApp` takes, beside the app's own, and
`fizzy.plugins.all` is fizzy's own set:

```zig
try fizzy.buildApp(fizzy_dep, &.{
    fizzy.plugins.workbench,
    fizzy.plugins.text,
    .{ .name = "graph", .module = graph_dep.module("plugin") },
});
```

Fizzy's own entries have no module: `build/plugins.zig`'s `link` builds
each one for every executable (native, packaged, web), wired to that
executable's framework, and re-homes the app's modules the way the
native and web builds each did on their own before. Fizzy's build passes
`fizzy.plugins.all` through the same path, so the exe and the web build
no longer keep separate copies of the list. The test build keeps its own
list, because it tests fizzy itself. A plugin that isn't listed doesn't
register, and fizzy's dylib for it (text, markdown, image) is neither
built nor installed.

Two plugins are always built, because fizzy's own code imports them as
libraries. **workbench** must be listed: the editor still holds its
state (`editor.workbench`, the dylib's `arg_c`), and a list without it
fails the build with a message naming the plan. **markdown** is always
compiled, because the store draws READMEs with it, but it only registers
as a plugin when listed.

**Transition:** fizzy's CI builds example-app's `main`, and example-app
pins fizzy by commit, so neither repo can make the change first. Until
example-app lists fizzy's plugins, a `buildApp` list that names none of
them gets all of them first, as before. That fallback is three lines in
`buildApp`, and step 2 removes it. fizzyedit/example-app#6 lists them
explicitly (its minimal shape links only workbench and text), and it
repins to this once merged.

Also:
- `Entry.zig` drops an unused `@import("text")`.
- The `postInit` comment no longer says the bundled plugins don't ship
as dylibs. They do, and it now says why.
- The root `AGENTS.md` describes the list.

Overlaps in files only with the Zig 0.17 drafts (#437, #438), which
touch the whole build; this change touches no build step.

## SDK impact

- [x] None

## Verified

- [ ] macOS: `zig build`, `check-web`, `test`, `test-integration`,
`test-sdk-version`, `check-fmt`. The four built-in dylibs are still
installed to `zig-out/<target>/plugins/`. `scripts/check-examples.sh`
passes against example-app with explicit lists (its minimal shape links
only workbench and text), and against example-app's `main` through the
fallback.
- [ ] Windows:
- [ ] Linux: CI
- [ ] Web: `check-web`

## Follow-ups

- Step 2 (plans#13): a link-only build, and removing the fallback.
- workbench becomes optional once its state moves from the Editor into
the plugin.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Opens the zig/0.17 integration branch (plans/ZIG_0_17_PLAN.md) green
on 0.17: the toolchain, the forks' zig-0.17 commits, the mechanical
rewrites from scripts/zig-0.17/rewrite.sh, and hand edits. Where a
migration is still to be done properly, a stand-in marked ZIG017
holds its place; each later PR into zig/0.17 replaces some, and the
merge to main has none.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@foxnne
foxnne marked this pull request as ready for review October 16, 2026 12:52
@foxnne
foxnne merged commit ea83962 into zig/0.17 Oct 16, 2026
13 checks passed
@foxnne
foxnne deleted the zig/017-scaffold branch October 16, 2026 12:52
foxnne added a commit that referenced this pull request Oct 16, 2026
Part of fizzyedit/plans#5 (`plans/ZIG_0_17_PLAN.md`, step 2: the build
system). **Stacked on #437**: based on `zig/017-scaffold`, so this diff
is only the app's build-system work. The plugin dev install and
`-Dplugin-use-llvm` moved down into #437. It goes to `zig/0.17` once
#437 merges.

## What changes

Zig 0.17 runs `build.zig` in a configurer and the graph in a separate
maker. There are no custom steps (`makeFn`), no install paths at
configure time, and the configuration is cached.

- **"Set aside a running executable"** (Windows hosts) becomes
`build/set_aside.zig`, run before the install. Its directory comes from
a new `common.installDirPath`: a `LazyPath` relative to the install
prefix, resolved when the build runs, in place of `b.getInstallPath`.
The packaging script's `--packDir` uses the same helper.
- **The `update` step and root `update.zig` are deleted.** They pulled
dvui-dev `main` and a zigwin32 0.16 branch, neither of which fizzy pins
any more.
- **Configure-time reads are declared**
(`dependOnFileContents`/`Metadata`/`DirectoryContents`): `VERSION`, each
plugin's `plugin.zig.zon`, `web/index.html`, the web plugin checkouts
and their `web/` dirs, `release.yml`, `patches/sdl`. Without that, 0.17
reuses a stale configuration (checked: a changed `VERSION` now reaches
the binary). Existence checks and directory opens go through `b.root`'s
own `Path` API.
- **A path that may not exist** (a web plugin checkout and its `web/`
directory; #437 does the same for a plugin's `release.yml`) is declared
by `dependOnPathExistence`: the file when it exists, otherwise the
nearest directory that does. 0.17's maker cannot load a configuration
that depends on a path inside a missing directory ("configuration
failed: FileSystemFailure"); a plugin without `.github/` hit it. Checked
by building `plugins/image` copied without its `.github/`.

**Left for the release-packaging PR**, each still marked `ZIG017`, since
they run only in release builds and need the release workflow to verify:
- the global `--libc` (→ `-Dwindows-libc`) and the MSVC translate-c
shim;
- Velopack's `addPathDir` for `mksquashfs`;
- the `FIZZY_MACOS_*` signing fallbacks and `SDKROOT`, which are
environment reads at configure.

## SDK impact

- [x] None: no `sdk/` file changes here (the plugin install moved to
#437).
- [ ] Core-only or additive: reaches plugins at the next SDK release
- [ ] Fingerprint moved
- [ ] SDK release

## Verified

- [x] macOS (arm64, 0.17.0):
- `scripts/check-fmt.sh`, `zig build`, `zig build test` (91/91 steps),
`zig build test-integration` (77/77 steps), `zig build check-web`, `zig
build test-sdk-version`, `zig build check-tests-reached`.
- `scripts/check-examples.sh` against the examples' `zig-0.17` branches:
all 8 builds pass.
- Standalone `cd plugins/image && FIZZY_PROFILE=<sandbox> zig build`
installs into `<sandbox>/plugins/image/`, again on a rebuild with no
change. A clean `-Doptimize=ReleaseFast` plugin build fetches no
Velopack (AGENTS.md's acceptance test).
- The set-aside step, forced on for a macOS run: nothing to do on a
first build (install dir absent), checks the installed exe on the next.
- [x] Windows: `zig build -Dtarget=x86_64-windows-gnu
-Dnative-backend=fizzy -Dno-emit` configures and compiles. Not run on a
Windows host.
- [ ] Linux: CI.
- [x] Web: `zig build check-web`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
foxnne added a commit that referenced this pull request Oct 16, 2026
Part of fizzyedit/plans#5 (`plans/ZIG_0_17_PLAN.md`, step 2:
`std.zon.parse`). **Stacked on #448**: based on
`zig/017-release-packaging`, so this diff is only the zon work.

## What changes

Zig 0.17's `std.zon.parse.fromSlice` takes a `gpa` for scratch and an
`arena` that owns the result and any diagnostics, and there is no
`free`. The scaffold (#437) bridged it with a stand-in: parse into a
throwaway arena, deep-copy into the old allocator, plus a file-local
copy of 0.16's `free`, in 20 files. This replaces every call with the
arena that actually owns its result. The diff is −1,298/+200: the
generated copy-and-free helpers go.

- **Parsed, used and freed in one scope** (recents, the settings
migrations, the workbench layout snapshot, the window-layout reads and
saves, tests): a local arena that dies where the free used to run.
Lifetimes are unchanged.
- **Parsing into an arena already** (`sdk.events.parse`, `Tape.parse`,
the restart session, `Command.Call.parse`, the replay and profile
tests): that arena, passed as `.arena`.
- **Types that hold no pointers** (the saved window geometry, SDL's
saved window, the demo verdict's expectations): `fromSliceNoAlloc`. It
checks at compile time that the type holds no pointers, and its result
outlives the arena.
- **API changes, each with every caller updated:**
- `Settings.parseOnly(allocator, arena, data)`. `freeParsed` is gone,
and with it the workaround for freeing a default string literal; `load`
still copies `theme`, the one string it keeps.
- `sdk.Manifest.parse(gpa, arena, source)`. `Manifest.free` is gone. All
of `PluginLoader`'s calls already parsed and freed in one scope.
  - `layout_file.loadWindowFile(gpa, arena, dir)`.
- **Plugin settings** (`sdk.settings.Schema`, `applyZon` and setting one
field from ZON text) parse into a short-lived arena, then copy a
non-default string out into the schema's allocator. `freeOwned`'s rule
still holds: a field is the schema's to free unless it is the declared
default.
- **Build scripts** reading a plugin's `plugin.zig.zon` read it into the
build's allocator, which lives as long as the build.
- **`scripts/zig-0.17/rewrite.sh` no longer converts zon calls.** A 0.16
call that a merge from `main` brings now fails to compile and gets
ported by hand.

After this, 4 `ZIG017` markers remain on the branch: the two per-pointer
capture sites (dvui#1014), the examples' branch in `check-examples.sh`,
and the `@typeInfo` field helper.

## SDK impact

- [ ] None
- [x] Core-only or additive: reaches plugins at the next SDK release.
`sdk.Manifest.parse` takes an arena and `sdk.Manifest.free` is gone.
That is SDK API, but not the plugin boundary: no fingerprint move (`zig
build test-sdk-version` passes), and no store plugin calls either.
- [ ] Fingerprint moved
- [ ] SDK release

## Verified

- [x] macOS (arm64, 0.17.0):
- `scripts/check-fmt.sh`, `zig build`, `zig build test` (91/91 steps),
`zig build test-integration` (77/77 steps, 523/523 tests, no leak
reports), `zig build check-web`, `zig build test-sdk-version`, `zig
build check-tests-reached`, `zig build -Dvelopack=true`.
- `scripts/check-examples.sh` against the examples' `zig-0.17` branches:
all 8 builds pass.
- The app, in a sandbox profile with an existing `layout.zon` and
`recents.zon`: starts without a parse warning. With a `settings.zon`
naming a non-default theme, the theme name survives the arena (fizzy's
"did not match any known theme" warning names it), and the settings
migration rewrites the file keeping `theme` and `font_body_size`.
- [ ] Windows: CI.
- [ ] Linux: CI.
- [x] Web: `zig build check-web`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@foxnne foxnne mentioned this pull request Oct 16, 2026
2 of 8 tasks
foxnne added a commit that referenced this pull request Oct 16, 2026
Part of fizzyedit/plans#5 (`plans/ZIG_0_17_PLAN.md`, step 2: the build
system). **Stacked on #438** (and #437): based on `zig/017-build-steps`,
so this diff is only release packaging.

## What changes

**Velopack 1.2.161.** `velopack_zig` moves to graphl-tech/velopack-zig#9
(`a80b037`, its Zig 0.17 port), and with it Velopack's runtime goes from
0.0.1589 to 1.2.161. `vpk` in `.config/dotnet-tools.json` goes from
0.0.1298 to 1.2.161; `vpk` 1.2 targets .NET 10, so `release.yml`
installs 10.0, not 8.0. What users gain is in velopack-zig#9: a code
signature that survives updates on macOS, atomic macOS updates, the
Linux locator fix, delta fixes, and 64-bit Windows bootstrappers.
`Velopack.h` only gained declarations; fizzy's updater is unchanged.

**The rest of the build system's 0.17 stand-ins:**
- **`Velopack.h` as a module.** `@cImport` is gone.
`velopack.linkVelopack(.c_modules = …)` gives each module that calls the
C API a `velopack_c` import (translate-c), once per module: the
integration, demo and bench tests share one `app` module. The updater
imports it only when Velopack is enabled.
- **`vpk pack` runs through velopack-zig's `tools/run_env.zig`**
(`velopack.addVpkRun`), which sets `DOTNET_ROLL_FORWARD` and puts the
bundled `mksquashfs` on `PATH` when vpk runs. So does the MSVC setup
script. In 0.17, `Run.setEnvironmentVariable` freezes the configure-time
environment into the cached configuration, and `Run.addPathDir` is gone.
`MksquashfsBuild.bin_dir` is now `mksquashfs`'s own output directory,
with no install step.
- **Signing and the macOS SDK are options only.** `-Dmacos-sign-app`,
`-Dmacos-sign-installer` and `-Dmacos-notary-profile` lose their
`FIZZY_MACOS_*` environment fallbacks, and `SDKROOT` becomes
`-Dmacos-sdk`: 0.17 caches the configuration without regard to the
environment. `scripts/release.sh` and `release.yml`'s macOS job turn the
same variables into the options, and `packageall` forwards them to its
macOS children.
- **No global `--libc`:** `-Dwindows-msvc-libc` or the managed
`.velopack-msvc/` only. The managed manifest's existence and an explicit
manifest's contents are declared to the configure cache.
- **The MSVC translate-c shim works again.** It finds each module's
translate-c step through the generated-file index and `Step.cast`, as
`std.Build` does, in place of the removed `Step.id`.
- **`docs/PITFALLS.md`, "The build (Zig 0.17)":** the four traps
(environment reads, `setEnvironmentVariable`, undeclared configure-time
reads, declared paths in missing directories), each with what fizzy does
instead.

After this, 36 `ZIG017` markers remain on the branch, none in the build
system: the zon stand-ins, the `@typeInfo` helpers, the example-branch
switch.

## SDK impact

- [x] None: no `sdk/` file changes here.
- [ ] Core-only or additive: reaches plugins at the next SDK release
- [ ] Fingerprint moved
- [ ] SDK release

## Verified

- [x] macOS (arm64, 0.17.0):
- `zig build package` packs a release with vpk 1.2.161:
`fizzy-arm64-macos-Setup.pkg`, `-Portable.zip`,
`fizzy-0.2.0-arm64-macos-full.nupkg`. The bundle carries
`Contents/Resources/sq.version`, 1.x's signature-preserving layout.
Unsigned: no identities on this machine.
- `zig build -Dvelopack=true` links Velopack (36 `vpkc_` symbols); `zig
build test-integration -Dvelopack=true` (79/79 steps).
- `scripts/check-fmt.sh`, `zig build`, `zig build test`, `zig build
test-integration` (77/77 steps), `zig build check-web`, `zig build
test-sdk-version`.
- `scripts/check-examples.sh` against the examples' `zig-0.17` branches:
all 8 builds pass.
- `zig build -Dtarget=x86_64-windows-msvc` configures;
`-Dtarget=x86_64-windows-gnu -Dno-emit` builds.
- [ ] Windows: not run. The MSVC path (`.velopack-msvc/`, the shim)
needs a packaging run.
- [ ] Linux: not run. The AppImage path is velopack-zig#9's `run_env
--path`, which its CI passes on `ubuntu-latest`.
- [ ] **A test release.** `release.yml` packaging all six targets,
signed, is the real check for this PR. It has no dry-run mode: it
releases an existing tag and stops at a *draft* GitHub release, which
nothing auto-updates to until it is published. So the check is a test
tag on this branch (e.g. `v0.2.0-zig017.1`), the workflow's draft
release, then deleting both. Tagging is the maintainer's call.
- [ ] An update round trip, from an installed 0.0.1589-runtime release
to one packed with this, per OS. It waits for that dry run's artifacts.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
foxnne added a commit that referenced this pull request Oct 16, 2026
## What changes

`sdk_version` goes from 0.2.27 to 0.2.28. Merging this tags
`sdk-v0.2.28` and publishes its tarball.

**Why now:**
- **Agents:** fizzyedit/agent#16 (an agent's edits don't lock the
document, and a document an agent makes opens beside the person) needs
#442's `OpenOptions.beside`.
- **Pixi's CI:** fizzy `main` dropped `setExplorerNewFilePath` in #449,
so every pixi PR fails the `fizzy-main` check until pixi repins to a
release that has `files.reveal`.
- **Untitled sprites:** #447's `loadUntitledDocument` lets a plugin's
untitled documents come back from a crash. Pixi needs a release carrying
it to implement that.

## SDK impact

- [ ] None
- [ ] Core-only or additive: reaches plugins at the next SDK release
- [ ] Fingerprint moved: recorded in `sdk/src/version.zig`,
`sdk_version` untouched, PR labelled `sdk`
- [x] SDK release: bumps `sdk_version`, lists the `sdk` PRs since the
last `sdk-v*` tag

**`sdk` PRs since `sdk-v0.2.27`:**
- **#442:** a plugin can open a document beside the person.
`EditorAPI.OpenOptions.beside` (one new defaulted field), and
`Host.openBeside` / `endOpenBeside`, so a command run on someone's
behalf opens what it makes beside them.
- **#447:** an untitled document survives a crash or a restart. A new
optional vtable member, `loadUntitledDocument`, for an owner whose
untitled documents should come back. `Plugin.bytesPath` is the path to
hand `documentBytes` for a document's bytes outside a save.
- **#456:** owners report a document's steps, and apply recorded ones
(`Step`, a vtable `step_format` and `applyStep`, Host events). All
additive: `step_format` defaults to 0 and `applyStep` to null, so an
owner that records nothing changes nothing.
- **#449:** the file tree keeps its own folders, and `files.reveal`
shows new files. `Host.setExplorerNewFilePath(path)` becomes
`host.getServiceTyped(sdk.services.files.Api)` then `.reveal(path)`. The
`files` service is now version 2. `explorerBranchIsOpen` and
`setExplorerBranchOpen` are gone, with no replacement.

**Reaching plugins, no fingerprint move:**
- **#444:** `core`: a canvas the person hasn't moved stays fitted to its
pane, refitting live as the pane changes size. `fitToView` animates
there.
- **#446:** `core`: `core.widgets.glass_button`, round glass buttons and
viewport bubbles.
- **#450:** `core`: a kept frost reads again under a region's swap.
- **#451:** `core`: a row carried out of a tree is the OS's glass, and
stays still.
- **#453:** `tape`: steps of a document's history, and a compressed
binary form. **`tape.binary.encode` takes a third argument, `Options`**
(`.{}` keeps the old output).
- **#457:** `tape`: a live tape with no ops finishes instead of
crashing.
- **#424:** `core`: the glass shader written once in HLSL, its Metal and
GLSL generated.
- **#458:** `sdk/src/dylib.zig`: a refused plugin says whether its build
mode or its SDK shape differs (it blamed the mode for both).
- **#435:** `tape`, `replay`: `builtin.target.os`, `.cpu` and `.abi` in
place of `builtin.os`.

Not in this release: #437 (the Zig 0.17 switch, labelled `sdk`) merged
into the `zig/0.17` integration branch, not `main`. `main` still builds
with Zig 0.16.

## Verified

- [x] macOS:
- On this head, `zig build test-sdk-version` passes, and
`scripts/pack-sdk.sh` packs `fizzy-sdk-v0.2.28.tar.gz`.
- Rebased on `main` after #456, #453, #457, #424 and #458. Re-checked on
the new head: `zig build test-sdk-version` passes and
`scripts/pack-sdk.sh` packs.
- fizzyedit/agent `main` doesn't build against it:
`src/Recording.zig:127` calls `tape.binary.encode(gpa, t)`, and #453
added an `Options` argument (its notes say no plugin calls it; the
agent's recording does). The fix is `encode(self.gpa, t, .{})`, in the
agent's repin.
- fizzyedit/pixi `main` doesn't build against it:
`src/dialogs/NewFile.zig:230` calls the removed
`setExplorerNewFilePath`. Expected; see Follow-ups.
- [ ] Windows:
- [ ] Linux: CI.
- [ ] Web:

## Follow-ups

- The repin automation opens `sdk: repin to fizzy SDK 0.2.28` PRs in the
store plugins whose build needs it.
- **pixi's** opens as a draft, since its build breaks. It needs #449's
change: `NewFile.zig` calls `files.reveal`. #449's notes say that change
is ready locally, to push with this repin. **atlas** needs the same, for
New Note.
- **agent's** repin opens as a draft too: it needs
`tape.binary.encode(self.gpa, t, .{})` in `src/Recording.zig` (#453).
- **Then:** once agent repins, fizzyedit/agent#16 can go ready, and pixi
can implement `loadUntitledDocument`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
foxnne added a commit that referenced this pull request Oct 17, 2026
Part of fizzyedit/plans#5 (`plans/ZIG_0_17_PLAN.md`, step 2, follow-up
(c): `@typeInfo`, with the fingerprint-equality test). Stacked on #486.

## What changes

- **The settings and command schema builders read 0.17's `@typeInfo`
directly.** `sdk/src/settings.zig` and `sdk/src/Command.zig` loop over
`field_names`, `field_types` and `field_attrs`, and take a cell's
default with `FieldAttributes.defaultValue`.
- This replaces the `ZIG017` stand-in that rebuilt 0.16-shaped field
records. `sdk/src/zig017_fields.zig` is deleted.
- The bare-payload structs (`Plain`) pass `@typeInfo`'s own name slice
to `@Struct` instead of copying it.
- **The proof the plan asked for.** `fingerprint.zig` was translated
loop for loop in #437, and this adds the check: a test hashing a fixed
set of types, one of each kind the hashes look into, at a depth that
reaches them all. Both hashes must equal the values **Zig 0.16**
computed for the same types, with the file as it was on `main`
(7b538aa):
- `hashAllShape`, the target-invariant hash that's recorded:
`0x2ae9f3848d7d3d9f`.
- `hashAll`, on fixed-width types only, since it folds in sizes:
`0xc0b59f8f6303fb84`.
- The 0.16 run used `main`'s file with Zig 0.16.0 and the 0.17 run uses
this branch's with Zig 0.17.0; the values match.

`ZIG017` markers left on the branch: the two per-pointer-capture notes
(dvui#1014) and `check-examples.sh`'s branch pins, which go back to
`main` at the merge.

## SDK impact

- [x] None. `zig build test-sdk-version` passes with the fingerprint
#486 recorded: the schema shapes plugins see are unchanged.

## Verified

- [x] macOS (arm64, Zig 0.17.0):
  - `scripts/check-fmt.sh`
  - `zig build`
  - `zig build test`: 95/95 steps
  - `zig build check-tests-reached`: 120/120
  - `zig build check-web`: 41/41
  - `zig build test-sdk-version`: 35/35
  - `zig build test-integration`: 77/77 steps
- [ ] Windows, Linux: CI.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@foxnne foxnne mentioned this pull request Oct 18, 2026
3 of 4 tasks
foxnne added a commit that referenced this pull request Oct 18, 2026
Part of fizzyedit/plans#5 (`plans/ZIG_0_17_PLAN.md`, step 2): fizzy, its
backend and its bundled plugins move to Zig 0.17. Every piece was
reviewed as its own PR into `zig/0.17`, and this PR lands that branch.
`main` is already merged in (through #555), so it carries no conflicts.

## What changes

**The move to 0.17** (`minimum_zig_version = "0.17.0"` everywhere), as
reviewed in:
- #437: the switch, scaffolded with marked stand-ins
- #438: the build's custom steps become helper programs
- #448: release packaging on 0.17, with Velopack 1.2.161 (+ #452's zon
arenas)
- #488: settings and command schemas read 0.17's `@typeInfo`, with a
test that the SDK fingerprint hashes the same facts as on 0.16
- #489: per-pointer capture through `captureMouseGet`
- #494: the Windows traps in `docs/PITFALLS.md`
- #496: `blitData` without an `extern struct` `@bitCast`
- #498: `fizzy.plugin.installBeside`, for a plugin's helper program
- #505: the 0.16 inflate workaround goes; the docs say 0.17
- #561: review fixes. A settings payload with pointers is a compile
error, and build reads go through `b.root`.
- #573: the plugin install helper compiles once, in Debug
- the catch-ups with `main` (#486, #503, #558, #574, and the last merge
here), each with `scripts/zig-0.17/rewrite.sh` re-run. That script is
deleted in the last commit.

Most of the diff is mechanical: `zig fmt`'s upgrades (`@backingInt`,
`std.lang`, …) and one-answer renames. The hand migrations are the build
(configure/make split, no custom steps), the zon arenas, `@typeInfo`,
translate-c for `@cImport`, and the `@bitCast` audit. A high-effort
review of the whole `main`…`zig/0.17` diff found five things; the three
correctness ones are fixed in #561, and one efficiency one in #573.

**Also on the branch, merged there by the maintainer:** the windowing
work.
- #480–#483: plain dvui apps on macOS
- #492, #501, #504, #510: OS windows for floating windows, full screen,
pointer routing
- #546: window materials

**Forks:** dvui-dev at its `zig-0.17` head `1b29a16`, which carries
fizzy-dev's patches on upstream's 0.17 `main`, including #540, #544 and
#563's dvui commits. Also assetpack, md4zig, sdl_zig and zig-objc on
their `zig-0.17` branches, and velopack-zig `3d553b7` (tagged
`fizzy-zig0.17`).

## Measured

macOS arm64, under load from other sessions, each step alternated
between the two. 0.16 is `main` #509; 0.17 is `zig/0.17` with #561.

| | Zig 0.16 | Zig 0.17 |
|---|---|---|
| App, cold Debug build | 94.4 s | 78.6 s |
| App, warm no-op rebuild | 0.7 s | 0.4 s |
| `test-integration` (compile + run) | 108.9 s | 90.4 s |
| App, ReleaseFast build | 142.8 s | 131.5 s |
| Image plugin, cold standalone build | 11.6 s | 14.0 s (with #573) |
| Startup, Debug (median of 5) | 565 ms | 466 ms |
| Startup, ReleaseFast (median of 5) | 175 ms | 177 ms |
| Text, ReleaseFast, µs/frame: idle / mid / scroll / typing | 305 / 424
/ 435 / 403 | 240 / 306 / 286 / 290 |
| Markdown, ReleaseFast, 4 docs, median of 5, µs/frame: idle top / mid /
scroll / resize | 1177 / 1735 / 2104 / 1319 | 1270 / 1699 / 1833 / 1192
|

The one slowdown is markdown idle at the top of a document, +8%. The
rest of the plugin build's gap is dvui's own translate-c on 0.17.

## The plan's gate

- **ZLS:** no 0.17 release yet (latest 0.16.0). zigtools/zls#3270 is on
ZLS's master. Completions across `fizzy_sdk` from a plugin are not
checked.
- **Minimum OS:** 0.17 needs Darwin 15+ (OS X 10.11); fizzy's macOS
minimum is 11.0, so no change. Windows 10+ and Linux 5.10+ are not
stated by fizzy.
- **Debug leak reports:** still print (`DebugAllocator`, deprecated,
unchanged). The move to `SafeAllocator` is a follow-up with its own
Debug frame-time check.
- **Velopack:** packaging proven by a test release on macOS (signed and
notarized), Windows and Linux. An update in place was not tested.
- **Every target builds:** in CI. Windows and Linux were not run by hand
on the VMs.

## SDK impact

- [x] Fingerprint moved (`0x5f9c818a6972efd4`, recorded in
`sdk/src/version.zig`); `sdk_version` untouched. `sdk: release 0.2.30`
follows this at once. Plugins need Zig 0.17 to build against it: there's
no overlap window.
- Plugin authors see `minimum_zig_version = "0.17.0"` and
`fizzy.plugin.installBeside`. A settings payload holding pointers other
than a string is now a compile error. `docs/PLUGINS.md` says 0.17.0.

## Right after this merges

1. fizzyedit/example-app#8 and fizzyedit/example-plugin#13 merge. Then a
one-line PR points `scripts/check-examples.sh` back at `main` (it pins
the examples' `zig-0.17` branches until then, so `main`'s CI is never
red).
2. `sdk: release 0.2.30`.
3. The store plugins' 0.17 PRs repin and release: fizzyedit/pixi#51,
zig#15, atlas#16, drive#13, ghostty#14, agent#23, chat#16.

## Verified

- [x] macOS (arm64, Zig 0.17.0), on this tree:
  - `scripts/check-fmt.sh`
  - `zig build`: 71/71
  - `zig build test`: 109/109 steps, 637/638 (1 skipped)
  - `zig build check-tests-reached`: 998 tests in 122 files
  - `zig build check-web`: 42/42
  - `zig build test-sdk-version`: 35/35
  - `zig build test-integration`: 78/78 steps, 608/608
- [x] All seven store plugins built against `zig/0.17`'s `sdk/` and
loaded together in a 0.17 fizzy (Debug, sandbox profile).
- [ ] Windows, Linux, the example repos: CI.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

sdk Moves the plugin boundary's fingerprint; ships at the next SDK release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant