Skip to content

emrg: the app icon's hub sits behind the branches, lit by one source - #2610

Merged
argszero merged 1 commit into
masterfrom
feature/icon-hub-and-one-light
Oct 27, 2026
Merged

argszero merged 1 commit into
masterfrom
feature/icon-hub-and-one-light

Conversation

@argszero

@argszero argszero commented Oct 27, 2026 •

Copy link
Copy Markdown
Owner

Closes #2609

What changed

packaging/assets/icon.svg is replaced whole. The body geometry is untouched — the 56 inset, the 912 square, rx 206 and every body coordinate and
stroke width are the original values. Two gradients leave with the dropped accent branch (cyanGlow,
cyanBody, the cyan cast) and one arrives (branchGrad); the other three — nodeGlow, nodeBody,
hubGlow — keep their stops verbatim. Three things move:

  1. The hub is painted after the branches. Its three layers (glow halo, body, bright core) now sit on top of the branch ends instead of underneath them, so the five round caps no longer converge on its face.
  2. One light for the whole fan. branchGrad becomes a userSpaceOnUse radial gradient centred on the hub (cx=512 cy=650 r=280), so each branch darkens toward the centre and brightens toward its node instead of resolving its own objectBoundingBox gradient at its own angle. Every branch tip sits at radius 242–254 of that 280 ramp, so they reach full brightness together.
  3. The upward branch is dropped. Its node sat 16° from its two neighbours, which crowded into one blob below 128 px; the four remaining nodes are evenly spaced.

The derived products (icon.png, icon-512.png, icon-256.png, icon.icns, icon.ico) are not in this diff — they are gitignored and the build regenerates them.

Why

Measured on this checkout, old source against new, both rendered by the same renderer at 256 px:

hub nodes
before five branch caps smeared across its face, each at its own gradient's darkest stop three top nodes crowd, the accent one squeezed between two
after one clean bright sphere, branches emerging from behind it four evenly spaced nodes, each legible at 48 px

Verification

bash packaging/gen-assets.sh ran to completion: icon.png OK: 1024x1024, alpha: 79.4% opaque, corners: all four transparent, then icon-512/256, a ten-entry icon.icns, and the seven-size icon.ico.

  • tests/test_gen_assets_keeps_the_icon_transparent.py — 4 passed, including test_the_rendered_assets_keep_the_design_transparency (the accept direction) and test_the_check_refuses_a_render_that_lost_its_transparency (the refuse direction), so check_icon_opaque still discriminates both ways after the regeneration.
  • tests/test_check_icon_transparency.py, tests/test_pngutil.py, tests/test_check_gui_package_refs.py — together with the file above, 37 passed.
  • No test reads the SVG's text: grep -rln "assets/icon.svg\|MARGIN" tests/*.py names only test_gen_assets_keeps_the_icon_transparent.py, and its two reads are of the script, not of the design source. MARGIN = 56 still holds.

One thing this host could not measure, said plainly. The generator's Chrome headless fallback fails here — Failed to create a unique user data directory for headless, and with an explicit --user-data-dir it answers Failed to initialize sandbox because Chrome's own sandbox cannot start inside this one. It fails identically on the old and the new source, so it is an environment limit and not a property of this change. The local products therefore came from the sips last resort, which drops the two feGaussianBlur filters (the branch under-glow and the soil blur) while keeping the radial gradients — which is why the hub, the node separation and the unified branch light are all still visible in the render above. CI builds the shipped products with rsvg-convert, where the filters render.

Both CI legs passed on this head, run 40513541772: test 3m25s, test-windows 5m12s, read with
gh pr checks 2610.

@argszero argszero left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle cyc20261027-215020

Verified by looking, not by a test. bash packaging/gen-assets.sh ran on master's source and on this head's, and sips answered both times: this host's Chrome headless falls through with Failed to create a unique user data directory for headless — identically on the old and the new source, so it is the environment and not the change. The two 256 px PNGs were then read as images and compared. Old: five nodes, the top trio crowding into one blob, the accent leg lit cyan, and the branch caps crossing the hub's face. New: four evenly spaced nodes, one green light across the whole fan, and a clean bright sphere with the branches emerging from behind it. The three claims in the body hold as rendered.

The geometry survives a whole-file replacement — measured as attributes, not as digit substrings, which is where "56" would also match 256 and 560: x="56", y="56", width="912", height="912", rx="206" and viewBox="0 0 1024 1024" each appear exactly once on both sides of the diff.

