# Cap decompressed project size Branch: `limit-decompression` → `main` PR 2 of the review fixes plan (`docs/review-fixes-plan.md`). Part of 0.5.2; the version does not change in this PR. ## What changed **Projects over 256 MB decompressed are refused (S2).** `xpj.Read` used to read the whole gzip stream into memory, however large it was. In the review, a 1 MB file padded with spaces used 3.1 GB and took 10 seconds. `Read` now reads through `io.LimitReader` and stops one byte past the new `xpj.MaxDecompressed`, 256 MB. Past that, it returns `xpj: project is larger than 256 MB when decompressed`. A comment on the constant gives the reason. **The limit is 256 MB, not the plan's 512 MB** (bajdev, 2026-11-12, during this PR). The limit counts only the project file once it is unpacked: the pads, the pool's list of file names and the sequences with their events. WAV files are separate files in the data folder and are not read through `xpj`. - A blank project is about 3 MB, almost all of it the 128 empty pad definitions. - Each recorded event takes about 480 bytes, and an empty sequence about 12 KB. - So 256 MB holds about half a million events, around 4,000 for each of 128 sequences. DemoFile has 225 events in total. The Decisions table and PR 2's tasks in `docs/review-fixes-plan.md` now say 256 MB. **Test.** `TestDecompressionLimit` builds its gzip streams in memory with `gzip.NewWriter`; nothing new is committed: - A minimal project padded with spaces to one byte over the limit is refused with that exact error. - The same project padded to exactly the limit still decodes. - Against the old `Read`, the over-limit case loads without error, so the test fails. - It takes about 3 seconds, and `go test -short` skips it. ## Notes **Memory near the limit, as measured on the server:** - A file padded to 3.1 GB (6 MB compressed) is now refused after 0.3 seconds, at a peak of about 600 MB. The buffer `io.ReadAll` grows can pass the limit before the check runs. - A file exactly at the limit still loads, at a peak of about 925 MB, because the JSON decoder keeps its own copy of the padding. - At 512 MB, these were about 1.3 GB and 2.3 GB. **The test needs memory:** about 860 MB while it runs. `go test -short ./...` skips it. ## Testing **On the server:** - `go test ./...` passes, including the round-trip tests, unchanged. - `go vet ./...` and `staticcheck ./...` are clean. - `govulncheck ./...` reports only GO-2026-6859 in `net/http`, which waits for Go 1.27.4 in PR 4. - `npm install`, `npm run check` (0 errors, 0 warnings) and `npm run build` pass. The lockfile `npm install` wrote was not committed. - `wails3 build` builds, and `timeout 15 xvfb-run -a bin/mpckit` starts and runs. The log holds only the usual headless GTK warnings. **On the laptop (PowerShell, in the repo):** 1. `git fetch; git checkout limit-decompression` 2. `go test -run TestDecompressionLimit -v ./internal/xpj`: `PASS`, in a few seconds. It uses about 900 MB of memory. 3. `go test ./...`: all `ok`. 4. `wails3 dev`: load `testdata\device\DemoFile.xpj` and `testdata\accepted\MpckitT1.xpj`. Both open as before.