docs: the phone-logging decision, how to use it, and two build-apk traps

DECISIONS.md gets the route and both rejected alternatives with what each
would have cost; RUST.md gets a "Phone logging" section with the build
command, where to read it on the phone, the end-to-end verification, and
the two rig traps that cost an hour -- Gradle's merged-native-libs cache
surviving build-apk.sh's `rm -rf jniLibs` (a --abi x86_64 APK packaged
arm64 and aborted with what reads exactly like a Vulkan fault), and the
648 MB debug bench APK that cannot be installed at all. IRIS.md gets the
client-core logging API with a before/after.

Queue item ticked.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
irisandClaude Fable 5.1 committed 2026-09-07 16:21:23 -04:00
1 parent 896c93a59a
commit 238057ad5e
3 files changed
+189 -5

No files matched your search

+101 -5
View File
@@ -89,6 +89,105 @@ Three things this says about the rig, since the rig is new:
claimed of it -- asserted at the end of every draw, and the test
checks both directions.
### Phone logging, 2026-09-07 (built and verified end to end)
**The problem**: Iris tests these builds on a phone with no `adb`, and
Android forbids one app reading another's `logcat`, so a `log::info!` in
the iris app could not reach her at all. What she asked for was Dev
Updater, which she already reads.
**The route, in one line**: the app keeps its own bounded log ring, posts
it to `ai-server`, and `ai-server` re-emits each line into its own
`tracing` output -- which Dev Updater *already* shows as that component's
**runtime log**, because it runs `ai-server` as a `Managed` service and
that service's script redirects stdout to
`$XDG_DATA_HOME/dev-updater/services/<key>-<component>/<...>.log` and
reports the path. **Nothing in dev-updater changed.** The alternatives and
what each would have cost are in docs/DECISIONS.md.
What exists now:
- `client_core::log_ring` -- `LogRing` (2000 lines / 256 KiB, whichever
bites first, with `dropped` reported rather than inferred), `RingLogger`
(a `log::Log` backend that records *and* forwards to the platform's own
logger), and `install_process_logger`. Reading does not consume, so the
report and the uploader are two readers of one ring.
- `client_core::log_upload` -- `LogUploader::flush_once` (batched at 500
lines, retried from the same cursor on failure) and `LogUpload::spawn`
(a thread flushing every 10s, stopped by dropping the handle).
**Nothing in it calls `log!`**: those lines would land in the ring it is
draining.
- `POST /client-log` on `ai-server` -- `{source, lines:[{seq, at, level,
target, message}]}`, re-emitted at the client's own level under the
target **`ai_server::client_log`**. Under the crate's path deliberately:
a bare `client_log` target is dropped by `RUST_LOG=ai_server=debug`, the
filter AGENTS.md tells people to run with, so every line a phone sent
vanished with nothing saying so. Found by running it.
- `iris/android-app/src/app_log.rs` -- the platform half only:
`android_logger` as the logger to forward to, and the destination baked
in by `build.rs` from `AI_APP_LOG_HOST`/`_PORT`/`_TOKEN` plus the pinned
CA (all three or none). `Copy report` appends the ring to the clipboard
text and flushes the uploader first; `Diagnostics` gains two lines --
how many lines are held and when the last arrived, and what the uploader
last did.
**How to use it.** Build the APK with the destination in the environment,
on the machine `ai-server` runs on:
AI_APP_LOG_HOST=10.66.0.1 AI_APP_LOG_PORT=8443 \
AI_APP_LOG_TOKEN=<a token that server accepts> \
./build-apk.sh release
Then read it on the phone: Dev Updater -> the ai-app project -> the
**server** component's log button -> the **Runtime** tab. The app's lines
are the ones tagged `ai_server::client_log`, each carrying `[<source>
<the app's own clock> #<seq>]` before the target and message. A build with
none of those variables set still keeps its ring and still puts it on the
clipboard from `Copy report`; the Diagnostics pane says so in as many
words.
**Verified 2026-09-07, all four hops.** ai-server on 127.0.0.1:8455 with a
scratch config; the bench APK built `--abi x86_64` with
`AI_APP_LOG_HOST=10.0.2.2 AI_APP_LOG_PORT=8455 AI_APP_LOG_TOKEN=...`;
installed and launched on this checkout's emulator. Twenty seconds later
the server's log held
INFO ai_server::client_log: [iris-bench 20:17:02.284 #0]
iris::android::view: iris: new_peer content_scale=2.625
and a scratch `dev-updater` (port 8492, `XDG_DATA_HOME=/tmp/du-test/data`)
with this checkout registered served exactly those lines back from
`GET /apps/ai-app-2/components/server/logs?kind=runtime` -- which is the
JSON the phone's Runtime tab renders.
**Two rig traps this cost an hour to find, both in `build-apk.sh`, both
still there.** Written down rather than fixed because fixing them belongs
with whoever next touches that script:
1. **Gradle's merged-native-libs cache survives `rm -rf jniLibs`.** The
script removes `app/src/main/jniLibs` before each build (its own
comment says why), but Gradle's `mergeReleaseNativeLibs` is *up to
date* against its cached inputs, so a build that switches ABI packages
the previous ABI. A `--abi x86_64` release APK contained
`lib/arm64-v8a/libmain.so`, installed fine, and aborted at startup with
`Could not get adapter!: NotFound { active_backends: VULKAN }` under
`libndk_translation` -- which reads exactly like the phone's own Vulkan
problem and is nothing of the kind. `rm -rf app/build/intermediates`
before the build is the workaround; check with
`python3 -c "import zipfile; print([i.filename for i in
zipfile.ZipFile('...apk').infolist() if i.filename.endswith('.so')])"`.
2. **The debug bench APK is 648 MB and will not install**
(`INSTALL_PARSE_FAILED_NOT_APK`): the debug `libmain.so` is 325 MB.
Use `release` on the emulator for this app, notwithstanding the general
rule that the emulator stays on debug -- there is nothing to measure
here, and the debug build cannot be installed at all.
Also: the bench APK's package is `dev.iris.android.demo.bench`, not
`dev.iris.android.demo`. An older non-bench build left installed answers
to the second name, runs, looks right, and reports whatever *it* was built
with -- which is how "log upload: this build has no server configured"
came from a build that had one.
### APK size (2026-09-07)
Iris's question: the iris bench APK is about double the Compose bench APK
@@ -366,11 +465,8 @@ closes it.
drawn through the header in the other (docs/IRIS_TODO.md, 2026-09-07).
Done 2026-09-07, e922b73 + d507ae4; the root causes and the test names
are in that IRIS_TODO entry, and the short version is below.
- [ ] Phone logging through Dev Updater (Iris has no logcat; see
docs/TODO.md and the memory note): research how Dev Updater shows an
app's runtime log, design the smallest route (the app keeps its own
recent log; a debug button copies it; Dev Updater reads it), write
the decision in docs/DECISIONS.md, build it.
- [x] Phone logging through Dev Updater -- **done 2026-09-07**, see
"Phone logging" above and docs/DECISIONS.md's entry of that date.
- [x] APK size: release profile tuned (`42af780`), -35% APK, -40% .so;
see "APK size (2026-09-07)". **Iris's verdict, 2026-09-07: "remove the
font for now; just match what compose does."** Done same day -- the six