From ae458d92cfde80472f66e72ca48f25f887b1fd33 Mon Sep 17 00:00:00 2001 From: Krzysztof kuhy Rudnicki Date: Mon, 20 Jul 2026 11:09:23 +0200 Subject: [PATCH] app_icons: register habit_stack with a new chain-link glyph; whitelist it New Flutter app (~/habit_stack) gets an on-style launcher icon from the shared family: two interlocking stadium-ring outlines, sized so the 72px stroke leaves a genuinely hollow interior at launcher size. Accent matches the app's own ThemeData seedColor (#66BB6A), not already claimed by a sibling app. Also whitelists com.kuhy.habit_stack in phone_focus_mode/config.sh (both the always-on productive-apps list and the night/curfew health-tool list) and redeploys, since an unwhitelisted com.kuhy.* app is killed within ~1s and would otherwise look like an install failure. Co-Authored-By: Claude Sonnet 5 --- .../evidence/habit-stack-icon-2026-07-20.json | 40 +++++++++++++++++++ phone_focus_mode/config.sh | 4 ++ python_pkg/app_icons/apps.py | 8 ++++ python_pkg/app_icons/glyphs.py | 15 +++++++ 4 files changed, 67 insertions(+) create mode 100644 docs/superpowers/evidence/habit-stack-icon-2026-07-20.json diff --git a/docs/superpowers/evidence/habit-stack-icon-2026-07-20.json b/docs/superpowers/evidence/habit-stack-icon-2026-07-20.json new file mode 100644 index 00000000..f70ad772 --- /dev/null +++ b/docs/superpowers/evidence/habit-stack-icon-2026-07-20.json @@ -0,0 +1,40 @@ +{ + "intent": "Register a new app_icons entry for habit_stack (a new Flutter app) so it gets an on-style launcher icon from the shared family, and whitelist its package id in phone_focus_mode so the app isn't killed on-device.", + "scope": [ + "python_pkg/app_icons/glyphs.py", + "python_pkg/app_icons/apps.py", + "phone_focus_mode/config.sh", + "No changes to any other package's behavior" + ], + "changes": [ + "Added a new chain-link glyph (_CHAIN_LINK) to glyphs.py: two interlocking stadium-ring outlines, sized so the 72px stroke leaves a genuinely hollow interior at launcher size", + "Registered habit_stack in apps.py's APPS dict (accent #66BB6A matching the app's own ThemeData seedColor, glyph chain-link, icon_name habit-stack, linux=False)", + "Added com.kuhy.habit_stack to phone_focus_mode/config.sh in both places a companion-app package id needs to appear (the always-on productive-apps whitelist and the night/curfew health-tool whitelist), then redeployed to the phone" + ], + "verification": [ + { + "command": "source .venv/bin/activate && python3 meta/scripts/pytest_changed_packages.py ", + "result": "pass", + "evidence": "877 passed in 1.91s; TOTAL coverage 100.00% (2816 stmts, 758 branches, 0 missed). Note: an earlier run against the pyenv global Python (not this repo's .venv) spuriously failed on a missing markdownify import in the unrelated archwiki_rag package -- confirmed pre-existing and environment-only, not a real gap; re-running via .venv resolved it." + }, + { + "command": "PYTHONPATH=~/testsAndMisc python3 -m python_pkg.app_icons preview -o /icon_sheet.png", + "result": "pass", + "evidence": "Visual review of the generated contact sheet (full size, circle-masked, 48dp) confirmed the chain-link glyph reads clearly as two interlocking rings at all three sizes, no safe-box warnings from the CLI" + }, + { + "command": "ADB_SERIAL=BL9000EEA0000102 bash phone_focus_mode/deploy.sh", + "result": "pass", + "evidence": "Deploy completed successfully; config.sh (including the new com.kuhy.habit_stack entries) pushed to device and daemon restarted" + } + ], + "risks": [ + "New glyph geometry was hand-derived (stroke width vs. ring interior hollow space); mitigated by visual review of the actual rendered preview sheet before committing to the design", + "phone_focus_mode/config.sh is a live-deployed config; a bad edit could affect other whitelisted apps -- mitigated by adding only new lines, never touching existing entries, and confirming via deploy.sh's own status output that other apps' whitelist state was unaffected" + ], + "rollback": [ + "git revert this commit", + "Re-run PYTHONPATH=~/testsAndMisc python3 -m python_pkg.app_icons generate --app habit_stack --android is then a no-op since the app entry no longer exists; the habit_stack repo's already-generated assets/icon/ files are unaffected (that's a separate repo)", + "Redeploy phone_focus_mode (ADB_SERIAL=BL9000EEA0000102 bash phone_focus_mode/deploy.sh) to push the reverted config.sh back to the phone" + ] +} diff --git a/phone_focus_mode/config.sh b/phone_focus_mode/config.sh index c8111bb1..86e532a3 100755 --- a/phone_focus_mode/config.sh +++ b/phone_focus_mode/config.sh @@ -338,6 +338,7 @@ com.kuhy.focusstatus com.stronglifts.app com.kuhy.workout_app com.kuhy.diet_guard_app +com.kuhy.habit_stack com.shazam.android com.ichi2.anki com.metrolist.music @@ -537,6 +538,9 @@ dev.kuhy.todo # --- Diet guard companion (log meals; health tool, not a scroll trap) --- com.kuhy.diet_guard_app +# --- Habit Stack companion (habit tracking; health tool, not a scroll trap) --- +com.kuhy.habit_stack + # --- Good-for-you apps (not scroll traps; kept per your request) --- # Remove any of these if you want a stricter night. com.stronglifts.app diff --git a/python_pkg/app_icons/apps.py b/python_pkg/app_icons/apps.py index 4aef3dbb..9cd69ac4 100644 --- a/python_pkg/app_icons/apps.py +++ b/python_pkg/app_icons/apps.py @@ -87,6 +87,14 @@ APPS: Final[dict[str, AppIcon]] = { icon_name="todo", linux=True, ), + AppIcon( + key="habit_stack", + repo=_HOME / "habit_stack", + accent="#66BB6A", + glyph="chain-link", + icon_name="habit-stack", + linux=False, + ), ) } diff --git a/python_pkg/app_icons/glyphs.py b/python_pkg/app_icons/glyphs.py index 4be2dcb4..4dca4eb8 100644 --- a/python_pkg/app_icons/glyphs.py +++ b/python_pkg/app_icons/glyphs.py @@ -78,6 +78,20 @@ _CHECKLIST = """\ """ +# 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". +# Ring height is tall enough that the 72px stroke leaves a genuinely hollow +# interior (height - 2*stroke-width well above MIN_NEGATIVE_SPACE); a +# shorter ring's stroke fills the interior solid and reads as a blob. +_CHAIN_LINK = """\ + + + + + + """ + GLYPHS: Final[dict[str, Glyph]] = { glyph.name: glyph @@ -87,6 +101,7 @@ GLYPHS: Final[dict[str, Glyph]] = { Glyph("clock", "Clock face with hands", _CLOCK), Glyph("shield-cutlery", "Shield guarding a fork and knife", _SHIELD_CUTLERY), Glyph("checklist", "Two ticked-off list rows", _CHECKLIST), + Glyph("chain-link", "Two interlocking chain links", _CHAIN_LINK), ) }