diff --git a/docs/superpowers/evidence/untools-app-icon-2026-08-09.json b/docs/superpowers/evidence/untools-app-icon-2026-08-09.json new file mode 100644 index 00000000..5135a62c --- /dev/null +++ b/docs/superpowers/evidence/untools-app-icon-2026-08-09.json @@ -0,0 +1,44 @@ +{ + "intent": "Give the new ~/untools Flutter app a launcher icon from the shared family (charcoal field + accent glyph), so it does not ship the stock Flutter beach icon, and whitelist its package in phone_focus_mode's config.", + "scope": [ + "python_pkg/app_icons/glyphs.py — new decision-tree glyph", + "python_pkg/app_icons/apps.py — register the untools app", + "phone_focus_mode/config.sh — add com.kuhy.untools to the productive-apps whitelist", + "Non-goal: no change to render/style/generate; the shared palette and safe-box rules are untouched" + ], + "changes": [ + "Added _DECISION_TREE: one node branching into two, drawn as hollow rings (r=78) so the 72px stroke leaves visible negative space at icon size. A two-level, seven-node version was tried first and rejected — the generator's safe-box check flagged it as overflowing by ~170x162px, and at 48dp the doubled connectors closed into a solid mass. Branch spread was then narrowed from 324/700 to 346/678 to clear a residual 44px horizontal overflow.", + "Registered the untools AppIcon (accent #B8862E, glyph decision-tree, linux=False because the desktop build is the web build in a Chrome --app window, so there is no GTK app to hand a hicolor PNG to).", + "Added com.kuhy.untools to config.sh's user-requested productive apps. Note this is source-of-truth only: the current Pixel 6a (23181JEGR08034) is not rooted, has no /data/local/tmp/focus_mode directory and no running daemon, so nothing was pushed to the device and no app is at risk of being killed there. Kept for the rooted BL9000 phone / any future redeploy." + ], + "verification": [ + { + "command": "PYTHONPATH=~/testsAndMisc python3 -m pytest python_pkg/app_icons/tests -q --cov=python_pkg.app_icons", + "result": "pass", + "evidence": "44 passed; app_icons at 100% (apps.py 24/24, glyphs.py 28/28, cli.py 60/60, generate.py 41/41). The repo-wide 5.35% total is the monorepo bar applied to a single-subpackage run, not a regression from this change." + }, + { + "command": "PYTHONPATH=~/testsAndMisc python3 -m python_pkg.app_icons generate --app untools --android", + "result": "pass", + "evidence": "wrote 6 asset files to /home/kuhy/untools/assets/icon; regenerated Android mipmaps; no safe-box warning on the final geometry." + }, + { + "command": "visual inspection of assets/icon/icon.png and icon_foreground.png", + "result": "pass", + "evidence": "Symmetric tree, three hollow rings holding their negative space, charcoal #211D1B field with #B8862E glyph — consistent with the rest of the family." + }, + { + "command": "adb -s 23181JEGR08034 install -r app-release.apk && am start", + "result": "pass", + "evidence": "Installed and launched; app alive after 4s (pid present), confirming no focus-mode interference on this device." + } + ], + "risks": [ + "The glyph reads as a generic org-chart/hierarchy at very small sizes; it is distinguished from the family's other glyphs by shape, and no other app uses a node-and-edge motif.", + "config.sh now lists a package that is not enforced on the current device. Harmless, but a reader could infer the whitelist is live here when it is not — the comment in this evidence file and the commit message state otherwise." + ], + "rollback": [ + "Revert the three files; delete ~/untools/assets/icon and the android mipmaps it regenerated.", + "After rollback, re-run the app_icons test suite (44 tests) and confirm the registry test still passes with the untools entry removed." + ] +} diff --git a/phone_focus_mode/config.sh b/phone_focus_mode/config.sh index ce55be91..f74ed503 100755 --- a/phone_focus_mode/config.sh +++ b/phone_focus_mode/config.sh @@ -362,6 +362,7 @@ com.kuhy.diet_guard_app com.kuhy.habit_stack com.kuhy.home_inventory com.kuhy.epopeja_karta +com.kuhy.untools com.shazam.android com.ichi2.anki com.metrolist.music diff --git a/python_pkg/app_icons/apps.py b/python_pkg/app_icons/apps.py index 80c37b29..3e08b2b6 100644 --- a/python_pkg/app_icons/apps.py +++ b/python_pkg/app_icons/apps.py @@ -89,6 +89,16 @@ APPS: Final[dict[str, AppIcon]] = { icon_name="todo", linux=True, ), + AppIcon( + key="untools", + repo=_HOME / "untools", + accent="#B8862E", + glyph="decision-tree", + icon_name="untools", + # Desktop is the web build in a Chrome --app window, so there is + # no GTK app to hand a hicolor PNG to. + linux=False, + ), AppIcon( key="habit_stack", repo=_HOME / "habit_stack", diff --git a/python_pkg/app_icons/glyphs.py b/python_pkg/app_icons/glyphs.py index d84f6f30..065d40c9 100644 --- a/python_pkg/app_icons/glyphs.py +++ b/python_pkg/app_icons/glyphs.py @@ -78,6 +78,29 @@ _CHECKLIST = """\ """ +# A branching decision tree: one node splitting into two. Reads as "decompose +# a problem into parts", the move every one of the 25 thinking tools makes in +# some form -- an issue tree most literally, but also a fishbone's ribs and a +# 2x2's quadrants. +# +# Nodes are hollow circles rather than filled dots: at 48dp a filled dot and +# the 72px stroke merge into a blob, while the ring keeps visible negative +# space (r=78 leaves 156-72=84px of interior). +# +# Two levels of branching were tried first (7 nodes) and rejected: the +# generator's safe-box check flagged it as overflowing by ~170px, and at icon +# size the doubled connectors closed up into a solid mass. One clean split +# survives the launcher mask and still reads as a tree rather than a Y-fork, +# because the square shoulders and vertical drops keep the hierarchy legible. +_DECISION_TREE = """\ + + + + + + + """ + # Two interlocking stadium (pill-ring) outlines, both rotated -45 deg and # offset along the anti-diagonal so their strokes overlap in the middle -- # reads as both "habit-stacking" (linked items) and "don't break the chain". @@ -238,6 +261,11 @@ GLYPHS: Final[dict[str, Glyph]] = { Glyph("quill-nib", "Dip-pen nib with vent hole and slit", _QUILL_NIB), Glyph("anvil", "Blacksmith's anvil with horn and flared foot", _ANVIL), Glyph("track-bars", "Rising log bars with a plotted trend point", _TRACK_BARS), + Glyph( + "decision-tree", + "Node branching into two levels of child nodes", + _DECISION_TREE, + ), ) }