Files
vitaImmich/CLAUDE.md
T
SadsandClaude Opus 4.8 351804819b Video bitrate probe, loading throbbers, boot spinner, transparent logo
- Video: probe the MP4 header (range request for moov) before downloading
  the whole clip; if the average bitrate overruns the declared H.264 level
  the Vita's decoder enforces, show the "too high" message up front instead
  of downloading then failing at playback. Verdict validated against the
  real demo videos (e.g. e75d495e: Level 3.1 @ 18.8 Mbps -> rejected).
- Error screens: word-wrap title + detail so long messages no longer run
  off the edges; the bitrate message now spells out the server-side fix
  (Immich transcoding policy / bitrate cap).
- Boot: one continuous spinner. A single boot worker thread does sign-in +
  first page + local scan while the main thread animates one logo with a
  never-resetting frame counter, instead of three separate static screens.
- Throbbers: the cloud/server-info page and smart search now fetch on a
  worker thread and animate a throbber instead of freezing the UI.
- Logo: swap the in-app logo for a clean 200x200 transparent PNG (the old
  one was matted from the white-background icon and had fringe); keep the
  launcher icon's white background. Linear-filter the logo + cloud badges
  so they scale/rotate smoothly.
- Drop AgenticLoop.md (and its CLAUDE.md reference).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 00:48:42 +02:00

7.2 KiB
Raw Blame History

CLAUDE.md — vitaImmich

PS Vita Immich client (PoC). C sources in src/. It's a unity build: main.c holds the includes, #defines and core globals, then #includes the section files (state.c, draw.c, config.c, net.c, jpeg.c, api.c, thumbs.c, display.c, ui.c, video.c, sync.c, app.c) in order. They form one translation unit and share all file-scope state — there are no headers or extern decls. Only src/main.c is compiled (CMakeLists lists just it); the parts are textual slices, so add new code to the relevant part, not to CMake. jsmn.h is vendored. Built with the VitaSDK toolchain into a .vpk, run in the Vita3K emulator, and driven headlessly through the kwin-mcp server. This file is the end-to-end loop: build → install → launch → drive → read logs. Full key map is in controls.MD.

App title id: VIMM00001.


1. Build the changes → a .vpk

After editing anything in src/ (or CMakeLists.txt), rebuild.

Preferred — Docker (reproducible), writes build/vitaImmich.vpk:

./build.sh

build/ ends up root-owned. If the Docker runtime is broken (containerd shim readlink /proc/self/exe: no such file or directory), use the native fallback.

Fallback — native host SDK, also writes build/vitaImmich.vpk:

export VITASDK="$HOME/vitasdk"
export PATH="$VITASDK/bin:$PATH"
cmake -B build -DCMAKE_BUILD_TYPE=Release \
      -DCMAKE_TOOLCHAIN_FILE="$VITASDK/share/vita.toolchain.cmake" .
cmake --build build --parallel "$(nproc)"

If a prior Docker build left build/ root-owned, clear it first (sudo rm -rf build) so the native build can write there.

Whichever path you use, the next step installs that .vpk.


2. Install the rebuilt VPK into Vita3K

Installing = launch Vita3K once with the .vpk as an argument; it extracts and auto-boots. deploy.sh does install-then-launch in one shot:

./deploy.sh    # installs ./build/vitaImmich.vpk, then launches VIMM00001 detached

Gotchas (these are why it must run from a script, not inline):

  • Background it from a script file with a sleep/wait then pkill. Inline backgrounding from the agent's shell does not persist.

  • Guard pkill with || true — a no-match exit code aborts the chain.

  • Vita3K preserves the zip's internal mtime on extract. To confirm a fresh install, compare the installed eboot.bin mtime against the vpk entry, not wall-clock:

    unzip -l build/vitaImmich.vpk | grep eboot
    stat -c 'eboot: %y' ~/.local/share/Vita3K/Vita3K/ux0/app/VIMM00001/eboot.bin
    
  • Wipe a stale install: vita3k -d VIMM00001.

