chore: the toolbar icons ship as opaque binaries with no in-tree way to regenerate them #378

Closed
opened 2026-08-23 21:26:29 +02:00 by clawbot · 4 comments
Collaborator

Follow-up from #371, filed now rather than when the mark first needs changing — the reproduction recipe currently lives only in a commit message, which is exactly where it will be missed.

#376 ships four PNGs (16/32/48/128) produced by a throwaway program that was deliberately not committed. The geometry is fully documented in that commit — both hex colours, the six-coordinate construction, 8x8 supersampling — and the reviewer independently confirmed the files are hand-encoded (only IHDR/IDAT/IEND chunks, filter type 0 on every scanline, no tool metadata or ICC profile). So nothing is lost today.

But the mark cannot currently be modified or reproduced without rewriting the generator from prose, and "placeholder-grade branding on a pre-release" is exactly the kind of thing that quietly becomes permanent.

Definition of done

  • An in-tree generator under script/, in the repo's own language rather than a second one, reproducing the four committed PNGs byte for byte from the documented geometry.
  • A test asserting regeneration matches the committed bytes, so the source and the artefact cannot drift.
  • No new external dependency, and no network fetch — the current files were produced with a hand-rolled encoder precisely to avoid both, and REPO_POLICIES.md forbids an unpinned external reference with zero exceptions.
  • make check green.

Two smaller notes from the same review, worth folding in while someone is in this code:

  • A file named icons/icon48.png that is not actually a PNG passes both make build and make package — only make check's IHDR assertion catches it. Existence is validated; content is not. Consider validating content where the reference is resolved.
  • build.js names the two manifest source paths twice — in the new MANIFEST_SOURCES map and inline in the existing manifest-copy block. Harmless today, but if one moves, copyIcons() could read a different manifest than the one that ships.
Follow-up from https://git.eeqj.de/sneak/AutistMask/issues/371, filed now rather than when the mark first needs changing — the reproduction recipe currently lives only in a commit message, which is exactly where it will be missed. https://git.eeqj.de/sneak/AutistMask/pulls/376 ships four PNGs (16/32/48/128) produced by a throwaway program that was deliberately not committed. The geometry is fully documented in that commit — both hex colours, the six-coordinate construction, 8x8 supersampling — and the reviewer independently confirmed the files are hand-encoded (only IHDR/IDAT/IEND chunks, filter type 0 on every scanline, no tool metadata or ICC profile). So nothing is lost today. But the mark cannot currently be modified or reproduced without rewriting the generator from prose, and "placeholder-grade branding on a pre-release" is exactly the kind of thing that quietly becomes permanent. ## Definition of done - [ ] An in-tree generator under `script/`, in the repo's own language rather than a second one, reproducing the four committed PNGs **byte for byte** from the documented geometry. - [ ] A test asserting regeneration matches the committed bytes, so the source and the artefact cannot drift. - [ ] No new external dependency, and no network fetch — the current files were produced with a hand-rolled encoder precisely to avoid both, and `REPO_POLICIES.md` forbids an unpinned external reference with zero exceptions. - [ ] `make check` green. Two smaller notes from the same review, worth folding in while someone is in this code: - A file named `icons/icon48.png` that is not actually a PNG passes both `make build` and `make package` — only `make check`'s IHDR assertion catches it. Existence is validated; content is not. Consider validating content where the reference is resolved. - `build.js` names the two manifest source paths twice — in the new `MANIFEST_SOURCES` map and inline in the existing manifest-copy block. Harmless today, but if one moves, `copyIcons()` could read a different manifest than the one that ships.
Author
Collaborator

Plan: a short node script under script/ that draws the mark from the geometry recorded on #376 and writes the PNGs with node's own zlib, plus a jest test comparing its output with the committed files byte for byte. No new dependency. If the recorded geometry cannot reproduce the exact bytes, the worker reports what differs here instead of changing the icons or loosening the test.

Model: opus-5-5

Plan: a short node script under `script/` that draws the mark from the geometry recorded on https://git.eeqj.de/sneak/AutistMask/pulls/376 and writes the PNGs with node's own zlib, plus a jest test comparing its output with the committed files byte for byte. No new dependency. If the recorded geometry cannot reproduce the exact bytes, the worker reports what differs here instead of changing the icons or loosening the test. Model: opus-5-5
Author
Collaborator

Stopped before a PR. The drawing reproduces the committed icons exactly, but node's own zlib cannot reproduce their compressed bytes, so the byte-for-byte test cannot pass unless the icons or the test change. Both are your call.

What matches. Every pixel at all four sizes, and the IHDR. The coordinates were never recorded on #376, so I read them back out of the PNGs:

  • rounded square, corner radius 0.22 of the side
  • outer triangle (0.5, 0.15), (0.115, 0.855), (0.885, 0.855)
  • inner triangle cut out of it (0.5, 0.40), (0.29, 0.70), (0.71, 0.70)
  • per pixel, one sample at the centre of each cell of an 8x8 grid; colour and alpha rounded half to even

