diff --git a/docs/superpowers/contracts/app-icons-tunedeck-headphones-2026-10-16.json b/docs/superpowers/contracts/app-icons-tunedeck-headphones-2026-10-16.json
new file mode 100644
index 00000000..d31fa261
--- /dev/null
+++ b/docs/superpowers/contracts/app-icons-tunedeck-headphones-2026-10-16.json
@@ -0,0 +1,14 @@
+{
+ "title": "tunedeck launcher icon (headphones)",
+ "objective": "The shared app_icons generator registers `tunedeck` with a new `headphones` glyph that stays inside SAFE_BOX and reads at 48dp, without a per-repo copy of the generator, while apps.py stays under the 250-line cap.",
+ "acceptance_criteria": [
+ "generate --app tunedeck --android --web writes assets/icon/*, the Android mipmaps and the web icons",
+ "python_pkg/app_icons stays at 100% line and branch coverage; every file <= 250 lines",
+ "every other app's registry entry is unchanged (tutor only moves module)"
+ ],
+ "out_of_scope": [
+ "Changing style.py or any other app's glyph",
+ "Committing the generated files in ~/src/tunedeck from this repo"
+ ],
+ "verifier": "pytest --cov=python_pkg.app_icons --cov-branch; the generate and preview commands; pre-commit hooks on the staged files"
+}
diff --git a/docs/superpowers/evidence/app-icons-tunedeck-headphones-2026-10-16.json b/docs/superpowers/evidence/app-icons-tunedeck-headphones-2026-10-16.json
new file mode 100644
index 00000000..b5b06399
--- /dev/null
+++ b/docs/superpowers/evidence/app-icons-tunedeck-headphones-2026-10-16.json
@@ -0,0 +1,36 @@
+{
+ "intent": "Give the new tunedeck Flutter app (music / podcast player) an on-style launcher icon: headphones in the shared accent on the family charcoal.",
+ "scope": [
+ "python_pkg/app_icons/_glyph_art_extra.py, glyphs.py: the `headphones` glyph",
+ "python_pkg/app_icons/_apps_more.py, apps.py: tutor moves to MORE_APPS (apps.py was at 249 lines) and tunedeck joins it",
+ "python_pkg/app_icons/tests/test_registry.py: headphones registry tests"
+ ],
+ "changes": [
+ "New `headphones` glyph: stroked r=216 headband over two filled 112x216 ear cups, 288px gap between cups, ink 512x428 inside SAFE_BOX",
+ "tunedeck registered with accent #B8862E, linux=True (desktop is a web build in a Chrome --app window)",
+ "APPS merges MORE_APPS unchanged, so lookups and ordering of existing apps are the same"
+ ],
+ "verification": [
+ {
+ "command": "PYTHONPATH=~/src/testsAndMisc python3 -m python_pkg.app_icons generate --app tunedeck --android --web",
+ "result": "pass",
+ "evidence": "tunedeck: wrote 6 asset files; wrote 5 web icons; regenerated Android mipmaps; icon.png viewed: headphones on #211D1B"
+ },
+ {
+ "command": "PYTHONPATH=~/src/testsAndMisc python3 -m python_pkg.app_icons preview -o ~/data/claude-scratch/tunedeck-icon-sheet.png",
+ "result": "pass",
+ "evidence": "Sheet viewed: headphones read at full size, circle-masked and 48dp next to the other glyphs; no safe-box warning"
+ },
+ {
+ "command": "python3 -m pytest -q python_pkg/app_icons/tests --cov=python_pkg.app_icons --cov-branch",
+ "result": "pass",
+ "evidence": "55 passed; every python_pkg/app_icons module 100% line and branch"
+ }
+ ],
+ "risks": [
+ "The band's round caps show as small shoulders above the cups; intended as the headphone yoke"
+ ],
+ "rollback": [
+ "git revert this commit; tunedeck and the headphones glyph leave the registry; tutor's entry is byte-identical either way"
+ ]
+}
diff --git a/python_pkg/app_icons/_apps_more.py b/python_pkg/app_icons/_apps_more.py
new file mode 100644
index 00000000..ebc5746f
--- /dev/null
+++ b/python_pkg/app_icons/_apps_more.py
@@ -0,0 +1,36 @@
+"""Later registry entries, split out of :mod:`apps` for the 250-line cap.
+
+Which file an entry lives in carries no meaning; :data:`apps.APPS` merges both.
+"""
+
+from __future__ import annotations
+
+from pathlib import Path
+from typing import Final
+
+from python_pkg.app_icons._app_icon import AppIcon
+
+_HOME = Path.home()
+
+MORE_APPS: Final[tuple[AppIcon, ...]] = (
+ AppIcon(
+ key="tutor",
+ repo=_HOME / "src/tutor/app",
+ accent="#B8862E",
+ glyph="lightbulb",
+ icon_name="tutor",
+ # Desktop is the web build in a Chrome --app window, as for todo
+ # and home_inventory, whose desktop entries use the hicolor set.
+ linux=True,
+ ),
+ AppIcon(
+ key="tunedeck",
+ repo=_HOME / "src/tunedeck",
+ accent="#B8862E",
+ glyph="headphones",
+ icon_name="tunedeck",
+ # Desktop is a web build behind a local wrapper in a Chrome --app
+ # window (see the repo's SPEC.md), so it gets the hicolor set too.
+ linux=True,
+ ),
+)
diff --git a/python_pkg/app_icons/_glyph_art_extra.py b/python_pkg/app_icons/_glyph_art_extra.py
index f3401888..bf2d51d5 100644
--- a/python_pkg/app_icons/_glyph_art_extra.py
+++ b/python_pkg/app_icons/_glyph_art_extra.py
@@ -24,3 +24,20 @@ _LIGHTBULB = """\
A 150 150 0 0 1 662 442 C 662 500 576 510 576 556 Z"/>
"""
+
+# Headphones -- tunedeck, a music player: the listening itself. A stroked
+# headband over two filled ear cups. The music note (``note-ascender``) is
+# lyricanki's, and a cassette's reels need more negative space than a 560px
+# box leaves at the family stroke weight, so the ear cups carry the meaning.
+#
+# Geometry, ink inside SAFE_BOX (x=256..768 = 512 wide, y=308..736 = 428 tall):
+# band semicircle r=216 about (512,560), so its ink tops out at y=308 and
+# its ends land at x=296 / x=728, buried inside the cups.
+# cups filled rounded rects x=256..368 and x=656..768, y=520..736; the
+# 288px gap between them is far clear of MIN_NEGATIVE_SPACE=36.
+_HEADPHONES = """\
+
+
+ """
diff --git a/python_pkg/app_icons/apps.py b/python_pkg/app_icons/apps.py
index 13f1913a..e308e0d2 100644
--- a/python_pkg/app_icons/apps.py
+++ b/python_pkg/app_icons/apps.py
@@ -13,6 +13,7 @@ from pathlib import Path
from typing import Final
from python_pkg.app_icons._app_icon import AppIcon
+from python_pkg.app_icons._apps_more import MORE_APPS
__all__ = ["APPS", "AppIcon", "get_app"]
@@ -215,16 +216,7 @@ APPS: Final[dict[str, AppIcon]] = {
icon_name="daily-limits",
linux=False,
),
- AppIcon(
- key="tutor",
- repo=_HOME / "src/tutor/app",
- accent="#B8862E",
- glyph="lightbulb",
- icon_name="tutor",
- # Desktop is the web build in a Chrome --app window, as for todo
- # and home_inventory, whose desktop entries use the hicolor set.
- linux=True,
- ),
+ *MORE_APPS,
)
}
diff --git a/python_pkg/app_icons/glyphs.py b/python_pkg/app_icons/glyphs.py
index 3ba015c5..4c00634e 100644
--- a/python_pkg/app_icons/glyphs.py
+++ b/python_pkg/app_icons/glyphs.py
@@ -29,7 +29,7 @@ from python_pkg.app_icons._glyph_art import (
_SHIELD_CUTLERY,
_STORAGE_BOX,
)
-from python_pkg.app_icons._glyph_art_extra import _LIGHTBULB
+from python_pkg.app_icons._glyph_art_extra import _HEADPHONES, _LIGHTBULB
from python_pkg.app_icons._glyph_art_more import (
_BOWL_STAR,
_HOURGLASS,
@@ -100,6 +100,7 @@ GLYPHS: Final[dict[str, Glyph]] = {
_REMOTE_WAVE,
),
Glyph("lightbulb", "Filled lightbulb over a two-bar screw base", _LIGHTBULB),
+ Glyph("headphones", "Headband arc over two filled ear cups", _HEADPHONES),
)
}
diff --git a/python_pkg/app_icons/tests/test_registry.py b/python_pkg/app_icons/tests/test_registry.py
index db595d80..41f7c6e1 100644
--- a/python_pkg/app_icons/tests/test_registry.py
+++ b/python_pkg/app_icons/tests/test_registry.py
@@ -93,3 +93,16 @@ class TestLightbulbGlyph:
def test_bulb_is_filled_in_the_accent(self) -> None:
"""The bulb takes the accent marker, so the monochrome layer recolours it."""
assert f'fill="{style.ACCENT_MARKER}"' in glyphs.get_glyph("lightbulb").body
+
+
+class TestHeadphonesGlyph:
+ def test_tunedeck_uses_it(self) -> None:
+ app = apps.get_app("tunedeck")
+ assert app.glyph == "headphones"
+ assert app.linux
+
+ def test_cups_are_filled_in_the_accent(self) -> None:
+ """The cups take the accent marker, so the monochrome layer recolours it."""
+ body = glyphs.get_glyph("headphones").body
+ assert f'fill="{style.ACCENT_MARKER}"' in body
+ assert 'stroke="none"' in body