phase: 100_page_width_consistency
**Phase 100 — final verification pass: all green.** - Verified the shipped CSS contract directly: `--chat-column: 72rem` in `:root`; 0 literal `max-width: 46rem`; no `@media (min-width: 1500px)` block; exactly 4 token-capped reading columns; tuning/theme/doc-edit shells cap-free, structurally `.sources-shell`; `mock_llm.py` diff is comment-only. - Defect found & fixed (phase-93 suite): `test_theme_semantic_completion.py::test_reset_removes_tag_byte_identical` raced theme.js's post-PUT refetch — it asserted the `#bor-theme` tag was gone right after the result line, but tag removal lands in the reconcile after the re-fetch (failed ~1 in 5 runs after `test_chat_rag`, reproduced). Fixed with an auto-waiting `expect(...).to_have_count(0)` (settled-state wait, the file's existing house pattern). 6/6 clean on the repro loop after. - Tests/lint/coverage: `uv run pytest --cov=app --cov-report=term-missing` → 2052 passed, **99%** on `app/`; `uv run ruff check .` + `uv run pyright` → 0 errors. - E2E in isolation (all passed): `test_wide_desktop_column.py` 3 (the phase suite — chat==tuning==theme==RAG ±4px at 1280 & 1920, ≈1152px; shared ≈1152px; standalone doc ≈1112px; modal unchanged ≈1100px; 360px overflow-free), `test_ui_customization` 4, `test_admin_theme_tab` 5, `test_document_viewer` 7, `test_save_share_ux` 5, `test_sticky_navbar` 3, `test_markdown_tables` 6, `test_responsive_polish` 7, `test_chat_rag` 3, `test_theme_semantic_completion` 8. - Completion criteria: ① measured 72rem everywhere (≥~1200px, ±4px) + full-width below + 360px clean — **PASS** (E2E); ② zero 46rem rules / no 1500px block / four token selectors — **PASS** (grep + unit pins); ③ B4 byte-identical no-op + mobile squeeze — **PASS** (theme + responsive suites green); ④ full suite / coverage / lint — **PASS**; ⑤ atomic commit — left to the harness per executor protocol (all changes in the working tree, uncommitted). - Deviations: none from phase decisions; only change in this pass is the race fix above (test-only, behavior unchanged). - Next pending phase: `98_sync_summary_visibility` (numeric order in `todo/`; also pending: 99, 101–105).
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
# Phase 100 — Every page matches the RAG page's width (the 72rem container, at every viewport)
|
||||
|
||||
**Source:** Owner request (chat, 2026-09-12) — "The theme, tuning, and chat pages are still pretty narrow, I want you to match the width of the RAG page for all other pages to keep things consistent."
|
||||
**Story:** n/a (owner request — supersedes the PLAN §7.1 "centered 46rem chat column (2× = 92rem at ≥1500px)" rule and the phase-58/27/91 form-column caps; the width lineage: `58_wide_desktop_column`, `86_history_page_width`).
|
||||
**Context:** `frontend/assets/styles.css`: `.container` is the shared 72rem centered column (`width: 100%; max-width: 72rem; padding-inline: 1.25rem;`). The RAG view's `.sources-shell` (and `.git-sources-shell` / `.history-shell` / `.tokens-shell`) carry NO max-width — their content is the full 72rem container: **that is the width the owner wants everywhere**. The narrow pages: `:root { --chat-column: 46rem; }` caps `.chat-shell` (the chat view — the div IS `.container.chat-shell`, the later rule overriding the 72rem cap), `.shared-shell` (shared.html), `.doc-md` (the document viewer's reading column) and `.doc-summary:has(+ .doc-md)` (exactly four selectors use the token); `@media (min-width: 1500px) { :root { --chat-column: 92rem; } .doc-shell { max-width: calc(var(--chat-column) + 2 * 1.25rem); } }` (phase 58 — the wide-desktop doubling + the standalone document page's cap lift); and three hard-coded `max-width: 46rem` form columns: `.tuning-shell`, `.theme-shell` (phase 91), `.doc-edit-shell` (phase 59). Width pins live in: `tests/unit/test_wide_column_css.py` (the phase-58 contract file — 46rem base, the 92rem media block, the four-token count, the three-hard-coded-46rem negative pin, the provenance comments), `tests/unit/test_markdown_tables.py`, `test_shared_page.py`, `test_pinned_composer.py`, `test_doc_edit_screen.py`, `test_remove_confirm_modal.py`, `test_source_ignore_paths.py` (grep `46rem|92rem|chat-column` under `tests/` for the authoritative list — some hits are fixture text, judge per pin), and E2E measured-width suites: `tests/e2e/test_wide_desktop_column.py` (the measured 1472px/736px contract), `test_document_viewer.py`, `test_save_share_ux.py`, `test_sticky_navbar.py`, `test_markdown_tables.py`, `test_responsive_polish.py` (mobile no-overflow).
|
||||
|
||||
## Objective
|
||||
Every view and page renders its content at the RAG page's width — the full 72rem container at every viewport (centered, with the container's 1.25rem gutters): the chat column, the Tuning and Theme form columns, the doc-edit screen, the shared page, and the document viewer all widen to 72rem; the phase-58 ≥1500px 92rem doubling and the 46rem form caps are retired; the mobile (≤640px) and tablet layouts are unchanged (everything was already full-width below the caps).
|
||||
|
||||
## Dependencies
|
||||
- `86_history_page_width` / `58_wide_desktop_column` (complete) — the width lineage being revised (their suites are updated IN THIS PHASE, the phase-97 task-07/08 precedent — asserted behavior preserved, only the width contract changes).
|
||||
- No code dependencies beyond `frontend/assets/styles.css` (CSS-only — `app/`, `frontend/*.html`, and every JS module are untouched).
|
||||
|
||||
## Decisions recorded here (owner instruction 2026-09-12 — PLAN.md is being redone by the owner)
|
||||
- **D1 — one width for everything:** the reading/form columns ALL ride the 72rem container. `:root { --chat-column: 72rem; }` (the token stays — the four reading-column selectors keep `max-width: var(--chat-column)`, so `.doc-md`/`.shared-shell`/`.chat-shell`/the doc-summary panel follow automatically); the `@media (min-width: 1500px)` block is DELETED entirely (the 92rem doubling AND the `.doc-shell` cap lift — with the token at 72rem the standalone document page's `.container.doc-shell` simply keeps the 72rem cap); the three hard-coded `max-width: 46rem` caps (`.tuning-shell`, `.theme-shell`, `.doc-edit-shell`) are REMOVED (plus their `margin-inline: auto` centering — the `.container` ancestor centers; the shells keep their flex-column + gap rules, matching `.sources-shell` exactly). Net effect: ZERO literal `max-width: 46rem` rules remain in the file (the negative pin flips). This SUPERSEDES PLAN §7.1's 46rem/92rem rule and the phase-58 owner instruction of 2026-08-31 (recorded here per the phase-94/96/97 convention — the owner's new instruction wins; the PLAN.md anchor table gets the owner's sign-off separately).
|
||||
- **D2 — the document viewer's modal is untouched:** the same-page doc modal's 1100px panel stays its effective ceiling (`.doc-md` at 72rem is wider than the panel's inner width, so the panel binds — no panel resize, no modal change). The standalone `document.html` page rides the 72rem container like every other page.
|
||||
- **D3 — CSS-only, contracts intact:** no HTML change (the B4 byte-identical theme contract is unaffected — the served HTML is unchanged; only `styles.css` bytes change), no JS change, no endpoint change; A19's `?v=<deploy-token>` versioning busts the old CSS automatically. The `≤640px` mobile block is UNCHANGED (at 360px the container is already 100% — the caps never bound there; the mobile squeeze rules for chat/shared/tokens/theme keep working).
|
||||
- **D4 — the comment sweep:** every stale "46rem base, 92rem at >=1500px" / "46rem column contract" / phase-58-provenance comment in `styles.css` is updated to the new contract (the owner instruction 2026-09-12 as provenance at the token + the note that the wide-desktop doubling was retired) — the house rule that comments carry the contract (and `test_wide_column_css.py` pins the comment states).
|
||||
|
||||
## Tasks
|
||||
1. `01_width_css.md` — the CSS change: the token to 72rem, the media block deleted, the three form caps removed, the comment sweep
|
||||
2. `02_width_unit_pins.md` — the unit pin updates: `test_wide_column_css.py` rewritten to the new contract + the grep-sweep of the other unit files
|
||||
3. `03_e2e_width_sweep.md` — `test_wide_desktop_column.py` rewritten to the measured 72rem contract + the other E2E width pins + the regression sweep + the atomic commit
|
||||
|
||||
## Testing & Quality
|
||||
- Unit: `tests/unit/test_wide_column_css.py` REWRITTEN to the new contract (the pins below); every other unit file whose pins reference the old widths updated (the grep sweep — `46rem|92rem|chat-column` under `tests/unit/`, judged per pin: a width assertion updates, fixture text does not).
|
||||
- E2E (mandatory, A16): `uv run pytest tests/e2e/test_wide_desktop_column.py -v --no-cov` in isolation — the phase's dedicated width suite, REWRITTEN to the measured 72rem contract (the phase-97 task-07/08 precedent for updating a completed phase's suite when its contract changes); the other measured-width suites updated in place.
|
||||
- Coverage: **>90%** on `app/` (untouched — the full-suite gate still runs).
|
||||
- Lint/types: `uv run ruff check . && uv run pyright`.
|
||||
|
||||
## Completion Criteria
|
||||
- [ ] at any viewport ≥ ~1200px, the chat, shared, document-viewer, Tuning, Theme, and doc-edit content columns measure the 72rem container width (E2E-measured, ±4 px) — identical to the RAG page's; below that, everything is full-width as today; 360px has no horizontal overflow
|
||||
- [ ] `styles.css` has ZERO literal `max-width: 46rem` rules and NO `@media (min-width: 1500px)` block; the four reading-column selectors still cap with `var(--chat-column)` (now 72rem)
|
||||
- [ ] the B4 byte-identical theme contract holds (no HTML change — the theme no-op test suite green); the mobile squeeze layouts green
|
||||
- [ ] `uv run pytest` green; coverage >90%; ruff + pyright clean
|
||||
- [ ] one atomic Conventional Commit, `--no-gpg-sign` (e.g. `feat(ui): match every page to the RAG page's 72rem width`)
|
||||
@@ -0,0 +1,27 @@
|
||||
# Task 01 — The CSS: one 72rem column everywhere (token, media-block deletion, form caps, comment sweep)
|
||||
|
||||
**Phase:** `100_page_width_consistency` · **Story:** n/a (owner request)
|
||||
|
||||
## Objective
|
||||
`styles.css` carries the new width contract (D1–D4 of `00_phase.md`): `--chat-column` is 72rem, the phase-58 ≥1500px block is gone, the three 46rem form caps are removed, and every stale width comment tells the new story.
|
||||
|
||||
## Work
|
||||
1. `frontend/assets/styles.css`:
|
||||
- `:root` — `--chat-column: 72rem;` (was 46rem). The token's provenance comment updates: the owner instruction 2026-09-12 ("match the width of the RAG page for all other pages") + the note that it now EQUALS the `.container`'s 72rem cap (the reading columns ride the same width as every other page; the phase-58 2026-08-31 instruction is superseded).
|
||||
- DELETE the entire `@media (min-width: 1500px) { … }` block at the bottom of the file (the `:root { --chat-column: 92rem; }` doubling + the `.doc-shell { max-width: calc(var(--chat-column) + 2 * 1.25rem); }` lift — D1). Its phase-58 block comment goes with it; the deletion is total (the `prefers-reduced-motion` block that follows it stays).
|
||||
- `.tuning-shell` — remove `max-width: 46rem;` and `margin-inline: auto;` (keep `display: flex; flex-direction: column; gap: 1.25rem; flex: 1;` — the shell now matches `.sources-shell` exactly); update its phase-27 comment (the "Same width discipline as the chat column — a centered, capped column" wording) to the full-container-width contract (owner instruction 2026-09-12).
|
||||
- `.theme-shell` — the same removal (phase-91's "form column" rationale is superseded — the owner wants it wide); comment updated.
|
||||
- `.doc-edit-shell` — the same removal (phase-59's form-column cap is superseded); comment updated.
|
||||
- The comment sweep (D4): every remaining reference to "46rem base", "92rem at >=1500px", "the 46rem chat-column width", "the >=1500px 92rem override", and the stale `≤640rem`/`≤640px`-era "46rem column contract" wording in the reading-column regions (`.chat-shell`'s block comment, `.shared-shell`'s inline comment + the shared-page region comment, `.doc-md`'s comment, the doc-summary panel comment, the `≤640px` shared-page note "the shell keeps its base 46rem column (the >=1500px 92rem override never applies here…)", and any others the grep `46rem|92rem|1500px` finds) — rewrite each to the new contract (72rem at every width; the wide-desktop doubling retired 2026-09-12). The `.shared-shell` comment must keep the "the PLAN §7 centered chat column" lineage sentence only if it still reads true — it does NOT (PLAN §7.1's 46rem rule is superseded): reword to "the shared page reads exactly like the chat page — both at the 72rem container width".
|
||||
- Do NOT touch: the `.container` rule itself, the `≤640px` mobile block (the squeeze rules), the doc modal's 1100px panel, any non-width CSS, any `app/` or HTML/JS file (D3 — CSS-only).
|
||||
2. Verify with a grep: `grep -n "46rem\|92rem\|1500px" frontend/assets/styles.css` returns NOTHING (or only the new provenance comment's mention of the retired 2026-08-31 instruction, if phrased without the literal values — prefer phrasing it without the literals so the grep is clean).
|
||||
|
||||
## Testing & Quality
|
||||
- Unit: the pin updates land in task 02 — this task's own check is the grep verification above + a visual sanity pass (`uv run uvicorn app.main:app --reload`, compare the chat/tuning/theme pages to the RAG page at 1280px and 1920px — the content columns match; the doc modal looks unchanged).
|
||||
- Coverage: **>90%** on `app/` (untouched — the full-suite gate runs at the phase's end).
|
||||
|
||||
## Completion Criteria
|
||||
- [ ] `styles.css`: `--chat-column: 72rem` in `:root`; no `@media (min-width: 1500px)` block; zero literal `max-width: 46rem` rules; the three shells (tuning/theme/doc-edit) structurally match `.sources-shell` (flex column, no cap)
|
||||
- [ ] the width grep is clean (task 02's pins will enforce the comment states — do not leave a stale "46rem base / 92rem" claim anywhere)
|
||||
- [ ] no HTML/JS/app change (D3)
|
||||
- [ ] the full unit suite is green EXCEPT the width pins task 02 rewrites (run it to enumerate the breakage — the list must be exactly the files named in task 02)
|
||||
@@ -0,0 +1,27 @@
|
||||
# Task 02 — The unit pins: `test_wide_column_css.py` rewritten + the grep-sweep of the other width pins
|
||||
|
||||
**Phase:** `100_page_width_consistency` · **Story:** n/a (owner request)
|
||||
|
||||
## Objective
|
||||
The unit layer pins the NEW width contract — `test_wide_column_css.py` becomes the phase-100 contract file, and every other unit pin that asserted the old widths is updated (fixture text that merely mentions a width is left alone).
|
||||
|
||||
## Work
|
||||
1. `tests/unit/test_wide_column_css.py` — REWRITE (keep the file's balanced-brace `_rule_block` helpers + its module purpose — "pin the styles.css markers the width contract depends on"):
|
||||
- **The token:** `:root` declares `--chat-column: 72rem` with the provenance comment citing the owner instruction 2026-09-12 (replaces the 2026-08-31 pin).
|
||||
- **The retired doubling:** NO `@media (min-width: 1500px)` block anywhere in the file (`css.count("@media (min-width: 1500px)") == 0`), and no `--chat-column: 92rem` assignment (`css.count("--chat-column: 92rem") == 0`).
|
||||
- **The four reading columns:** `.chat-shell`, `.shared-shell`, `.doc-md`, `.doc-summary:has(+ .doc-md)` each cap with `max-width: var(--chat-column)` — and exactly those four rules use the token (`css.count("max-width: var(--chat-column)") == 4` — the token count pin survives, the VALUE changes).
|
||||
- **The flipped negative pin:** ZERO literal `max-width: 46rem` rules remain (`css.count("max-width: 46rem") == 0`) — the three former form columns (`.tuning-shell`, `.theme-shell`, `.doc-edit-shell`) must NOT carry a max-width at all (each `_rule_block` free of `max-width`), and they carry no `margin-inline: auto` (the `.container` centers).
|
||||
- **The comment pins:** the stale claims are gone (`"≤46rem" not in css`, `"92rem at >=1500px" not in css`, `"46rem base" not in css`); the provenance appears at the token (owner instruction 2026-09-12); the chat-shell / shared-shell comments name the 72rem-everywhere contract (pin the key phrases, not whole paragraphs — the house "pin the contract words" style).
|
||||
- Update the module docstring to describe the new pins.
|
||||
2. The grep sweep — `grep -rln "46rem\|92rem\|chat-column" tests/unit/` (the authoritative list; `test_wide_column_css.py` is done above). For each hit, judge per pin: a pin that ASSERTS the old width (a literal in an `assert`, a `_rule_block` expectation, a counted occurrence) is updated to the new contract (72rem / no cap / the new comment wording); a hit that is fixture text, a docstring example, or an unrelated measurement (e.g. a fixture document mentioning "46rem", or a pin about an UNRELATED rule's max-width) is left untouched. Expected candidates (from the phase-100 `00_phase.md` context): `test_markdown_tables.py`, `test_shared_page.py`, `test_pinned_composer.py`, `test_doc_edit_screen.py`, `test_remove_confirm_modal.py`, `test_source_ignore_paths.py` — the actual set is whatever the grep + the run reveal.
|
||||
3. Run `uv run pytest tests/unit/ -q` — green.
|
||||
|
||||
## Testing & Quality
|
||||
- Unit: the rewritten `test_wide_column_css.py` + the swept pins ARE this task's test layer (they guard the CSS bytes — the house source-pin pattern).
|
||||
- Coverage: **>90%** on `app/` (untouched — the full-suite gate runs at the phase's end).
|
||||
|
||||
## Completion Criteria
|
||||
- [ ] `test_wide_column_css.py` pins the phase-100 contract (72rem token + 2026-09-12 provenance, no 1500px block, no 92rem, the four token selectors, ZERO literal 46rem rules, the three shells cap-free, the comment states)
|
||||
- [ ] the grep sweep is complete — every remaining `46rem|92rem|chat-column` reference under `tests/unit/` is either the new contract's pin or non-width fixture text (list the judgment in the commit's test-run notes / the final summary)
|
||||
- [ ] `uv run pytest tests/unit/ -q` green
|
||||
- [ ] no behavior change in completed work (the non-width pins in the swept files still pass unchanged)
|
||||
@@ -0,0 +1,31 @@
|
||||
# Task 03 — The E2E width sweep: measured 72rem everywhere + updated suites + regressions + commit
|
||||
|
||||
**Phase:** `100_page_width_consistency` · **Story:** n/a (owner request)
|
||||
|
||||
## Objective
|
||||
The browser proves the owner's ask: at every desktop viewport the chat, shared, document-viewer, Tuning, Theme, and doc-edit content columns measure the SAME width as the RAG page (the 72rem container) — the phase's dedicated width suite is rewritten to that contract and the other measured-width suites are updated in place.
|
||||
|
||||
## Work
|
||||
1. `tests/e2e/test_wide_desktop_column.py` — REWRITE (the phase-58 measured-width suite becomes the phase-100 one — the phase-97 task-07/08 precedent: a completed phase's suite updates in place when its contract changes; keep its seeding helpers — the fixture markdown doc for the viewer, the shared-chat helper):
|
||||
- **`test_all_columns_match_the_rag_page`** (the core pin): viewport 1280×800 — measure the `.chat-shell` bounding-box width on `/`, the `.tuning-shell` width on `/tuning.html` (admin), the `.theme-shell` width on `/theme.html` (admin), and the RAG page's `.sources-shell` width on `/sources.html` (admin): all four EQUAL (± 4 px) — the owner's "match the width of the RAG page" as one assertion. Then viewport 1920×1080: the same four still EQUAL each other (± 4 px) and each ≈ 1152 px (72rem at the 16px root, ± 4 px — the box includes the container's 2×1.25rem padding, border-box).
|
||||
- **`test_reader_columns_wide`** (the token's consumers): viewport 1920×1080 — `.doc-md` in the opened fixture doc ≈ 1058 px (the 1100px border-box modal panel minus its 1px borders and `.doc-modal-content`'s 2×1.25rem padding is the binding ceiling there — 1100 − 2 − 40; D2: pin that the modal is UNCHANGED by asserting the modal panel's own width is still ~1100 px, not that `.doc-md` is 1152); `/shared/<token>`'s `.shared-shell` ≈ 1152 px; the standalone `document.html?source=…&path=…` page's `.doc-md` ≈ 1112 px (its `.container.doc-shell` is the 72rem container → inner content = 1152 − 40 px padding).
|
||||
- **`test_narrow_unchanged`** (the no-regression leg): viewports 360×800 and 900×600 — `.chat-shell` width == viewport width (100%, no overflow: `document.documentElement.scrollWidth <= viewport width` — the `test_responsive_polish.py` assertion style), and at 900px the chat/tuning/theme shells are all 900px wide (equal to each other — the cap never bound below 72rem anyway).
|
||||
2. The other measured-width E2E suites — update in place (each in isolation, `--no-cov`, DB up): `test_document_viewer.py` (its `≤ 736 px` standalone `.doc-md` pin → the ≈ 1112 px contract; keep its non-width assertions byte-identical), `test_save_share_ux.py` (any 92rem/46rem measured or source-pinned width references), `test_sticky_navbar.py` (its width-related pins only), `test_markdown_tables.py` (the e2e table-width pins against the reading column), `test_responsive_polish.py` (the mobile pins — expected UNCHANGED; run to confirm). Judge per assertion: a width measurement updates, a layout/behavior assertion never does.
|
||||
3. The theme no-op contract (D3): run `test_ui_customization.py` + `test_admin_theme_tab.py` in isolation — no HTML changed, so the B4 byte-identical pins must pass UNCHANGED (if one fails, the CSS change touched something it should not — fix the cause, not the pin).
|
||||
4. Regression sweep (each in isolation, `--no-cov`): the five suites from step 2 (updated) + `test_chat_rag.py` (the chat layout smoke) + `test_theme_semantic_completion.py` (the phase-93 page-head panels at the new width).
|
||||
5. Full gates + commit: `uv run pytest` green; `uv run pytest --cov=app --cov-report=term-missing` >90%; `uv run ruff check . && uv run pyright` clean; move `100_page_width_consistency` → `.agents/phases/complete/`; one atomic commit:
|
||||
```bash
|
||||
git add -A .agents/ frontend/ tests/ && git commit --no-gpg-sign -m "feat(ui): match every page to the RAG page's 72rem width"
|
||||
```
|
||||
|
||||
## Testing & Quality
|
||||
- E2E (mandatory, A16): `uv run pytest tests/e2e/test_wide_desktop_column.py -v --no-cov` green in isolation — the phase's dedicated suite.
|
||||
- Coverage: **>90%** on `app/` (the full-suite gate).
|
||||
- Lint/types: `uv run ruff check . && uv run pyright`.
|
||||
|
||||
## Completion Criteria
|
||||
- [ ] the rewritten width suite passes in isolation: chat == tuning == theme == RAG at 1280 AND 1920 (± 4 px, ≈ 1152 px at 1920); the doc modal unchanged (~1100 px); shared + standalone document pages ≈ 1152/1112 px; 360px overflow-free
|
||||
- [ ] the other measured-width suites pass in isolation (updated in place); the theme no-op suites pass UNCHANGED
|
||||
- [ ] the regression sweep passes in isolation
|
||||
- [ ] full suite green, coverage >90%, ruff + pyright clean
|
||||
- [ ] phase dir moved to `complete/`, one atomic `--no-gpg-sign` Conventional Commit
|
||||
Reference in New Issue
Block a user