deploy.sh launches detached on the live desktop. For agent-driven verification with screenshots, skip deploy.sh's launch and instead launch inside the MCP's isolated session (step 3) — only do the install half first (vita3k ./build/vitaImmich.vpk for ~4s, then pkill).


3. Launch + drive via kwin-mcp (screenshots + keystrokes)

Launch the app inside kwin-mcp's own isolated KWin session — its screenshot tool only works there. session_connect to the live desktop does not capture (spectacle can't save non-interactively; KWin's ScreenShot2 DBus rejects unauthorized callers). The isolated session is headless: the human cannot watch it live, which is exactly why we capture every frame to disk.

  1. Wipe the per-session frame folder first: rm -rf agent-run/frames && mkdir -p agent-run/frames. (agent-run/ is git-ignored; frames/ is the throwaway capture folder — your curated keepers stay in agent-run/ proper and are never touched.)

  2. session_start with app_command: "vita3k -r VIMM00001", keep_screenshots: true, and env: {"SDL_VIDEODRIVER": "wayland", "DISPLAY": ""} — starts the isolated session, launches the app, and keeps every captured frame after session_stop. Give it ~12s to boot.

    The env is mandatory. The session inherits the host's DISPLAY=:1, so without it Vita3K's SDL backend picks X11 and renders to the host display — the captured frames come back blank white, focus_window "Vita3K" fails ("no application matching"), and keystrokes never reach the app. Forcing SDL_VIDEODRIVER=wayland and clearing DISPLAY makes Vita3K render into the MCP's Wayland session, where capture and input both work. Sanity check: list_windows should show a Vita3K window and frames should be ~300–500 KB, not ~8 KB.

  3. focus_window with app_name: "Vita3K" BEFORE every key. Without focus, keypresses are dropped. They also drop intermittently — re-focus and re-press. One key per call; rapid bursts get coalesced/dropped.

  4. keyboard_key with screenshot_after_ms: <ms> after every command, so the frame folder is a complete visual record.

  5. session_stop when done, then pkill -9 -f vita3k. kwin-mcp always writes frames to /tmp/kwin-mcp-screenshots-*/ (not configurable) — move them into the repo and clear tmp, then tell the human where they are:

    cp /tmp/kwin-mcp-screenshots-*/*.png agent-run/frames/ 2>/dev/null
    rm -rf /tmp/kwin-mcp-screenshots-*
    

Key map (this app's actions; full list in controls.MD)

Vita button host key (kwin-mcp) in-app action
D-pad Right Left Up Down move grid / browse
Cross (X) x view / play / replay
Circle (O) c back
Select Shift_R sync
Start Return exit

4. Read the logs — verify from TEXT, not frames

Capturing a frame is free (PNGs on disk are not in context). Reading a PNG back into context is the expensive part. So capture every command, but do not Read frames back during the loop. Verify progress from the app's own text log instead:

~/.local/share/Vita3K/Vita3K/ux0/data/vitaimmich/log.txt

This is the app's own log — it records page fetches, video open/play, scans, and errors. Also available:

  • MCP read_app_log with the launched PID.
  • /tmp/vita3k-runtime.log (Vita3K runtime stdout/stderr from deploy.sh).
  • /tmp/vita3k-install.log (install output).

Only Read a frame when a screenshot is genuinely the only way to resolve an ambiguity (rare). Reading every frame is what bloats context — that's what this rule avoids.

Typical verify loop

  1. Build (step 1) → install half of step 2.
  2. rm -rf agent-run/frames && mkdir -p agent-run/frames, then session_start (keep_screenshots: true, env forcing Wayland) → wait ~12s.
  3. focus_window "Vita3K".
  4. keyboard_key "Right" (+ screenshot_after_ms), stepping to the target tile, one key per call; x to open, x again to play.
  5. Confirm what happened from log.txt — not the frames.
  6. session_stop + pkill; move frames into the repo and clear tmp (cp /tmp/kwin-mcp-screenshots-*/*.png agent-run/frames/ 2>/dev/null && rm -rf /tmp/kwin-mcp-screenshots-*), then hand agent-run/frames/ to the human as the visual record.