app_icons: open-book glyph for book-guard; android_ui: hermetic serial test

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FramxKw57WdVkHPsDUv3Vs
This commit is contained in:
Krzysztof Rudnicki 2026-09-26 19:42:31 +02:00
parent ae99698d5b
commit 50b7065854
7 changed files with 78 additions and 29 deletions

View File

@ -0,0 +1,14 @@
{
"title": "Register book-guard in the shared app-icon family",
"objective": "The book-guard Flutter app (Android + web) ships an icon from the shared family, generated by the one shared generator, and the android_ui CLI test suite is hermetic with respect to the phone lease. Success: the registry tests pass at 100% coverage and the icon renders legibly at 48dp.",
"acceptance_criteria": [
"open-book glyph stays inside SAFE_BOX with negative space >= MIN_NEGATIVE_SPACE",
"book_guard_app registered with the shared accent and generates Android assets",
"test_cli passes regardless of the host's phone lease state"
],
"out_of_scope": [
"Restyling the family (style.py)",
"Any other app's glyph"
],
"verifier": "pre-commit (pytest with coverage enforcement) + python3 -m python_pkg.app_icons preview"
}

View File

@ -1,28 +0,0 @@
{
"intent": "Unblock the shared branch by applying prettier to endurain/README.md, which was committed unformatted by another agent and fails the pre-push prettier hook for everyone pushing after it.",
"scope": [
"endurain/README.md (formatting only)",
"Non-goal: any content change; this file belongs to another agent's feature"
],
"changes": [
"Ran the repo's prettier over endurain/README.md, reflowing 9 lines"
],
"verification": [
{
"command": "npx prettier --check on every markdown/json/yaml file in the push range",
"result": "fail",
"evidence": "Only endurain/README.md was flagged; my own files and the other agent's json/yml were already clean."
},
{
"command": "npx prettier --write endurain/README.md; git diff --stat",
"result": "pass",
"evidence": "9 insertions, 9 deletions -- pure reflow, no content added or removed."
}
],
"risks": [
"The file belongs to another agent's in-flight feature; a formatting-only change could conflict with their next edit, though prettier output is deterministic so a re-run converges."
],
"rollback": [
"git revert the commit; the pre-push prettier hook will then block pushes again until it is reformatted"
]
}

View File

@ -0,0 +1,31 @@
{
"intent": "book-guard's new Flutter app gets a launcher icon from the shared family, and android_ui's CLI test stops depending on the host's live phone lease.",
"scope": [
"python_pkg/app_icons/_glyph_art_more.py, glyphs.py, apps.py: new open-book glyph + book_guard_app registry entry",
"python_pkg/android_ui/tests/test_cli.py: test_serial_is_forwarded_to_the_driver patches _lease_phone",
"Non-goal: no change to style.py or any other app's icon"
],
"changes": [
"Added the filled open-book glyph (two pages, 48px spine gap >= MIN_NEGATIVE_SPACE) and registered book_guard_app -> ~/src/book-guard/app",
"Patched _lease_phone in the serial-forwarding test: unpatched it read the real lease and exited busy whenever another session held the phone"
],
"verification": [
{
"command": "python3 -m pytest python_pkg/app_icons/tests python_pkg/android_ui/tests/test_cli.py",
"result": "pass",
"evidence": "44 app_icons tests pass at 100% branch coverage; test_cli 20 passed, including while a phone lease was held"
},
{
"command": "python3 -m python_pkg.app_icons preview; generate --app book_guard_app --android",
"result": "pass",
"evidence": "Open book reads at full size, circle mask and 48dp; APK with the icon installed on the Pixel 6a"
}
],
"risks": [
"Registry invariant tests assume every app shares the accent -- satisfied (#B8862E)"
],
"rollback": [
"git revert this commit; regenerate book-guard's icon if the glyph goes away",
"Re-run python_pkg/app_icons tests after rollback"
]
}

View File

@ -165,7 +165,13 @@ class TestMisc:
assert "com.example/.Main" in capsys.readouterr().out
def test_serial_is_forwarded_to_the_driver(self) -> None:
with patch(f"{MOD}.AndroidUi") as factory:
# The lease is patched too: unpatched, this read the host's REAL phone
# lease, and while any session held it the call exited "busy" before
# building the driver (failed the gate on 2026-09-26).
with (
patch(f"{MOD}.AndroidUi") as factory,
patch(f"{MOD}._lease_phone", return_value=True),
):
factory.return_value = MagicMock()
cli.main(["-s", "ABC123", "focus"])
assert factory.call_args.kwargs["serial"] == "ABC123"

View File

@ -191,3 +191,19 @@ _UMBRELLA = """\
M 272 512 A 240 240 0 0 1 752 512 A 80 80 0 0 0 592 512 \
A 80 80 0 0 0 432 512 A 80 80 0 0 0 272 512 Z"/>
<path d="M 512 440 L 512 700 A 56 56 0 0 1 400 700"/>"""
# An open book, two filled pages either side of the spine -- book-guard, the
# paper-book reading gate. Filled, not stroked: an outlined page at 72px weight
# leaves an interior under MIN_NEGATIVE_SPACE and fills in at 48dp anyway.
#
# Geometry, inside SAFE_BOX (x=232..792, y=232..792):
# left page x=232..488, top edge curving 344 -> ~318 -> 360, bottom
# 728 -> ~700 -> 744
# right page mirror image about x=512, x=536..792
# spine gap 488..536 = 48px, clear of MIN_NEGATIVE_SPACE=36, so the two
# pages never merge into one slab at launcher size.
_OPEN_BOOK = """\
<path fill="{{ACCENT}}" stroke="none" d="\
M 232 344 Q 372 290 488 360 L 488 744 Q 372 674 232 728 Z \
M 536 360 Q 652 290 792 344 L 792 728 Q 652 674 536 744 Z"/>"""

View File

@ -203,6 +203,14 @@ APPS: Final[dict[str, AppIcon]] = {
icon_name="umbrella",
linux=False,
),
AppIcon(
key="book_guard_app",
repo=_HOME / "src/book-guard/app",
accent="#B8862E",
glyph="open-book",
icon_name="book-guard",
linux=False,
),
)
}

View File

@ -33,6 +33,7 @@ from python_pkg.app_icons._glyph_art_more import (
_BOWL_STAR,
_HOUSE_TICK,
_NOTE_ASCENDER,
_OPEN_BOOK,
_PADLOCK_CLOSED,
_PUNCH_CARD,
_REMOTE_WAVE,
@ -92,6 +93,7 @@ GLYPHS: Final[dict[str, Glyph]] = {
Glyph("bowl-star", "Filled star above an open bowl", _BOWL_STAR),
Glyph("house-tick", "House outline with a tick inside", _HOUSE_TICK),
Glyph("umbrella", "Filled umbrella canopy over a J-handle", _UMBRELLA),
Glyph("open-book", "Open book, two filled pages", _OPEN_BOOK),
Glyph(
"remote-wave",
"Handset with punched buttons under a broadcast arc",