# Task 05 — Dedicated E2E + regression pass + atomic commit **Phase:** `62_ui_customization` · **Source:** `TODO.md:3` — "Allow UI customization. This is brain of reese, but I want anyone to be able to deploy it with their name…" **Story:** n/a (TODO-derived) ## Objective One dedicated Playwright suite proves the whole contract end-to-end — an instance booted with all three customization vars shows the custom look, the default shared server is BYTE-IDENTICAL to today, and a malformed `BOR_THEME` refuses to boot — then one atomic commit and the phase is done. ## Work 1. `tests/e2e/test_ui_customization.py` (NEW — one story, one file, run in isolation, DB up). Boots a second app instance exactly the way `test_configurable_brand.py`'s `testy_server` does (same env block, same DB, mock-LLM base URL, admin auth, static dir, mock-calibrated threshold) with THREE changes: port `APP_PORT + 2` (the brand suite owns `APP_PORT + 1` — do not collide), and the env overrides `BOR_INPUT_PLACEHOLDER="Ask the archive…"`, `BOR_FOOTER_TEXT="Custom footer line"`, `BOR_THEME="indigo.css"`. Test → contract mapping: 1. `test_config_serves_the_overrides` — the instance's `GET /api/config` returns the three overridden values alongside the six-key set (the name stays the DEFAULT — this suite does not re-test `BOR_APP_NAME`; that is the phase-39 suite's job). 2. `test_chat_page_shows_custom_placeholder_footer_theme` (instance) — on the chat page: `#message-input` placeholder === `Ask the archive…`; the first `.footer-text` textContent === `Custom footer line`; a `link#theme-override[rel="stylesheet"][href="/assets/themes/indigo.css"]` exists in the ``; `getComputedStyle(document.documentElement).getPropertyValue("--brand").trim()` === `#818cf8` (the indigo value — custom properties return the specified token, so the string compare is stable; the built-in is `#f43f5e`). 3. `test_footer_text_applies_on_other_pages` (instance) — on a second page (e.g. `/login.html`): the `.footer-text` textContent === `Custom footer line` (multi-page proof, no composer on that page — the placeholder application no-ops without error). 4. `test_default_server_is_byte_identical` (shared conftest server) — placeholder === `Ask me anything…`; footer === `Powered by self-hosted models`; NO `#theme-override` link; computed `--brand` === `#f43f5e` (the phase-39/61 no-op contract). 5. `test_malformed_theme_refuses_startup` — boot a short-lived uvicorn subprocess with `BOR_THEME="../evil.css"` (any port): the process exits non-zero within the timeout (or never starts listening) and its stderr names the value — the phase-56 fail-loud style, proven end-to-end, not just via the validator unit test. - Determinism: settled-state assertions throughout; Playwright `expect` retries ride out the brand.js fetch (the same pattern the brand suite documents in its module docstring). 2. Regression E2E pass (each in isolation, DB up): `test_configurable_brand.py` (its key-set assertions were updated in task 01), `test_stale_ui_copy.py` (phase 61's copy contract is this phase's baseline), `test_smoke.py`, `test_dark_tech_theme.py` (the built-in palette is untouched when unset). 3. One atomic commit (`.agents/` untracked — owner instruction, commit 281f355): ```bash git add app/ frontend/ tests/ .env.example README.md Containerfile && git commit --no-gpg-sign -m "feat(web): customizable placeholder, footer text, and color theme via BOR_* env vars" ``` Commit body: one line — the three env vars + the indigo example theme + the docs/theme-guide pointers. ## Testing & Quality - `uv run pytest` green (unit + integration); coverage TOTAL **>90%** (validate.sh gate). - `uv run pytest tests/e2e/test_ui_customization.py -v --no-cov` green in isolation. - `uv run ruff check . && uv run pyright` clean. ## Completion Criteria - [ ] `tests/e2e/test_ui_customization.py` exists (5 mapped tests) and is green in isolation (DB up). - [ ] The four regression E2E suites are green in isolation. - [ ] Full suite green, coverage >90%, lint + types clean. - [ ] Exactly one new commit on `main`, staged paths `app/ frontend/ tests/ .env.example README.md Containerfile`, `--no-gpg-sign`. - [ ] Phase dir moved to `.agents/phases/complete/62_ui_customization/` (`.agents/` stays untracked).