Repository navigation
app: fizzy times its own startup, and a handover's - #365
Merged
Merged
Conversation
fizzy stamps the moments its start takes (core.profile.phase): main, the single-instance lock, the window, the editor, the plugins, the window shown, the first frame; and for an instance a restart started beside the old one, the handover's ready and lock. The first frame logs how long startup took, and the profiler's report has them on request (ReportOptions.startup). Measured by the app itself on every platform, where a restart's gap was measured from outside with a window sampler. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
foxnne
added a commit
that referenced
this pull request
Oct 11, 2026
## What changes `sdk_version` goes from 0.2.22 to 0.2.23. Merging tags `sdk-v0.2.23`, publishes its tarball, and asks the store plugins to repin. **Why now.** The plugin boundary's fingerprint moved since `sdk-v0.2.22` (`0x1bf417f6…` → `0x65096e94…`, by #360), so store plugins built against 0.2.22 don't load in a fizzy built from `main`, and fizzyed.it/app is built from `main`. Also, #355 moved the `plugins` service to version 2, and the agent plugin's `fizzy_build` asks for version 1 until it repins. ## 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 **The `sdk` PRs since `sdk-v0.2.22`:** - #360: a plugin's panic message reaches its crash report. This moves the fingerprint. **Additive, reaching plugins with this release:** - #355: the `plugins` service is version 2, with `list` (each plugin's identity, link, version, registration counts and status). A caller built for version 1 is refused until it rebuilds, which this release's repin does. - #351: a crash report says whose code crashed (`sdk/plugin_sdk.zig`). **Also reaching plugins (`core/`):** - `core.viz`, graphics for live numbers: `line`, `bar`, `stat`, `Table` (#345); `Pie`, `keyColor`, `palette` (#347); the table's rounded header and shaded rows (#359). - `core.profile`: - `lookback()`: 30 s of costs and the frames over budget, plus `ReportOptions.over_ms` / `.hitches` and `selfTimes` (#353). - `frameCauses()` and `ReportOptions.causes`: why each frame happened (#364). - `phase` / `Phases` / `ReportOptions.startup`: startup timing (#365, which landed inside #364's squash commit). - The profiler's `abi` stays 4. ## Verified - [x] macOS: - `zig build` and `test-sdk-version` on this head. - `scripts/pack-sdk.sh` packs `fizzy-sdk-v0.2.23.tar.gz`. - Every store plugin (fresh pulls of pixi, zig, drive, atlas and ghostty `main`) builds against this tree's `sdk/` with `zig build --fork=<sdk>` into a throwaway profile (31/31, and 28/28 for each of the other four). The fingerprint moved, so each repin PR is a real rebuild, but none should open as a draft for a broken build. The agent and chat plugins build too (22/22, 19/19). - [ ] Windows: - [ ] Linux: CI. - [ ] Web: ## Follow-ups - Each repin PR merged and its plugin tagged (the plugin repos' tags stay the maintainer's). - `fizzyedit/agent` and `fizzyedit/chat` repin to the `sdk-v0.2.23` tarball. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
3 of 4 tasks
foxnne
added a commit
that referenced
this pull request
Oct 13, 2026
Part of `plans/RESTART_AND_CRASHES_PLAN.md` (open items); the gap was found with the startup phases from #365. ## What changes **The window never appears empty.** fizzy showed its window at the end of `AppInit`, before the first frame. On a launch it appeared empty for the ~80 ms that frame took. In a restart's handover (#356), the new window covered the old one before it had drawn anything. Now: - **Shown unseen** (`platform.window.showUndrawn`). A hidden window gets nothing to present into, so it can't simply draw first and show afterwards. - **macOS:** shown fully transparent (`SDL_SetWindowOpacity` 0, `alphaValue`). - **Windows:** shown cloaked (`DWMWA_CLOAK`): shown and composited, but not on screen. That avoids SDL's opacity, which would make the window layered, which `win32_titlebar` already rules out (it breaks native dialogs' input). - **Elsewhere:** shown as before. - **The first frame** is drawn into it and presented. It then asks for the next frame, so an idle launch is still revealed. - **Revealed** as the next frame begins (`platform.window.reveal`). The `shown` phase now comes after `first frame`, and the log says `startup: shown N ms after main`. - **A handover's new instance** signals "ready" only once its window is on screen. The old instance quits only after that, so the overlap is guaranteed by the order. - Moving "ready" after the first frame exposed a bug: `restart.pollReleased` ran in the new instance's first frame, with its wait deadline still zero. It deleted the handover folder and took the lock while the old instance still held it, so the new instance handed its own launch over and exited. Polling now starts only once "ready" has been signalled. ## SDK impact - [x] None ## Verified - [x] **macOS:** - **Presented at opacity 0:** the backend's `Health` counters show the first frame was presented while the window was transparent (presents 0 → 1 between the first frame's start and the reveal, skips unchanged). So what is revealed has been drawn. - **A handover's new instance**, its phases read through the agent: `first frame` at 525.5 ms, `shown` 541.7, `handover ready` 541.7, `handover lock` 566.0. Two handovers in a row each came up as a new instance that answered. - **The fix above** was found by a handover that stalled for 30 s: the new instance exited about 300 ms in, and the old one gave up. - **Gates:** `zig build`, `zig build test` (541/542, 1 skipped), `test-integration` (383/383), `check-web`, `test-sdk-version`. - [x] **Windows,** Windows 11 ARM64 VM, an `aarch64-windows-gnu` build launched in the user's session, sampled every 50 ms: shown and cloaked at 3852 ms, uncloaked 136 ms later. It is never left cloaked. - [ ] Linux: CI. The window shows as before there. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of
plans/DASHBOARD_PLAN.md, "What performance work lacked": startup and handover phases. Stacked on #364 (it shares the report code); retarget tomainonce that merges.What changes
fizzy times its own startup.
core.profile.phase(io, name)stamps a moment of the app's start. fizzy stamps:main,lock(the single-instance lock),window(the backend up: GPU, fonts),editor,plugins,shown, andfirst frame;lock deferred (handover),handover readyandhandover lock.Each run's first frame logs one line:
The phases in the report:
core.profile.reportincludes them with.startup = true, and they're published likeLookbackandFrameCauses: their own data key, no change to the profiler's layout orabi. The new instance of a handover, read that way:It already shows something to act on: the window is shown about 80 ms before its first frame is drawn. That's true of every launch, and in a handover the new window covers the old one for those 80 ms before it has drawn anything. A follow-up can show it after the first frame.
Measuring a restart used to take a window sampler and a pid poller running outside fizzy, on macOS only.
SDK impact
core.profile.phase,phases,Phases,ReportOptions.startup. No fingerprint move.Verified
main: the clock went throughdvui.io, which the backend hadn't set yet. Sophasetakes theio.zig build,zig build test(533/534, 1 skipped),test-integration(375/375),check-web,test-sdk-version.Follow-ups
proc_pidinfo, WindowsGetProcessTimes, Linux/proc/self/stat), somainisn't time zero and dyld and static initialisation are counted.fizzy_buildon fizzy's repo reports the new instance's phases, once the SDK is released and the agent repinned.🤖 Generated with Claude Code