evidence · 5.8 KiB

app-appearance-host.md

Committed at docs/evidence/omarchy-themes/app-appearance-host.md. This is what grounds the requirements that cite it.

Open raw source

Installed-app appearance adapter: host checkpoint

Observed 2026-09-23 from integration base 99ac8e48. The source is tools/app_appearance.py; this checkpoint does not install or invoke it in the normal shell service. Its input is an already prepared generation’s bounded report.json with the pinned upstream resolver’s palette. It writes only app-appearance/generations/ID/{terminal-foot.ini,monitor-foot.ini,coverage.json} under private theme state. sync obtains the activation lock, reads the acknowledged current/active pointer, and atomically updates a separate app-appearance/active pointer for future Foot launches. It rejects foreign generation/pointer paths and does not edit the source checkout.

The two generated Foot configs retain the portrait font and distinct app IDs, and use resolved semantic colors plus all sixteen explicit ANSI slots. Both Foot light and dark color sections receive the same selected palette so the app cannot silently switch back to an unrelated default. The opt-in osc-current command writes the pinned upstream OSC sequence only to the caller’s own terminal stdout after an isatty and TERM=foot check; these checks are an opt-in routing guard, not proof of the terminal executable. It never enumerates /dev/pts, signals a process, or closes an app. Selection and output flush hold the same bounded activation lock as theme publication, so a newer generation cannot publish between them. The host test compares its 21 sequences byte-for-byte with omarchy-theme-osc on the prepared fixture.

nix/handheld-theme-command.nix now installs k230-app-appearance. The pinned default package includes static terminal and monitor Foot configs generated by this same adapter; tools/generate_default_foot.py --check rejects drift. The static files extend the package output without changing the existing palette/appearance generation identity. A fresh home can use those Nix-store files before a mutable active pointer exists.

The host coordinator now calls app sync only after a shell commit ACK and after releasing the activation lock. Sync reacquires that lock and checks the expected generation, preventing a late first activation from overwriting a newer app selection. omarchy-theme-set and the JSON chooser return an app_appearance result separate from shell activation: applied, failed or superseded. An app failure does not falsely roll back an acknowledged shell theme. The host failure fixture verifies the shell pointer and phase history stay committed when app sync fails.

Coverage is deliberately bounded: htop, nano and nnn inherit Foot’s terminal colors; mpv’s video surface is not a terminal-color consumer; Help uses shell tokens; the on-screen keyboard remains a separate shell-surface integration. Existing Foot windows are not automatically retinted, and the normal Nix launch paths still point at their earlier immutable base configs. The generated configuration and sync hook must be wired to those launch paths before task 3.4 can be marked complete. Any app reload failure belongs in a separate status from the shell generation ACK; it must not be reported as a successful existing-session recolor.

An opt-in k230-foot-session source wrapper is now packaged for Foot launch paths. Invoked inside Foot as k230-foot-session --state-root STATE --default-generation PINNED -- COMMAND, it validates its own stdout TTY, forks one bounded color follower, then execs COMMAND with its original args and foreground process group. The follower inherits only that terminal’s stdout and a Linux pidfd opened before forking for the exec’d parent. The parent closes its copy before exec, so even a fast-exiting app cannot turn the follower’s parent lookup into PID 1. The follower reopens its own inherited TTY stdout through /proc/self/fd/1 as a separate nonblocking file description; it does not change the app’s stdout flags. OSC writes have a 100 ms deadline even with a stopped/full PTY, and release the activation lock on timeout. It polls the acknowledged generation once per second by default, emits OSC only on a generation change under the activation lock, uses the pinned default on rollback to no active pointer, and exits when the parent exits. It does not scan PTYs, signal apps, or keep a terminal open after its app leaves. If pidfd_open fails, the requested app still execs and a fixed diagnostic identifies the missing follower; no OSC claim is made for that session. A fake PTY with a controlling terminal proved unchanged argument execution, foreground process group, initial OSC, and follower exit. Separate host cases cover fast app exit and a full PTY with injected persistent backpressure and bounded lock release. A single 2026-09-23 x86_64 host run observed 14,600 KiB VmRSS for one Python follower; this is neither a target RSS nor a frame-cost measurement. The one-second polling and target cost need review on the actual handheld. The normal Foot launchers do not yet invoke this wrapper, so existing-window automatic recolor remains unproved in production.

python3 tests/test_handheld_app_themes.py   PASS 9 host tests
python3 tests/test_handheld_theme_default.py  PASS 2 host tests
python3 tests/test_omarchy_theme_transaction.py  PASS 13 host tests
python3 tests/test_theme_catalog.py  PASS 9 host tests
python3 tests/test_omarchy_theme_activation.py  PASS 9 host tests
python3 tests/test_foot_color_session.py  PASS 6 host tests; one host RSS=14,600 KiB
python3 tools/generate_default_foot.py --check  PASS
python3 -m py_compile tools/app_appearance.py  PASS
openspec validate the-shell-loads-omarchy-themes --strict  PASS
git diff --check  PASS

No Nix build, device session, physical display observation, or live Foot reload was performed for this adapter checkpoint. OpenSpec task 3.4 remains open, as do the cross-surface, app-session and physical gates.