chore(agent): track .agent/ planning tree in git
Remove the blanket .agent/ gitignore so the phase roadmap, user stories, reports, and PLAN.md are versioned with the code. Only runtime artifacts (.agent/phase-sessions/, .agent/pipeline.log) remain ignored. Update AGENTS.md git protocol rule to match.
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
# Phase 60 — Sticky Navbar (stays stuck to the top while scrolling)
|
||||
|
||||
**Source:** `TODO.md` L3 — "The navbar disappears when you scroll down, should stay stuck to the top of the screen"
|
||||
**Story:** n/a (TODO-derived — owner roadmap confirmation 2026-08-31, A1–A3)
|
||||
**Context:** The navbar already HAS the sticky CSS: `.app-header` (`frontend/assets/styles.css` ~L194 — `position: sticky; top: 0; z-index: 20`) and the document viewer's two-row `.doc-header` (~L2201 — also `position: sticky; top: 0`), both direct children of `<body>` on every page. The bug: `html, body { height: 100% }` (~L40) pins the body box to exactly one viewport, and a sticky element's travel range is constrained to its containing block — so after ~1 viewport of scrolling the header un-pins and scrolls away with the body. Verified with headless Chromium during the TODO audit (2026-08-31): with the current rule the header's rect top is −1264px after a 2000px scroll; with `height` dropped from `body` (keeping `min-height: 100dvh`) it is top = 0, and on a short page the footer still lands at the viewport bottom (800/800 at 1280×800). The document must stay the scroll container (phase 52 no-inner-scroller contract, ~L387) — this fix adds no scroller, it only lets the body grow to its content. `.agent/` is untracked (owner commit 281f355) — phase commits stage `frontend/ tests/` only.
|
||||
|
||||
## Objective
|
||||
The navbar — and the document-viewer header — stays stuck to the top of the screen on every page at every scroll position, while the short-page layout (flex stretch, pinned composer, footer) stays exactly as it is today.
|
||||
|
||||
## Dependencies
|
||||
- `59_response_to_docs_push` (todo, preceding — no functional dependency; ordering by number)
|
||||
|
||||
## Tasks
|
||||
1. `01_sticky_header_css.md` — drop `height: 100%` from `body` (keep it on `html`), refresh the stale phase-12 comment, unit CSS pins in `tests/unit/test_sticky_header.py`.
|
||||
2. `02_e2e_sticky_navbar.md` — `tests/e2e/test_sticky_navbar.py` (rect-top == 0 proofs on Sources + viewer at the bottom of long pages; short-page stretch/composer regressions), regression suites, atomic commit.
|
||||
|
||||
## Testing & Quality
|
||||
- Unit CSS pins (house style, `tests/unit/test_save_chat_ui.py` pattern): `html` keeps `height: 100%`; the `body` rule carries NO `height:` (negative pin) and keeps `min-height: 100dvh`; `.app-header` and `.doc-header` keep `position: sticky; top: 0`.
|
||||
- E2E (mandatory, A3): `tests/e2e/test_sticky_navbar.py`, run in isolation.
|
||||
- Coverage: **>90%** on `app/` (validate.sh gate — pure-CSS phase, gate still runs).
|
||||
|
||||
## Completion Criteria
|
||||
- [ ] Sources page (KB long enough to scroll), 1280×800: after `window.scrollTo(0, 999999)` the `.app-header` rect top == 0 (±1px) and the header is visible.
|
||||
- [ ] Document viewer on a long doc: after full scroll, `.doc-header` rect top == 0 (±1px).
|
||||
- [ ] Short page (empty chat, 1280×800): body height == 800, `.app-footer` bottom == 800, `.composer` bottom ≈ 800 (±2px) — the phase-52 pin holds.
|
||||
- [ ] `uv run pytest` green; coverage TOTAL >90%.
|
||||
- [ ] `uv run pytest tests/e2e/test_sticky_navbar.py -v --no-cov` green in isolation (DB up).
|
||||
- [ ] Regression E2E suites green in isolation: `test_pinned_composer.py`, `test_document_viewer.py`, `test_responsive_polish.py`, `test_smoke.py`, `test_no_reply_autoscroll.py`, `test_mobile_hamburger_nav.py`.
|
||||
- [ ] `uv run ruff check . && uv run pyright` clean.
|
||||
- [ ] One `--no-gpg-sign` commit; phase dir moved to `.agent/phases/complete/` (`.agent/` stays untracked — owner instruction, commit 281f355).
|
||||
|
||||
## Locked decisions
|
||||
- **Owner-locked (2026-08-31, roadmap confirmation, A1–A3):**
|
||||
1. **A1 (root cause):** the sticky travel range is capped by `body { height: 100% }` (`styles.css` L40) — the body box is pinned to one viewport and sticky elements (`.app-header`, `.doc-header`) can only travel within it.
|
||||
2. **A2 (fix):** CSS-only — drop `height: 100%` from `body` (keep it on `html`); the body's existing `min-height: 100dvh` keeps driving the short-page stretch. No JS changes, no new scroller (the document stays the scroll container).
|
||||
3. **A3 (E2E):** long-scroll surfaces = the seeded Sources table + the document viewer, with KB seeding via `import_sources` (the `test_document_viewer.py` pattern — fixtures + generated docs). No real LLM involved.
|
||||
|
||||
## Commit
|
||||
```bash
|
||||
git add frontend/ tests/ && git commit --no-gpg-sign -m "fix(web): keep the navbar stuck to the top — drop the body height cap on the sticky range"
|
||||
```
|
||||
@@ -0,0 +1,38 @@
|
||||
# Task 01 — Drop the body height cap on the sticky range
|
||||
|
||||
**Phase:** `60_sticky_navbar` · **Source:** `TODO.md:3` — "The navbar disappears when you scroll down, should stay stuck to the top of the screen"
|
||||
**Story:** n/a (TODO-derived)
|
||||
|
||||
## Objective
|
||||
The navbar's sticky rule already exists but is capped: `body`'s fixed `height: 100%` pins its box to one viewport and sticky elements may only travel within it. Drop that cap so the body grows to its content and `.app-header` (every page) / `.doc-header` (document viewer) stay stuck to the top across the whole document scroll.
|
||||
|
||||
## Work
|
||||
1. `frontend/assets/styles.css`:
|
||||
- L40: `html, body { height: 100%; }` → `html { height: 100%; }`, with a replacement comment:
|
||||
```css
|
||||
/* Phase 60 (owner confirmation 2026-08-31, TODO L3): body must NOT carry a
|
||||
fixed height — a sticky element's travel range is capped by its
|
||||
containing block, and `body { height: 100% }` pinned the box to one
|
||||
viewport, so the navbar un-pinned after ~1 viewport of scroll.
|
||||
`min-height: 100dvh` on body (below) is what stretches short pages. */
|
||||
html { height: 100%; }
|
||||
```
|
||||
- The `body` rule (~L47) is UNCHANGED: `min-height: 100dvh` is the stretch driver; the flex column stays as-is.
|
||||
- The `.app-header` phase-12 comment (~L199–202, "body is a definite-height flex column; without this the header shrinks (flex-shrink:1)…") — reword to match reality (body stretches via `min-height: 100dvh`; the `flex-shrink: 0` guard stays for content-overflow pages, e.g. Sources at ≤640px). CSS properties byte-identical.
|
||||
- No other CSS, JS, or HTML changes in this task.
|
||||
2. `tests/unit/test_sticky_header.py` (new — house pattern `tests/unit/test_save_chat_ui.py`: read `frontend/assets/styles.css` as text, assert on it):
|
||||
- Negative pin: the substring `html, body { height: 100%; }` is GONE.
|
||||
- Positive pin: `html { height: 100%; }` present.
|
||||
- The `body { … }` block (extract via regex over the CSS text) contains NO `height:` declaration and DOES contain `min-height: 100dvh`.
|
||||
- The `.app-header` block contains `position: sticky` and `top: 0`; the `.doc-header` block contains `position: sticky` and `top: 0` (they already do — these pins guard against future "simplification").
|
||||
- ASSUMPTION: A1 — the root cause is the `body { height: 100% }` cap (verified with a headless Chromium repro during the TODO audit: header rect top −1264px after a 2000px scroll with the current rule, 0px with the fix; short-page footer still at the viewport bottom).
|
||||
- ASSUMPTION: A2 — the fix is CSS-only; `html` keeps `height: 100%` (harmless viewport baseline); no JS or scroller changes are needed.
|
||||
|
||||
## Testing & Quality
|
||||
- Unit: the pins above; `uv run pytest` green; `uv run ruff check . && uv run pyright` clean.
|
||||
- Coverage: **>90%** on `app/` (validate.sh gate — pure-CSS task, gate still runs).
|
||||
|
||||
## Completion Criteria
|
||||
- [ ] `styles.css` L40 region: only `html` carries `height: 100%`; the comment cites phase 60 provenance.
|
||||
- [ ] All CSS properties byte-identical except the one selector change (no incidental reformatting).
|
||||
- [ ] Unit pins green; full suite green; lint + types clean.
|
||||
@@ -0,0 +1,34 @@
|
||||
# Task 02 — E2E: navbar stuck at the top + short-page regressions + commit
|
||||
|
||||
**Phase:** `60_sticky_navbar` · **Source:** `TODO.md:3` — "The navbar disappears when you scroll down, should stay stuck to the top of the screen"
|
||||
**Story:** n/a (TODO-derived)
|
||||
|
||||
## Objective
|
||||
An isolated Playwright proof that the navbar (and the viewer header) is stuck at the top at the bottom of long pages, plus short-page regressions proving the flex stretch and the pinned composer are intact — and a green pass over the scroll/layout regression suites.
|
||||
|
||||
## Work
|
||||
1. `tests/e2e/test_sticky_navbar.py` (new — house E2E pattern: `app_server`/`browser`/`page` fixtures from `tests/e2e/conftest.py`; admin `login` from `tests/e2e/auth_helpers.py`; KB truncate + `import_sources` in a worker thread exactly like `tests/e2e/test_document_viewer.py`):
|
||||
- Module-scoped setup: truncate the KB, then import `tests/fixtures/docs/` **plus** a generated scratch dir the test writes (e.g. under `tmp_path`) — ~40 short unique docs `gen/doc-001.md` … `gen/doc-040.md` (one-line bodies, unique titles) and one `gen/doc-long.md` with a ~40,000-char body. Deterministic — the mock LLM embeddings server handles imports, no real LLM.
|
||||
- Test 1 — app navbar (Sources page): viewport 1280×800, admin login, open `/sources.html`; guard `document.documentElement.scrollHeight > 1.5 * 800` (fail loudly with a descriptive message if the table is not long enough — it should not be); `window.scrollTo(0, 800)` → assert `.app-header` bounding-box `top` == 0 (±1px); `window.scrollTo(0, 999999)` → assert `.app-header` top == 0 (±1px) again and the header is visible.
|
||||
- Test 2 — viewer header: open `gen/doc-long.md` in the document viewer (same navigation as `test_document_viewer.py`); guard that the page scrolls (scrollHeight > 800); scroll to the very bottom; assert `.doc-header` bounding-box `top` == 0 (±1px).
|
||||
- Test 3 — short-page regression (empty chat, 1280×800): `body` rect height == 800 (the `min-height: 100dvh` stretch is intact), `.app-footer` rect bottom == 800, `.composer` rect bottom ≈ 800 (±2px — the phase-52 pinned composer holds).
|
||||
- NO `frontend/` JS/HTML or `app/` changes in this task — task 01 already made the CSS change.
|
||||
- ASSUMPTION: A3 — the long-scroll surfaces are the seeded Sources table + the document viewer (fixture + generated-doc seeding, `test_document_viewer.py` pattern); the real LLM is not involved.
|
||||
2. Regression pass (DB up — `podman compose up -d db`; each suite run in isolation, house gate):
|
||||
- `uv run pytest tests/e2e/test_sticky_navbar.py -v --no-cov`
|
||||
- then, in isolation: `tests/e2e/test_pinned_composer.py`, `tests/e2e/test_document_viewer.py`, `tests/e2e/test_responsive_polish.py`, `tests/e2e/test_smoke.py`, `tests/e2e/test_no_reply_autoscroll.py`, `tests/e2e/test_mobile_hamburger_nav.py` (same flags).
|
||||
- Full gate: `uv run pytest --cov=app --cov-report=term-missing` (TOTAL >90%) and `uv run ruff check . && uv run pyright`.
|
||||
3. Atomic commit — task 01's CSS + this task's tests are the only non-`.agent/` changes:
|
||||
```bash
|
||||
git add frontend/ tests/ && git commit --no-gpg-sign -m "fix(web): keep the navbar stuck to the top — drop the body height cap on the sticky range"
|
||||
```
|
||||
|
||||
## Testing & Quality
|
||||
- E2E above (bounding-box top assertions, run in isolation per AGENTS.md rule 4).
|
||||
- Coverage: **>90%** on `app/` (validate.sh gate — no `app/` changes; the gate still runs).
|
||||
|
||||
## Completion Criteria
|
||||
- [ ] `tests/e2e/test_sticky_navbar.py` — all three tests green in isolation.
|
||||
- [ ] The six regression E2E suites green in isolation.
|
||||
- [ ] `uv run pytest` green; coverage TOTAL >90%; `uv run ruff check . && uv run pyright` clean.
|
||||
- [ ] One `--no-gpg-sign` commit containing only `frontend/` + `tests/` changes; phase dir moved to `.agent/phases/complete/`.
|
||||
Reference in New Issue
Block a user