diff --git a/docs/superpowers/contracts/epopeja-karta-icon-2026-08-03.json b/docs/superpowers/contracts/epopeja-karta-icon-2026-08-03.json new file mode 100644 index 00000000..39784ccf --- /dev/null +++ b/docs/superpowers/contracts/epopeja-karta-icon-2026-08-03.json @@ -0,0 +1,20 @@ +{ + "title": "Register the new Epopeja character-sheet app in the shared icon family and the phone focus-mode whitelist", + "objective": "~/epopeja_karta is a new Flutter app, ported out of a claude.ai session, that still shipped the stock Flutter 'beach' launcher icon -- which per CLAUDE.md means it is not done. It also carries a com.kuhy.* package id, and phone_focus_mode disables any com.kuhy.* package not on its whitelist within about a second at home, so an install would appear to work and then close instantly. Give the app a family icon generated by the shared generator (never a hand-rolled one-off) and add it to the whitelist. Success: `generate --app epopeja_karta --android` writes mipmaps showing the app's own glyph, the glyph is distinguishable from the other eight at 48dp, and the package id is present in config.sh ready for the next deploy.", + "acceptance_criteria": [ + "The glyph is registered in python_pkg/app_icons/glyphs.py and the app in python_pkg/app_icons/apps.py -- no per-repo copy of the generator and no hand-drawn icon anywhere in ~/epopeja_karta", + "The accent is the shared family accent #B8862E, not an app-specific colour: apps in this family are distinguished by glyph shape only, as apps.py's own docstring requires", + "The glyph reads as what it depicts at launcher size and collides with nothing already in the family; the first draft rendered as a map pin and was rejected on exactly that ground", + "Geometry is inside the family budget: safe_box_overflow returns (0, 0), and every gap between filled regions is at or above MIN_NEGATIVE_SPACE (36px), so nothing merges into a blob at 48dp", + "The vent hole and slit are true holes via fill-rule=evenodd, because icon_foreground and icon_monochrome render on transparency where a painted hole shows as a blob and in the wrong colour", + "python_pkg/app_icons keeps 100% branch coverage with zero suppressions, and pre-commit passes on every changed file except hooks failing for pre-existing repo-wide reasons", + "com.kuhy.epopeja_karta appears in the WHITELIST block of phone_focus_mode/config.sh alongside the other com.kuhy.* productive apps" + ], + "out_of_scope": [ + "The app itself -- lib/, tests, CI and the Android package rename all live in ~/epopeja_karta, a separate repo, and are not part of this commit", + "Deploying the whitelist to the phone -- deploy.sh needs the device, which is intentionally not being used this session; config.sh and the device stay out of sync until then", + "A Linux hicolor icon set or .desktop entry -- the app has a linux/ target purely so it can be driven on the desktop during development, so linux=False", + "Any change to the shared accent or to another app's glyph" + ], + "verifier": "PYTHONPATH=~/testsAndMisc python3 -m python_pkg.app_icons list (shows epopeja_karta -> quill-nib #B8862E); preview -o sheet.png and eyeball the nib against the other eight glyphs at all three sizes; python3 -c \"...render.safe_box_overflow(glyphs.get_glyph('quill-nib'), '#B8862E')\" returns (0, 0); generate --app epopeja_karta --android then view android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png; python3 -m coverage run --branch --source=python_pkg/app_icons -m pytest python_pkg/app_icons/tests then coverage report (100%); pre-commit run --files python_pkg/app_icons/glyphs.py python_pkg/app_icons/apps.py phone_focus_mode/config.sh" +} diff --git a/docs/superpowers/evidence/epopeja-karta-icon-2026-08-03.json b/docs/superpowers/evidence/epopeja-karta-icon-2026-08-03.json new file mode 100644 index 00000000..dbb1cac6 --- /dev/null +++ b/docs/superpowers/evidence/epopeja-karta-icon-2026-08-03.json @@ -0,0 +1,51 @@ +{ + "intent": "Give the new Epopeja character-sheet app (~/epopeja_karta) a launcher icon from the shared family, and let it run on the phone under focus mode.", + "scope": [ + "python_pkg/app_icons/glyphs.py - new quill-nib glyph", + "python_pkg/app_icons/apps.py - register epopeja_karta", + "phone_focus_mode/config.sh - whitelist com.kuhy.epopeja_karta", + "Non-goal: the app itself lives in its own repo (~/epopeja_karta) and is not part of this change", + "Non-goal: no accent change - the family accent #B8862E is shared, apps differ by glyph only" + ], + "changes": [ + "Added the quill-nib glyph: a filled dip-pen nib with a true vent hole and slit (fill-rule=evenodd), sized so every gap clears MIN_NEGATIVE_SPACE (36px) and the ink stays inside SAFE_BOX.", + "First draft was a symmetric almond and rendered as a map pin; the shipped shape has a flat shoulder, which is what makes it read as a nib.", + "Registered epopeja_karta -> quill-nib with the shared #B8862E accent, linux=False (a linux/ target exists for desktop development only, so no hicolor set or desktop entry).", + "Whitelisted com.kuhy.epopeja_karta in the focus-mode config; not yet deployed to the device, so config and phone are intentionally out of sync until deploy.sh runs." + ], + "verification": [ + { + "command": "python3 -m pytest python_pkg/app_icons/tests -q", + "result": "pass", + "evidence": "44 passed. Registry tests cover the new entry: every app references a real glyph, accents are shared, no glyph paints the background colour." + }, + { + "command": "python3 -m coverage run --branch --source=python_pkg/app_icons -m pytest python_pkg/app_icons/tests", + "result": "pass", + "evidence": "254 stmts, 32 branches, 0 missed - 100% on apps.py and glyphs.py included." + }, + { + "command": "render.safe_box_overflow(get_glyph('quill-nib'), '#B8862E')", + "result": "pass", + "evidence": "(0, 0) - no overflow on either axis, same as the receipt-split reference." + }, + { + "command": "python3 -m python_pkg.app_icons generate --app epopeja_karta --android", + "result": "pass", + "evidence": "Wrote 6 asset files and regenerated the Android mipmaps; mipmap-xxxhdpi/ic_launcher.png visually confirmed as the nib, not the stock Flutter beach icon." + }, + { + "command": "pre-commit run --files python_pkg/app_icons/glyphs.py python_pkg/app_icons/apps.py phone_focus_mode/config.sh", + "result": "partial", + "evidence": "ruff, ruff format, mypy, pylint, bandit, shellcheck, codespell, binary-file block and secret scan all pass. Two hooks fail for reasons outside this change: 'workflow contract for multi-file code changes' needs a contract artifact, and 'pytest with coverage enforcement' aborts on pre-existing collection errors in code_tutor and wsg_grabber. Both must be resolved before this is committed - they are not bypassed." + } + ], + "risks": [ + "The focus-mode whitelist entry has no effect until deploy.sh is run against the phone; until then a com.kuhy.epopeja_karta install would be killed within ~1s at home.", + "quill-nib is a filled silhouette, so it depends on fill-rule=evenodd keeping the vent and slit as true holes; a renderer change that ignores that would paint them charcoal and break icon_monochrome." + ], + "rollback": [ + "Remove the epopeja_karta entry from APPS, the quill-nib entry from GLYPHS and its _QUILL_NIB body, and the whitelist line from phone_focus_mode/config.sh.", + "Re-run the app_icons test suite and confirm 100% coverage still holds; re-run deploy.sh if the whitelist had already been pushed to the device." + ] +} diff --git a/phone_focus_mode/config.sh b/phone_focus_mode/config.sh index 42e0d86e..ad78c748 100755 --- a/phone_focus_mode/config.sh +++ b/phone_focus_mode/config.sh @@ -13,7 +13,7 @@ SCRIPT_DIR="${FOCUS_MODE_SCRIPT_DIR:-$(cd "$(dirname "$0")" && pwd)}" # $0 points to the wrapper path rather than this file's directory. Fall back # to the canonical runtime location if config_secrets is not alongside $0. if [ ! -f "$SCRIPT_DIR/config_secrets.sh" ] && [ -f "/data/local/tmp/focus_mode/config_secrets.sh" ]; then - SCRIPT_DIR="/data/local/tmp/focus_mode" + SCRIPT_DIR="/data/local/tmp/focus_mode" fi . "$SCRIPT_DIR/config_secrets.sh" @@ -340,6 +340,7 @@ com.kuhy.workout_app com.kuhy.diet_guard_app com.kuhy.habit_stack com.kuhy.home_inventory +com.kuhy.epopeja_karta 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 c9fb1680..345fbe46 100644 --- a/python_pkg/app_icons/apps.py +++ b/python_pkg/app_icons/apps.py @@ -125,8 +125,9 @@ APPS: Final[dict[str, AppIcon]] = { accent="#B8862E", glyph="quill-nib", icon_name="epopeja-karta", - # `flutter create` scaffolded no desktop target for this one; it is - # a character sheet you bring to the table, i.e. phone-only. + # A linux/ target exists, but only so the app can be driven on the + # desktop during development. It is a character sheet you bring to + # the table, so it ships no desktop entry and gets no hicolor set. linux=False, ), )