phase: 190_app_name_settings_fix
All gates re-verified green. Report: - **Verified (all 4 tasks already complete; this was the final pass):** `effective_settings` resolves the 3 brand strings panel-row → tab-row → env (`applied_override_keys`, presence-not-value); `PUT /api/ui-settings` ignores string body fields; Theme-tab form is colors-only (17 pickers, no `#theme-app-name`/`#theme-placeholder`/`#theme-footer`); the ONE shared `applyBrandFromConfig` re-applies title/`.brand-text`/footer/placeholder after a panel save-reset and a tab save-reset; boot path byte-identical (single `/api/config`). - **Completion criteria:** - strings order panel→tab→env, both branches — ✅ `tests/unit/test_theming.py::test_effective_app_name_prefers_settings_panel_row_over_ui_settings_row`, `..._fall_back_to_ui_settings_row_without_panel_row`, `..._per_field_panel_then_tab_then_env`, `..._ignore_panel_rows_when_the_overlay_is_off` - PUT ignores string fields + no 3 inputs in the tab — ✅ `tests/integration/test_ui_settings_api.py::test_put_ignores_string_fields_in_body` (+2) and the E2E `test_theme_tab_dom_is_colors_only` - live apply without reload + incident regression — ✅ `tests/e2e/test_app_name_settings_fix.py` tests 1–4 - byte-identical boot, no double-apply — ✅ `tests/unit/test_brand_live.py` markers + E2E "exactly one /api/config" - suite green, coverage >90%, ruff+pyright clean — ✅ - no behavior change in prior phases — ✅ (palette, phase-168 panel, env fallback suites all green) - atomic commit — **left to the harness** (pipeline rule overrides per-phase committing; no git write run) - **Gates:** `uv run pytest --cov=app --cov-report=term-missing` → **4630 passed, TOTAL 6941 16 99%**, exit 0 · `uv run pytest tests/e2e/test_app_name_settings_fix.py -v --no-cov` → **6 passed** in isolation · regression E2E (isolated, one process each, all exit 0): `test_smoke` `test_configurable_brand` `test_admin_theme_tab` `test_theme_save_and_coverage` `test_theme_semantic_completion` `test_themed_favicon` `test_dark_tech_theme` `test_admin_settings_panel` `test_admin_settings_layout` `test_settings_mobile_value_width` · `uv run ruff check .` → All checks passed · `uv run pyright` → 0 errors. - **Documentation:** README — fixed "saving one repaints *every* open page" (only the open document repaints; other pages on next load) and a broken paragraph wrap in *Customizing the look*; AGENTS.md rule 16 — `test_chunks_hnsw_recall.py` now described accurately (index paths forced with `enable_seqscan`/`indexonlyscan`/`bitmapscan` off, unforced plan asserted only on a pristine relation); `tests/unit/test_theme_frontend.py` — two stale section headers ("20 form fields"/"20 inputs" → 17 color). No AGENTS.md settings-overlay/resolution-order clause exists, so per the phase none was added. Full env-var cross-check (`BOR_*` in `app/config.py` vs README) → 0 gaps; 0 missing file references. - **Notable:** the working tree also carries this phase's flake hardening of the phase-188 `tests/integration/test_chunks_hnsw_recall.py` (verified legit and green). Live infra untouched; shared DB left clean (0 `app_settings`, 0 `ui_settings`). **Flagged, not changed:** `settings_overlay.applied_override_keys` lets a `ProgrammingError` (missing `app_settings`) propagate from public `GET /api/config`, where `load_overrides` deliberately fails soft — only reachable on an un-migrated (0027) DB, outside this phase's pinned A1 semantics, so left for the owner to rule on rather than silently deviating. - **Next pending phase:** `191_source_name_labels_tags`.
This commit is contained in:
@@ -324,6 +324,11 @@ Admin-only — anonymous visitors see a sign-in gate.
|
||||
The overlay can be switched off for the whole process with
|
||||
`BOR_SETTINGS_DB=0` (env-only; the panel stays visible and says so).
|
||||
|
||||
This panel is also the **single writable owner of the three brand strings**
|
||||
(`app_name`, `input_placeholder`, `footer_text`): saving one repaints the page
|
||||
you are on the moment you click, and the Theme tab can no longer write them —
|
||||
see *Customizing the look*.
|
||||
|
||||
### Shared chat (`/shared/<token>`)
|
||||
|
||||
A read-only snapshot of any conversation, shareable via a link. Open to
|
||||
@@ -1692,7 +1697,9 @@ Everything is an environment variable (the `BOR_` prefix, plus
|
||||
to `.env` and adjust.
|
||||
|
||||
**DB-over-env overrides (phase 168):** the precedence for every setting
|
||||
is **`app_settings` row → `BOR_*` environment → code default** — an
|
||||
is **`app_settings` row → `BOR_*` environment → code default** (the
|
||||
three brand strings have one extra tier between the row and the env —
|
||||
the legacy `ui_settings` Theme-tab row, see *Customizing the look*) — an
|
||||
override the admin saves in the Settings panel is stored in Postgres
|
||||
(`app_settings`, one row per key) and **wins over the env var for
|
||||
that key — with no restart**. The panel never writes `.env` (the env
|
||||
@@ -1862,18 +1869,53 @@ startup (the DB rows shadow the env, `.env` is never rewritten).
|
||||
|
||||
### Customizing the look
|
||||
|
||||
Every identity string is an env var: `BOR_APP_NAME` (display name),
|
||||
`BOR_INPUT_PLACEHOLDER` (chat composer placeholder), and
|
||||
Every identity string defaults from an env var: `BOR_APP_NAME` (display
|
||||
name), `BOR_INPUT_PLACEHOLDER` (chat composer placeholder), and
|
||||
`BOR_FOOTER_TEXT` (footer line) — all served by `GET /api/config` and
|
||||
applied by `assets/brand.js` at boot.
|
||||
|
||||
The colors are set from the admin **Theme tab** (`/theme.html`,
|
||||
admin-only — phase 91): the 17 palette colors (the 9 identity colors plus
|
||||
the 8 semantic state colors — success/error/accent families) and the
|
||||
three strings above are edited with text fields and color pickers,
|
||||
persisted in the `ui_settings` table, and injected into every served page
|
||||
as an inline `<style>` BEFORE first paint (no red flash, no pop-in). The
|
||||
browser-tab **favicon** follows the same effective palette: `GET
|
||||
**The admin Settings panel (`/settings.html`) is the single writable
|
||||
owner of those three brand strings** (phase 190, A1). Their resolution
|
||||
order is **panel row → Theme-tab row → env**: saving `app_name` there
|
||||
wins over everything, and it beats a value an old Theme-tab save baked
|
||||
into `ui_settings` (the fix for "changing the App name in settings
|
||||
broke, it no longer updates the name"). `PUT /api/ui-settings` still
|
||||
accepts the three string fields so an old cached client keeps working,
|
||||
but it **ignores** them and never writes (or clears) those columns — a
|
||||
Theme-tab save can never re-bake a name. A string is reset with the
|
||||
panel's `DELETE /api/admin/settings/{key}` (back to the Theme-tab row if
|
||||
one still holds a value, else back to the env var), and with
|
||||
`BOR_SETTINGS_DB=0` no panel row is in effect, so the old order —
|
||||
Theme-tab row, then env — stands.
|
||||
|
||||
**The save is visible without a reload** (phase 190, task 03). Saving one of
|
||||
the three brand strings in the panel — or resetting it with
|
||||
`DELETE /api/admin/settings/{key}` — re-reads `GET /api/config` and applies it
|
||||
to the OPEN document through the ONE shared brand pass (`assets/brand.js`'s
|
||||
`applyBrandFromConfig`, the very function the boot runs): the page title, the
|
||||
header brand, the footer line and the chat composer placeholder update on the
|
||||
click, no reload — including a rename of an app whose old name is already on
|
||||
the page. The re-apply runs only when one of those three keys was involved (the
|
||||
other ~72 rows cost no extra request), never stacks a second fetch while one is
|
||||
in flight, and a failed one leaves the page exactly as it showed — the save's
|
||||
own status or error line stays the feedback. The Theme tab runs the same pass
|
||||
after its Save and its Reset; it no longer edits the strings, so that read is
|
||||
what keeps an open tab honest about a name the panel changed meanwhile.
|
||||
The whole contract — the incident regression (a color save no longer shadows a
|
||||
later rename) and the live repaint of title / brand / footer / placeholder — is
|
||||
pinned in a real browser by `tests/e2e/test_app_name_settings_fix.py` (run in
|
||||
isolation).
|
||||
|
||||
The colors stay the **Theme tab's** (`/theme.html`, admin-only — phase
|
||||
91): the 17 palette colors (the 9 identity colors plus the 8 semantic
|
||||
state colors — success/error/accent families) are edited with color
|
||||
pickers and nothing else, persisted in the `ui_settings` table, and
|
||||
injected into every served page as an inline `<style>` BEFORE first
|
||||
paint (no red flash, no pop-in). The tab's form renders those 17 pickers
|
||||
only — its old "Branding" fieldset (`#theme-app-name`,
|
||||
`#theme-placeholder`, `#theme-footer`) is gone, and its PUT body carries no
|
||||
string key. The browser-tab **favicon** follows the same effective palette:
|
||||
`GET
|
||||
/favicon.svg` renders the hexagon art from the saved colors on every
|
||||
request (no process cache — a theme change is visible on the next
|
||||
request, no restart) and degrades to the built-in art with 200 on a
|
||||
|
||||
Reference in New Issue
Block a user