All gates green. Verification complete.
**Phase 123 — final verification pass (all 4 tasks already in `complete/`)**
- Verified the full implementation is in the working tree: `app/api/chat_images.py` (upload/serve pair), `ChatRequest.image`/`ChatMessage.image` (path-validated, omitted-when-None), toggle-off + stale-file hinted error frames, `build_user_content` multimodal build at both sites (chat.py deflected branch + `run_agent`), config-gated composer attach/preview/upload-then-send, restore + shared rendering, CSP `img-src 'self' data:` carve-out, mock-LLM capture buffer.
- `uv run pytest` → **2796 passed**, exit 0 (unit + integration).
- `uv run pytest --cov=app --cov-report=term-missing` → **TOTAL 99%** (29/4615 missed; phase-123 modules 99–100%).
- `uv run pytest tests/e2e/test_chat_image_questions.py -v --no-cov` → **5 passed** in isolation.
- `uv run ruff check . && uv run pyright` → clean (0 errors).
**Completion criteria:** (1) attach→send→multimodal text+image to the model, bubble/reload/shared all render it, saved chat stores the PATH with `"base64" not in json.dumps(stored)` — **verified** (E2E tests 1–4 + integration round-trip); (2) `BOR_IMAGES=false` — control hidden, exact hinted error frame, zero model calls / no query_log row — **verified** (E2E test 5 + integration); (3) text-only byte-identical (`content` stays a plain `str`) — **verified** (unit + integration); (4) all gates green — **verified**; (5) commit + phase move — left to the harness per pipeline rules (no `git add`/`commit` run).
No defects found; no live-infrastructure changes (repo + local dev DB only). **Next pending phase: none** — 123 is the last phase in `todo/`.
The client-side TURN_TIMEOUT_MS was the binding constraint: turns with
slow prompt processing (no first SSE frame within 120 s of visible time)
errored with the 'stuck' copy even though nginx (300 s) and
BOR_LLM_TIMEOUT (300 s) would have let them run. Raise the guard to
300 s so the upstream timeouts are reachable, and re-pin the tests:
the unit constant pins and the fake-clock E2E timeline (295 s hidden
+ 290 s after the re-arm = 585 s: past the original 300 s deadline,
short of the re-armed 595 s deadline).
Verified: tests/unit (full, 100% pass), tests/e2e/test_hidden_tab_stream.py
and tests/e2e/test_loading_feedback.py in isolation.
One env var (BOR_APP_NAME, default "Brain of Reese") now drives the app's
display name everywhere (TODO.md L12 — owner ask: "a way to customize the
name for 'Brain of'. Should be an env var."). The existing app_name setting
is the source of truth (phase locked decision — no new variable, no rename);
with the variable unset the app is byte-identical to before.
Endpoint (A10 public/stateless, no secrets):
GET /api/config → exactly {app_name, version} (app/api/config.py, the
health.py pattern; registered before the static mount). Integration tests:
anonymous 200, default values, a Settings override follows, key set is
exactly two keys — no other setting may leak in later.
Frontend brand layer (A11 — runtime fetch, static templates stay static):
assets/brand.js — a CLASSIC script, first on all six pages, so its top
level runs at parse time: window.BOR_BRAND = "Brain of Reese"
synchronously (the default renders immediately, no blank flash), then a
no-store fetch of /api/config applies the name — document.title (global
replace), every .brand-text (a name starting "Brain of " keeps the bold
split Brain of <strong>rest</strong>, any other name renders plain; the
operator-controlled name is HTML-escaped before innerHTML), a TreeWalker
over text nodes (script/style rejected — page source never rewritten),
and the aria-label/placeholder/meta-content attributes. Fetch failure
keeps the default + console.warn (the loadHealth house style).
app.js (status labels, typing label, elapsed-hint aria, tool labels) and
document.js (viewer titles) read window.BOR_BRAND at CALL time via
brand() — a label set after the fetch lands carries the configured name.
Containerfile: esbuild minify line for brand.js (classic, like markdown.js);
the phase-33 ?v= cache-busting picks the new asset ref up automatically.
E2E (A16 — one story, one file, isolated): test_configurable_brand.py boots
a SECOND app instance (same DB/mock-LLM/admin-auth env block, port APP_PORT+1,
BOR_APP_NAME="Brain of Testy") — the shared conftest server keeps the
default name so every other suite's title/label assertions stay untouched —
and asserts /api/config on both instances, the index title/brand/greeting/
#messages aria-label, the sources + login page titles, and one pre-token
chat turn (think out loud marker) whose #send-status reads "Brain of Testy
is thinking"; the no-op regression pins the shared server's default bytes.
Docs: .env.example App section + README configuration reference — what it
affects (titles, header brand, status labels, aria text), the default, the
bold-split rendering rule.
Gates: 695 unit+integration passed, app/ coverage 99% (>90%), story E2E
green in isolation (two consecutive runs), brand-string suites (smoke,
shared header, header consistency, chat persistence) green, ruff + pyright
clean.