# HANDOFF, 2026-08-13 (eighth session) Live state is in `NEXT.md`. This file is only what this session did. The previous handoff is in `JOURNAL.md`. ## Asked Push the repo to Gitea and open a PR to `master`. Then, from the PR review: fix the s3 URI constants, run ruff and fix what it finds, push, run a ponytail audit, fix, push, merge. Then audit. ## Result No GPU work. Nothing ran on a pipeline stage. PR #1 is open at `https://gitea.kvmx.ru/kami/manga-recap-pipeline/pulls/1`, `master` <- `restore-runtime`. `master` held only the reconstruction commit `ff6a512`. All 27 commits of real work sat unpushed on `restore-runtime`. ## Landed | commit | what | | --- | --- | | `12d4ed8` | the `ARCHITECTURE.md` rewrite, which was finished but uncommitted | | `be63b22` | deleted a 0-byte file named `II, d[i+4:i+12]))\n"; done`, added by `8113bdf` | | `bec9411` | s3 URI templates into `transport.py`, plus `ruff.toml` and the lint fixes | ### S3 URIs Eight templates now live in `transport.py`: `PANEL_URI`, `PAGE_PANEL_URI`, `AUDIO_URI`, `AUDIO_FLAT_URI`, `LAYER_URI`, `CLIP_URI`, `CHAPTER_URI`, `CHAR_PNG_URI`, `CHAR_NPY_URI`. Five workers formatted their own before. `transport.ids_from_uri` replaces three separate copies of the same parse in `worker_tts`, `worker_layers` and `worker_render`. It raises on a uri too short to carry the ids instead of returning a wrong pair. `worker_render._mc_from_uri` is gone, its 6 call sites repointed. ### Lint `ruff check .` exits 0. Ruff's defaults found 115. Fixed: implicit `Optional` in 8 signatures, an unparenthesized implicit concatenation inside the ASS filter list, 5 `subprocess.run` calls now saying `check=False` out loud, 1 unused import, 1 duplicate exception handler, 1 non-executable shebang, 4 `dict()` calls and 2 `startswith` chains. 12 rules are off in `ruff.toml`, each with its reason. The ASYNC ones matter: ffmpeg and ffprobe run synchronously inside `async def` endpoints, so a busy worker cannot answer `/health`. That is already `caveats/audit-open.md#blocking-event-loop`, tracked as [#199], and it needs a per-handler refactor rather than a lint fix. `CLAUDE.md` said "There is no lint or build step". It now names `ruff check .` and the rule that every output URI is a template in `transport.py`. ## Audit findings Applied: - `delete:` `attic/worker_ocr.py` and `attic/worker_parse.py`, 224 lines, imported by nothing and named in no doc. `attic/char-recognition.md` and `attic/plan-workpc.md` kept, they are design history. - `delete:` `worker_vision._panel_size`, 8 lines, one reference and it is the definition. Found and NOT applied, in order of size: - `shrink:` `call_gemma4`, `_extract_json` and `_strip_thought` each exist twice, in `worker_vision.py` and `worker_script.py`, and **have already diverged**. `worker_vision.call_gemma4` is 21 lines and takes a content list, `worker_script`'s is 8 and takes a prompt string plus a system prompt. The two `_extract_json` bodies carry the same comment about `raw_decode` but different error text. This is the live risk: `caveats/audit-open.md#repair-fabricates` says the JSON repair pass can fabricate dialogue, and a fix would land in one copy. A shared `gemma.py` costs one new file and removes about 25 duplicated lines. Not done because it touches the two largest workers and nothing has run on a GPU since. - `stdlib:` `worker_render._wrap2`, 16 lines of greedy word-wrap with a 2-line cap, is close to `textwrap.wrap(text, width, max_lines=2, placeholder="…")`. Not identical: `_wrap2` does `rstrip(".,")` before the ellipsis, and textwrap reserves width for the placeholder, so the break points differ. It renders burned-in subtitles, so the diff is user-visible. Verify against the render self-check before swapping. - `shrink:` `_letterbox` and `_load` are duplicated between `bubble_detect.py` and `face_detect.py`, two ONNX detectors with the same preprocessing. About 16 lines. - `delete:` `worker_layers.py:14` points `LAYERED_WORKFLOW` at `legacy/qwen_layered_workflow.json`. `legacy/` was **never tracked in git** and is absent from disk, so the module's self-check has never been able to pass here. This is the other half of `caveats/audit-open.md#layers-writes-nothing`. ## Checks ```bash ruff check . # All checks passed .venv/bin/python worker_render.py # ok, ffmpeg ran, about 4 minutes ``` Self-checks pass: `transport`, `collage`, `bubble_detect`, `test_vision_parse`, `worker_crop`, `worker_scene`, `worker_script`, `worker_identity`, `worker_tts`, `session_manager`, `worker_vision`, `worker_render`, `face_detect`. `worker_layers` fails, and did before this branch, on the missing `legacy/` file above. ## Next command The fifth GPU cycle is still the next pipeline work. It is blocked only on the GPU being free. The exact sequence is in `JOURNAL.md` under the seventh session, and `NEXT.md` item 1 holds the expectations. **Clear the panels prefix first** or the wired caption merge will not take effect. Before that, or instead of it while the GPU is busy, `ARCHITECTURE.md` step 0 is the measurement spine: `eval/chapter-truth.json` plus purity and fragmentation printed by `audit_registry.py`. One ordering trap, unresolved: the truth file cannot be keyed on `panel_id` or `character_id`. The fifth cycle re-crops and calls `/characters/reset`, which destroys both. Key it on page-space geometry, or write it after the fifth cycle rather than before.