refactor(agents): migrate .agent/ planning tree to .agents/

Standardize on the .agents/ directory (shared with project skills):
phases/, user_stories/, reports/, screenshots/, validate.sh, and
phase-sessions/ + pipeline.log all move to .agents/ (git mv preserves
history; runtime artifacts move alongside).

Updates every reference in AGENTS.md, README.md, .gitignore, app
docstrings, and test story headers. Historical KB content in data/
and the runtime pipeline.log transcript are left untouched.
This commit is contained in:
2026-09-05 10:57:07 -04:00
parent 766702c750
commit dbf2af26c6
1118 changed files with 664 additions and 664 deletions
@@ -0,0 +1,54 @@
# Phase 62 — UI Customization (deploy with your own look)
**Source:** `TODO.md` L3 — "Allow UI customization. This is brain of reese, but I want anyone to be able to deploy it with their name. The project should support an environment variable to call it 'Brain of <name>' along with custom message-input placeholder, custom footer-inner text, custom color themes, and anything else that an end-user might want to customize."
**Story:** n/a (TODO-derived — owner roadmap confirmation 2026-09-01; builds on the COMPLETED phase-39 capability, whose dir was never filed under `complete/` — the code + `tests/e2e/test_configurable_brand.py` exist, commit `fe55be0`)
**Context:** The app-name half of this TODO item is DONE (phase 39): `BOR_APP_NAME` → `Settings.app_name` (`app/config.py` L43) → `GET /api/config` (`app/api/config.py`, public, returns `{app_name, version, docs_repo_configured}`) → the classic `frontend/assets/brand.js` layer (parse-time `window.BOR_BRAND`, one `cache: "no-store"` fetch whose never-rejecting promise is `window.BOR_CONFIG_PROMISE`, DOM passes for title / `.brand-text` / text nodes / aria-label+placeholder+meta attributes; fetch failure ⇒ default stands + `console.warn`, the page never breaks). This phase extends that EXACT mechanism to three more settings — no new network surface, no new script, no templating engine (the frontend stays `StaticFiles(html=True)`, `app/main.py` L101):
- Composer placeholder: `#message-input` exists only on `frontend/index.html` (the chat page).
- Footer text: after phase 61, ALL 9 pages carry `<span class="footer-text">…</span>` inside `.footer-inner`.
- Theme: the entire palette is `:root` custom properties in `frontend/assets/styles.css` L7–25 (`--bg, --surface, --ink, --ink-soft, --line, --brand, --brand-soft, --brand-ink, --accent-bg, --accent-ink, --accent-line, --ok-bg, --ok-ink, --err-bg, --err-ink, --err-line`) — a theme file overriding those variables is a drop-in; no component CSS references literal colors except via the variables (spot-verify while authoring the example theme).
- No-CDN rule (AGENTS.md rule 6): theme CSS is served from the FastAPI static dir (`frontend/` in dev via `BOR_STATIC_DIR`; `/app/static` in the container).
- `Containerfile` stage 1 esbuild/minify chain + per-page HTML copy does NOT ship a new `frontend/assets/themes/` dir — one line is added (task 03, A7).
- Contract tests that must be updated when `/api/config` grows: `tests/integration/test_api.py` L21–46 (exact key set, two tests) and `tests/e2e/test_configurable_brand.py` L150 + L158 (exact key set); `tests/unit/test_frontend_brand.py` pins the brand layer.
- The phase-39 E2E boots its own second instance (`testy_server`, `APP_PORT + 1`, `BOR_APP_NAME` override) — phase 62's E2E reuses that pattern on a distinct port (task 05).
- `.agents/` is UNTRACKED (owner instruction, commit 281f355) — the phase commit stages `app/ frontend/ tests/ .env.example README.md Containerfile`.
## Objective
Anyone can deploy BOR under their own identity with env vars alone: the app name (already shipped) plus a custom composer placeholder (`BOR_INPUT_PLACEHOLDER`), custom footer line (`BOR_FOOTER_TEXT`), and a custom color theme (`BOR_THEME=indigo.css` → `frontend/assets/themes/indigo.css`). Unset ⇒ byte-identical to the phase-61 neutral defaults.
## Dependencies
- `61_stale_ui_copy` (todo, preceding — FUNCTIONAL: phase 61's neutral strings become these Settings' DEFAULTS, and its `.footer-text` class is the footer selector this phase drives; the "unset ⇒ byte-identical" contract is measured against phase-61 copy)
## Tasks
1. `01_config_settings.md` — three new `Settings` fields (+ fail-loud `theme` filename validator) + three new `GET /api/config` keys; update the exact-key-set contract tests (integration + the phase-39 E2E).
2. `02_brand_layer_customization.md` — `brand.js` applies placeholder / footer text / theme link from the same boot fetch; unit pins.
3. `03_theme_assets.md` — `frontend/assets/themes/indigo.css` example theme (WCAG-AA palette) + `themes/README.md` authoring guide + the one `Containerfile` line.
4. `04_docs.md` — `.env.example` + README env table + "Customizing the look" section.
5. `05_e2e_customization.md` — `tests/e2e/test_ui_customization.py` (own instance, all three vars set; default instance byte-identical; malformed-theme fail-loud), regression suites, one atomic commit.
## Testing & Quality
- Unit: `theme` validator (empty ok; bare filename ok; path/`..`/non-`.css` rejected naming the value); `GET /api/config` returns the six keys with defaults AND with overrides; brand-layer JS pins (house style).
- Integration: the config contract tests updated to the six-key set (task 01).
- E2E (mandatory, house rule): `tests/e2e/test_ui_customization.py`, run in isolation — overridden instance shows custom placeholder/footer/computed theme color on the pages; the shared default server is byte-identical (default placeholder/footer, NO theme link, built-in `--brand`).
- Coverage: **>90%** on `app/` (validate.sh gate).
## Completion Criteria
- [ ] `GET /api/config` returns exactly `{app_name, version, docs_repo_configured, input_placeholder, footer_text, theme}` — proven by integration + E2E.
- [ ] With `BOR_INPUT_PLACEHOLDER` / `BOR_FOOTER_TEXT` / `BOR_THEME=indigo.css` set: the chat composer placeholder, the footer line on multiple pages, and the computed `:root --brand` all reflect the overrides; a `<link href="/assets/themes/indigo.css">` is present.
- [ ] With nothing set: placeholder `Ask me anything…`, footer `Powered by self-hosted models`, no theme link, `--brand` = built-in `#f43f5e` — the phase-39/61 no-op contract holds.
- [ ] `BOR_THEME=../evil.css` (or any malformed value) refuses startup, naming the value (phase-56 house style).
- [ ] `uv run pytest` green; coverage TOTAL >90%; `uv run ruff check . && uv run pyright` clean.
- [ ] `uv run pytest tests/e2e/test_ui_customization.py -v --no-cov` green in isolation (DB up).
- [ ] Regression E2E suites green in isolation: `test_configurable_brand.py`, `test_stale_ui_copy.py`, `test_smoke.py`, `test_dark_tech_theme.py`.
- [ ] `frontend/assets/themes/` ships in the container image (Containerfile line) and serves in dev (static dir).
- [ ] One `--no-gpg-sign` commit; phase dir moved to `.agents/phases/complete/` (`.agents/` stays untracked).
## Locked decisions
- **Owner-locked (2026-09-01, roadmap confirmation, A4):** env var names — `BOR_INPUT_PLACEHOLDER`, `BOR_FOOTER_TEXT`, `BOR_THEME` (theme value = a FILENAME, e.g. `indigo.css`).
- **Owner-locked (2026-09-01, roadmap confirmation, A5):** the theme mechanism — `BOR_THEME` names a file under `frontend/assets/themes/` served statically by FastAPI (no-CDN intact); the file overrides the `:root` palette variables; the example theme is `indigo.css` (dark indigo/slate, every text/background pair ≥4.5:1); a MALFORMED `BOR_THEME` (anything not matching `^[a-z0-9_-]+\.css$`) fails startup loudly, naming the value (phase-56 house style); a theme file MISSING at runtime degrades to the built-in theme (`console.warn`, the page never breaks — the loadHealth/brand.js house style).
- **Owner-locked (2026-09-01, roadmap confirmation, A6):** the TODO's open-ended "anything else that an end-user might want to customize" is scoped to these three vars + the existing `BOR_APP_NAME` / `BOR_SUGGESTIONS`; the settings → `/api/config` → `brand.js` path is the extension point for future items (no further surface invented now).
- **Owner-locked (2026-09-01, roadmap confirmation, A7):** the Containerfile ships themes via `cp -r ./assets/themes /out/assets/themes` in stage 1 (no per-file esbuild minify — a future theme file needs no Containerfile edit).
## Commit
```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"
```