What differs. Only the compressed image data (IDAT). The committed files are stock zlib 1.3.1 at level 9. Node 22.22.0 ships a modified zlib (1.3.1-470d3a2) whose level-9 output is 5 to 23 bytes longer per file, and no level, memory level or strategy node offers gives the committed bytes.

What is missing: your decision.

  • (a) Keep the four files, and have the test compare the decoded image exactly (header and every pixel) instead of the compressed bytes.
  • (b) Re-encode the four files once with node's zlib (same pixels, slightly larger files) and keep comparing bytes.

Recommendation: (a). Under (b), make check depends on which zlib build the running node carries: the container's node and a host's node can compress differently, so make icons on a host could write files the container's test rejects, and a node update could turn make check red with the picture unchanged. (a) has one wart: the first make icons rewrites the four files with node's compression, same pixels.

The generator (script/lib/icons.js), its byte-for-byte test (red on exactly this), make icons and its README line are on branch issue-378-icon-generator: https://git.eeqj.de/sneak/AutistMask/src/branch/issue-378-icon-generator. No PR opened; the committed icons are unchanged. Of the two smaller notes, the duplicated manifest paths in build.js are folded in. The build-time PNG check is not: make check already catches a non-PNG icon.

Model: opus-5-5

Stopped before a PR. The drawing reproduces the committed icons exactly, but node's own zlib cannot reproduce their compressed bytes, so the byte-for-byte test cannot pass unless the icons or the test change. Both are your call. **What matches.** Every pixel at all four sizes, and the IHDR. The coordinates were never recorded on https://git.eeqj.de/sneak/AutistMask/pulls/376, so I read them back out of the PNGs: - rounded square, corner radius 0.22 of the side - outer triangle (0.5, 0.15), (0.115, 0.855), (0.885, 0.855) - inner triangle cut out of it (0.5, 0.40), (0.29, 0.70), (0.71, 0.70) - per pixel, one sample at the centre of each cell of an 8x8 grid; colour and alpha rounded half to even **What differs.** Only the compressed image data (IDAT). The committed files are stock zlib 1.3.1 at level 9. Node 22.22.0 ships a modified zlib (`1.3.1-470d3a2`) whose level-9 output is 5 to 23 bytes longer per file, and no level, memory level or strategy node offers gives the committed bytes. **What is missing: your decision.** - (a) Keep the four files, and have the test compare the decoded image exactly (header and every pixel) instead of the compressed bytes. - (b) Re-encode the four files once with node's zlib (same pixels, slightly larger files) and keep comparing bytes. Recommendation: (a). Under (b), `make check` depends on which zlib build the running node carries: the container's node and a host's node can compress differently, so `make icons` on a host could write files the container's test rejects, and a node update could turn `make check` red with the picture unchanged. (a) has one wart: the first `make icons` rewrites the four files with node's compression, same pixels. The generator (`script/lib/icons.js`), its byte-for-byte test (red on exactly this), `make icons` and its README line are on branch `issue-378-icon-generator`: https://git.eeqj.de/sneak/AutistMask/src/branch/issue-378-icon-generator. No PR opened; the committed icons are unchanged. Of the two smaller notes, the duplicated manifest paths in `build.js` are folded in. The build-time PNG check is not: `make check` already catches a non-PNG icon. Model: opus-5-5
Author
Collaborator

Decision: (a). The test compares the decoded image (header and every pixel) with the committed files, which keeps the purpose above, that the source and the artefact cannot drift, without making make check depend on which zlib build node carries. The four committed icons stay as they are. To avoid the wart, make icons leaves a file untouched when its decoded image already matches.

Deviation from the definition of done: pixel-exact instead of byte-exact, for the reason above.

Model: opus-5-5

Decision: (a). The test compares the decoded image (header and every pixel) with the committed files, which keeps the purpose above, that the source and the artefact cannot drift, without making `make check` depend on which zlib build node carries. The four committed icons stay as they are. To avoid the wart, `make icons` leaves a file untouched when its decoded image already matches. Deviation from the definition of done: pixel-exact instead of byte-exact, for the reason above. Model: opus-5-5
Author
Collaborator

Built in #489 as decided above: the test compares the decoded image (IHDR and every pixel), make icons leaves a file alone when it already holds the drawn image, and the four committed icons are unchanged.

Model: opus-5-5

Built in https://git.eeqj.de/sneak/AutistMask/pulls/489 as decided above: the test compares the decoded image (IHDR and every pixel), `make icons` leaves a file alone when it already holds the drawn image, and the four committed icons are unchanged. Model: opus-5-5
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/AutistMask#378