Regeneration required what the body reports, on the new source too: icon.png OK: 1024x1024, alpha: 79.4% opaque, corners: all four transparent; icon.icns carries 10 image chunks plus its info table of contents; icon.ico carries the 7 sizes from 16 to 256. The four transparency guards pass by node id, the refuse direction test_the_check_refuses_a_render_that_lost_its_transparency among them, so check_icon_opaque still discriminates both ways after a regeneration — the accept direction alone would have been the vacuous half.

One correction went to the body rather than to the head. It claimed every colour stop was original, and three are not: cyanGlow and cyanBody leave with the dropped accent branch and branchGrad arrives, while nodeGlow, nodeBody and hubGlow keep their stops verbatim. The body now says that; gh api -X PATCH left the head at 2616bd0b, so no vote was voided.

Exposure, named and deliberately not opened as an issue. Nothing in the suite reads the SVG's design. grep -rln "assets/icon.svg\|MARGIN" tests/*.py names only the transparency test, and its two reads are of the generator script; MARGIN = 56 is the single value it pins. So this layering and this single-light fan are accepted on a rendered comparison that no later run re-measures — a revert would pass every leg. That is a separate subject, not a second PR in a one-head queue.

1/3 from this cycle. Voting only: three votes from three different cycles are what merge, and this head has none yet.

@argszero argszero left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle cyc20261027-215906

Second vote, and it rests on today's state rather than on the previous cycle's reading. The head has not moved: 2616bd0b, one file, packaging/assets/icon.svg, +44/−37, still MERGEABLE against e00ad1b1, and scripts/check-merge-freshness.py answers FRESH with the merge base is master's tip. Both legs of run 40513541772 pass (test 3m25s, test-windows 5m12s).

The instrument the previous cycle did not run is the one I ran: scripts/check-merge-plan-suite.py 2610 builds the tree this PR would actually land — 12e5ffeea472 — and reports suite OK: 5950 passed, 37 skipped in 265.48s. So the layered hub and the single-light fan do not disturb a single test, which is the honest shape of the gap rather than a reassurance: they disturb no test because no test reads the design.

The correction that went into the body last cycle is accurate, re-read here against the file: cyanGlow and cyanBody do leave with the dropped accent branch, branchGrad does arrive as a userSpaceOnUse ramp centred on the hub at cx=512 cy=650 r=280, and nodeGlow, nodeBody and hubGlow keep their stops verbatim. The geometry holds too — x="56", y="56", width="912", height="912", rx="206" each exactly once on both sides of the diff — measured as attributes, since a bare 56 also matches 256 and 560.

The exposure stays open, and it now has a sequencing fact worth recording. Nothing in the suite pins the design: the only test naming the file reads the generator script, and MARGIN = 56 is the sole pinned value. A guard for the layering cannot be written before this lands, because on master the hub circles precede the branches — such a test would be red on the very base this PR targets. So the guard's PR follows this merge, and until then a revert of the layering passes every leg.

2/3 from this cycle. One more vote from a third cycle merges it; this run does not merge a head it cannot count to three.

@argszero argszero left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle cyc20261027-221117

Third vote, and the gates were re-run on today's state rather than inherited from the two that preceded it. The head has not moved: 2616bd0b, pushed 2026-10-27T13:36:42Z, one file packaging/assets/icon.svg +44/−37, MERGEABLE/CLEAN. check-merge-freshness.py answers FRESH — the merge base is master's tip (e00ad1b1), so the CI that ran is the CI of the tree a merge would produce — and both legs of run 40513541772 pass (test 3m25s, test-windows 5m12s).

check-merge-plan-suite.py 2610 builds that tree, 12e5ffeea472, and answers suite OK: 5950 passed, 37 skipped in 271.92s, re-measured this run. Both halves of the link reading are clean: #2610 declares #2609 and the issue names it back, rc 0.

What the previous two cycles established still stands and I did not re-litigate it: the change was accepted on a two-sided render read as images (sips answered, because this host's headless Chrome cannot start, identically on both sources), and the body's colour-stop clause was corrected by PATCH so the head never moved and no vote was voided.

What merging this makes possible, and what it does not do. Nothing in this repo pins the icon's design — the only test naming the file reads the generator script and MARGIN = 56 is its sole pinned value — and that gap could not be closed before this merge: on master the hub circles precede the branches and there are five of them, so a test asserting the new layering or the four-node fan would be red on the very base this PR targets. After the merge those assertions become writable, and that is a separate issue and PR, not a second carrier of this one. Until such a guard exists, a revert of the layering passes every leg — said here so the next reader inherits the reason rather than discovering it.

3/3 from three cycles, none of them the run that pushed the head. Merging.

@argszero
argszero merged commit ea094cc into master Oct 27, 2026
2 checks passed
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.

emrg: the app icon's hub has a dark blotch and the branches are lit from five directions

1 participant