Skip to content

Image tooling and prep for the v6 disk image release#382

Merged
felixrieseberg merged 6 commits into
mainfrom
v6-image-tooling
Jun 4, 2026
Merged

Image tooling and prep for the v6 disk image release#382
felixrieseberg merged 6 commits into
mainfrom
v6-image-tooling

Conversation

@felixrieseberg

@felixrieseberg felixrieseberg commented Jun 3, 2026

Copy link
Copy Markdown
Owner

What this does

Prepares the app for the v6 disk-image release: a much smaller images zip (298 MB → 153 MB), a regenerated default state, and the tooling/docs that came out of building it.

The image and default-state binaries themselves are not in this PR — they ship via the private images release (images_v6_slimmed.zip, built with tools/pack-disk.sh).

What changed

STATE_VERSION 5 → 6 (src/constants.ts). The v6 image has different content than v5, so old saved states must go through the legacy recovery flow instead of resuming over the new disk.

Probe harness: state-restore mode (src/renderer/emulator.tsx). WIN95_PROBE_RESTORE=1 tools/probe-boot.sh boots the app and restores the saved/default state instead of cold-booting, so a freshly generated default-state.bin can be verified headlessly. Refuses to boot if there is no state file, so a missing state cannot pass as a false SUCCESS. This is now part of the release checklist.

tools/zero-free-clusters.py: zeroes free clusters directly (FAT untouched) so the packed zip compresses better — the only safe way to do this; the classic "mcopy a giant zero file, then delete it" trick deterministically breaks Win95 cold boot in v86. Currently shelved along with all offline image modification (see below).

Docs/skills. docs/qemu.md now documents the recommended image-cleanup workflow: do it inside Windows (Explorer delete + ScanDisk + clean shutdown), verify with the probe, ship the image untouched. Offline modification (mtools) measurably worsens v86 cold-boot reliability — Win95 can hit "Invalid VxD dynamic link call" on cold boot in v86 while the same image boots fine in QEMU and passes fsck. The probe skill gains a "VXDLINK: flake vs. real bug" section with the canonical verdict-interpretation policy: cold boot fails sporadically (~1 in 2–3 attempts) on any image; one SUCCESS means the image is good, three identical failures mean the image content must change.

How this was verified

  • Cold-boot probes (tools/probe-boot.sh) of the v6 image
  • Restore-path probes (WIN95_PROBE_RESTORE=1) of the new default-state.bin — passes every run
  • yarn run tsc and prettier pass

After merge

  1. Upload images_v6_slimmed.zip to the private images release and point DISK_TAG at it
  2. Cut the app release
  3. Longer term: fix the cold-boot bug in the v86 fork — it would also fix "Boot from scratch" being flaky for end users

WIN95_PROBE_RESTORE=1 makes the probe harness restore the saved/default
state instead of cold-booting, so a freshly generated default-state.bin
can be verified headlessly. Also document when to retry probe failures
versus treat them as real.
tools/slim-disk.sh deletes dead content (online-service installers, MSN
setup cabinets, uninstall backups, NetMeeting, caches) from the disk
image via mtools; tools/zero-free-clusters.py zeroes free clusters
without touching the FAT so the packed zip compresses better.

Both are currently shelved: offline modification of the image worsens
v86 cold-boot reliability. The failure modes, reliability data, and a
repro kit are in docs/v86-cold-boot-bug.md, and docs/qemu.md now
describes the recommended in-Windows cleanup workflow instead.

Also git-ignore packed images_*.zip release artifacts.
The v6 images release ships a cleaned-up disk and a freshly generated
default-state.bin. Old state-v5 snapshots reference the previous disk
layout and must go through the legacy recovery flow instead of resuming
over the changed disk.
- emulator: initialize isBootingFresh to false (bootFromScratch sets it
  itself); in restore-probe mode, refuse to boot when there is no state
  file so a missing default-state.bin cannot pass as a false SUCCESS;
  split getStateFilePath() out of getState() so the existence check does
  not read the whole state file
- zero-free-clusters: read the partition offset from the MBR instead of
  hardcoding it, and coalesce free-cluster runs into large reads/batched
  writes (minutes -> under a second on a ~700 MB free region)
- slim-disk: collapse the vestigial tier arrays into DELETE_DIRS /
  DELETE_FILES / EMPTY_DIRS (the empty-and-recreate dirs were listed
  twice), document --no-zerofill, drop the duplicated post-run checklist
- constants: correct the STATE_VERSION comment - legacy-state recovery
  extracts files to a host folder, it does not produce a mountable .img
- docs/skills: state the probe retry/ship policy once (probe-win95 skill)
  and link to it from qemu.md and the bug doc; renumber the inspect-disk
  warnings and fix its stale slim-disk recommendation

Verified: tsc, prettier, dry-run parity (41 targets), and live probes of
both boot paths (cold boot + state restore) after the changes.
Fold the essential facts (known triggers, QEMU-boots-but-v86-doesn't
symptom, retry policy) into the probe skill's "VXDLINK: flake vs. real
bug" section, which the other docs already point at, and drop the
standalone document.
The offline-deletion workflow is not recommended (it worsens v86
cold-boot reliability), so don't keep a tool whose own header says not to
run it. The in-Windows cleanup checklist in docs/qemu.md covers what it
deleted; tools/zero-free-clusters.py stays as the only safe way to zero
free space if that is ever needed. Also drop a redundant comment on
isBootingFresh.
@felixrieseberg felixrieseberg merged commit 61a1d71 into main Jun 4, 2026
8 checks passed
@felixrieseberg felixrieseberg deleted the v6-image-tooling branch June 4, 2026 02:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant