diff --git a/.agents/phases/complete/174_git_sources_card_rhythm/00_phase.md b/.agents/phases/complete/174_git_sources_card_rhythm/00_phase.md new file mode 100644 index 0000000..df59c6e --- /dev/null +++ b/.agents/phases/complete/174_git_sources_card_rhythm/00_phase.md @@ -0,0 +1,46 @@ +# Phase 174 — Sources (git sources) page: the card stack regains its 1.25rem rhythm + +**Source:** Owner bug report 2026-10-21 (screenshot of `/git-sources.html` — "the bubbles on the sources page are really smooshed together": the "Git sources" header card, the "Add a git source" card, the "Upload a source archive" card, and the sources table card stack flush — only their 1px borders separate them). +**Story:** n/a (owner bug report, this chat 2026-10-21) +**Context:** `frontend/index.html` — `#view-git-sources` (L646): `.container.git-sources-shell` holds TWO children — the anonymous `.sources-gate` (`#git-sources-gate`, L651) and the single `#git-sources-content` wrapper (L672) that wraps **every** visible card: `.page-head`, the `#git-sources-load-error` alert (hidden), the `#git-source-env-note` (hidden), `#git-source-form`, `#archive-upload-form`, and the `.table-wrap#git-sources-table-wrap`; `frontend/assets/styles.css` — `.git-sources-shell` (L3193–3200: `display:flex; flex-direction:column; gap: 1.25rem; flex:1` — the gap only separates the gate from the wrapper), `#git-sources-content` (**no rule at all** — a plain div, so its children stack with zero vertical rhythm — the root of the "smooshed" look), the phase-93 `.page-head` card (L2423–2428), the phase-27/49 form cards `#git-source-form, #archive-upload-form` (L3207+), the ≤640px block (L6400–6403: the forms go column — they compose with the fix unchanged), `frontend/assets/git-sources.js` (L294, L1515–1519: the gate↔content reveal — `contentEl.hidden` flips only); `tests/e2e/auth_helpers.py::login` (the admin E2E sign-in), `tests/e2e/test_admin_settings_layout.py` (the phase-171 bounding-box rhythm E2E — the house pattern this suite copies), `tests/unit/test_git_source_branch_ui.py` (the static CSS/HTML/JS pin style this phase extends). + +## Objective +The five stacked cards on the Sources page (the navbar "Sources" link → `/git-sources.html`) get one uniform 1.25rem gap between them — CSS only, reusing the shell's **existing** `gap: 1.25rem` token (the phase-171 `#settings-content` idiom applied to the one remaining admin view whose cards live inside a wrapper div). Nothing else changes: no JS, no `app/` code, no new palette/spacing value, the gate + load-error + env-note reveal lifecycles byte-identical. + +## Design (shared by all tasks — the executor reads this, not the chat) +- **D1 — the rhythm (task 01).** `#git-sources-content` becomes `display: flex; flex-direction: column; gap: 1.25rem;` — the **same** token `.git-sources-shell` already uses (L3196) and the value phase 171 gave `#settings-content`. One rule, placed directly after the `.git-sources-shell` rule with a phase-174 comment explaining the wrapper-swallowed-the-shell-gap root cause. The children (page-head card, load-error alert, env note, the two form cards, the table card) then stack with one uniform gap at every viewport. +- **D2 — the hidden children (task 01, pinned in 02).** The two `hidden` children (`#git-sources-load-error`, `#git-source-env-note`) keep the UA `[hidden] → display:none` even inside a flex parent (the parent's `display` never overrides a child's own `display`), so `git-sources.js`'s reveal lifecycle (L1515–1519) is untouched; a revealed error/note simply joins the rhythm. The anonymous gate↔content flip (L1515–1519: `contentEl.hidden = false`) works on a flex element exactly as on a block. +- **D3 — no other changes.** `.git-sources-shell` (its `flex: 1` + gap stay — the gate↔content separation at anonymous), `git-sources.js`, `app/` server code, the form/table card rules, and the ≤640px media queries (L6400–6403) are all untouched; the fix is a single wrapper rule. + +## Dependencies +- `171_admin_settings_layout` (complete) — the idiom this phase reuses (wrapper div → flex column + the existing 1.25rem token). +- `93_theme_semantic_completion` (complete) — the `.page-head` card language the rhythm spaces out (context only). + +## Tasks +1. `01_content_card_rhythm.md` — `#git-sources-content` becomes the flex-column rhythm wrapper (the one CSS rule); the static pins. +2. `02_e2e_sources_card_rhythm.md` — `tests/e2e/test_git_sources_card_rhythm.py` (the phase's dedicated Playwright suite, AGENTS rule 4), the regression set, the full gates, the atomic commit. + +## Testing & Quality +- **Unit:** `tests/unit/test_git_sources_card_rhythm.py` (new module, the `test_git_source_branch_ui.py` pin style): the `#git-sources-content` rule in `styles.css` with `display: flex` / `flex-direction: column` / `gap: 1.25rem` (the exact token — a regression to a different value fails), the `.git-sources-shell` gap staying `1.25rem` (unchanged), the `#git-sources-content` div present in `index.html` inside `.git-sources-shell` with the gate as a sibling (the structural pin — if the cards ever move back to the shell's direct children the rule becomes dead weight and the pin must say so). +- **E2E (AGENTS rule 4, task 02):** `tests/e2e/test_git_sources_card_rhythm.py`, run **in isolation** — the rhythm contract proven in a browser at 1440px and 375px, plus the hidden-children pin and the standard a11y/no-CDN light pass. +- **Coverage:** **>90%** on `app/` (this phase changes no `app/` code — the gate must stay green, not drop). +- **Lint/types:** `uv run ruff check . && uv run pyright`. +- The phase is "Complete" only when the **entire** project test suite passes 100% (every test in the project, not just this phase's) and the gates above are green. + +## Documentation & Consistency +- The final pass reviews `README.md` and `AGENTS.md` in full against the project as it stands after this phase — fixing every inconsistency found, **including pre-existing ones not caused by this phase**. +- Tasks update `README.md`/`AGENTS.md` immediately when their own changes are user-visible (this phase is a visual fix with no setup/command/config surface — expected: no doc edits). + +## Completion Criteria +- [ ] On `/git-sources.html` (admin), the five cards (page-head → add form → upload form → table, plus a revealed load-error when one is forced) stack with one uniform ≈20px gap — no two card edges touch (the E2E bounding-box pins, at 1440px and 375px). +- [ ] `#git-sources-content`'s hidden children (`#git-sources-load-error`, `#git-source-env-note`) stay hidden in the flex wrapper (E2E pin); the anonymous gate still shows and the content stays hidden for anonymous (regression). +- [ ] The ≤640px mobile layout is intact (the forms column-stack per L6400–6403; the table scrolls inside its card — the E2E mobile pin). +- [ ] `uv run pytest` green, `app/` coverage >90% (`uv run pytest --cov=app --cov-report=term-missing`), ruff + pyright clean. +- [ ] `uv run pytest tests/e2e/test_git_sources_card_rhythm.py -v --no-cov` green **in isolation**; `test_git_sources_admin.py` + `test_archive_upload_sources.py` + `test_git_source_dates.py` green in isolation (the regression set). +- [ ] README.md and AGENTS.md are accurate and consistent with the whole project (verified in the final pass). +- [ ] One atomic `--no-gpg-sign` Conventional Commit; the phase dir moves to `.agents/phases/complete/`. + +## Commit +```bash +git add frontend/ tests/ .agents/phases/ && git commit --no-gpg-sign -m "fix(sources): Sources (git sources) page — the card stack regains its 1.25rem rhythm" +``` diff --git a/.agents/phases/complete/174_git_sources_card_rhythm/01_content_card_rhythm.md b/.agents/phases/complete/174_git_sources_card_rhythm/01_content_card_rhythm.md new file mode 100644 index 0000000..8d07cbf --- /dev/null +++ b/.agents/phases/complete/174_git_sources_card_rhythm/01_content_card_rhythm.md @@ -0,0 +1,45 @@ +# Task 01 — `#git-sources-content` becomes the flex-column rhythm wrapper + +**Phase:** `174_git_sources_card_rhythm` · **Story:** n/a (owner bug report 2026-10-21) + +## Objective +The one CSS rule that restores the vertical rhythm on the Sources page: `#git-sources-content` becomes a flex column with the shell's existing `1.25rem` gap — so the five stacked cards (page-head, load-error, env-note, add form, upload form, table card) stop stacking flush. + +## Work +1. `frontend/assets/styles.css` — directly after the `.git-sources-shell` rule (L3193–3200), add the `#git-sources-content` rule with a phase-174 comment block (the house comment style: root cause + the reused token + the hidden-children behavior): + ```css + /* Phase 174 (owner bug report 2026-10-21): the shell's gap only + separates the gate from this wrapper — every visible card + (page-head, the load-error alert, the env note, the two form + cards, the table card) is a child of THIS div, which was a + plain block, so the cards stacked flush (1px borders touching). + The phase-171 #settings-content idiom: the wrapper itself is + the rhythm column, reusing the shell's own 1.25rem token — no + new spacing value. The hidden children (#git-sources-load-error, + #git-source-env-note) keep the UA [hidden] display:none inside + a flex parent, so git-sources.js's reveal lifecycle is + untouched; a revealed one joins the rhythm. */ + #git-sources-content { + display: flex; + flex-direction: column; + gap: 1.25rem; + } + ``` + No other CSS change. Do not touch `.git-sources-shell`, the form/table card rules, or the ≤640px block (L6400–6403 — the forms' column-stacks compose with the new wrapper unchanged). +2. `tests/unit/test_git_sources_card_rhythm.py` — new module (the `test_git_source_branch_ui.py` pin style: `FRONTEND`/`STYLES_CSS`/`SHELL_HTML` path constants, raw text assertions, module docstring citing the phase + the owner report): + - the `#git-sources-content` rule exists in `styles.css` and carries `display: flex`, `flex-direction: column`, and `gap: 1.25rem` **exactly** (extract the rule body with a small regex over the `#git-sources-content { … }` block — a regression to any other gap value or a missing flex line fails); + - the `.git-sources-shell` rule still carries `gap: 1.25rem` (the fix must not have "fixed" the shell — D3); + - the structural pin: in `index.html`, `#git-sources-content` sits inside `.container.git-sources-shell` and the `.sources-gate` (`#git-sources-gate`) is a sibling of it (so the rule is not dead weight — if a future refactor moves the cards to the shell's direct children, this pin names the dead rule); + - `#git-sources-load-error` and `#git-source-env-note` still carry `hidden` in `index.html` (the D2 premise the E2E pin proves in a browser). + +## Testing & Quality +- Unit: the pins above; `uv run pytest tests/unit/test_git_sources_card_rhythm.py -v` green. +- Coverage: **>90%** on `app/` (unchanged by this task — the gate must stay green, not drop). +- No `app/` code change; no JS change. + +## Completion Criteria +- [ ] `uv run pytest tests/unit/test_git_sources_card_rhythm.py -v` green. +- [ ] `uv run pytest` green (the full unit + integration suite — no pin broke). +- [ ] `uv run ruff check . && uv run pyright` clean. +- [ ] README.md/AGENTS.md need no change for this task (visual-only; the phase's final pass re-checks both project-wide). +- [ ] no behavior change in completed work (the gate reveal, the load-error retry, the env note, the form lifecycles are untouched — the rule only adds inter-card spacing). diff --git a/.agents/phases/complete/174_git_sources_card_rhythm/02_e2e_sources_card_rhythm.md b/.agents/phases/complete/174_git_sources_card_rhythm/02_e2e_sources_card_rhythm.md new file mode 100644 index 0000000..b9fd3de --- /dev/null +++ b/.agents/phases/complete/174_git_sources_card_rhythm/02_e2e_sources_card_rhythm.md @@ -0,0 +1,33 @@ +# Task 02 — the phase's dedicated Playwright E2E suite + gates + commit + +**Phase:** `174_git_sources_card_rhythm` · **Story:** n/a (owner bug report 2026-10-21) + +## Objective +Prove the rhythm in a browser — `tests/e2e/test_git_sources_card_rhythm.py` (the phase's dedicated Playwright suite, AGENTS rule 4): the cards on `/git-sources.html` stack with one uniform gap at 1440px and 375px, the hidden children stay hidden in the flex wrapper, and the anonymous gate is intact. Then the regression set, the full gates, and the atomic commit. + +## Work +1. `tests/e2e/test_git_sources_card_rhythm.py` — new module, run **in isolation** (`uv run pytest tests/e2e/test_git_sources_card_rhythm.py -v --no-cov`; DB must be up: `podman compose up -d db`). House shape: module docstring (the owner report + the run command + the test→contract map), the session `mock_llm`/`app_server`/`app_url`/`db_ready`/`page` fixtures from `tests/e2e/conftest.py`, `tests/e2e/auth_helpers.py::login` for the admin sign-in, a per-test `_truncate_kb`-style reset so each test's table state is its own (the `test_git_sources_admin.py` pattern — this suite is UI-only: it needs NO sync, no git, no network; the app can boot plain, the cards render regardless of row count). + Tests (four): + - `test_cards_stack_with_uniform_rhythm_desktop` — 1440×900, admin (`login(page, app_url, next="/git-sources.html")`): `#git-sources-content`'s computed `display` is `flex` and its `gap` resolves to 20px; the bounding boxes of the four visible cards — `.page-head` → `#git-source-form` → `#archive-upload-form` → `#git-sources-table-wrap` — are **pairwise disjoint** and each successive gap is ≈20px (`box_{n+1}.top − box_n.bottom == 20 ± 2`); no two card edges touch (the pre-fix regression: the gaps were ~0 — the 1px borders). + - `test_cards_stack_with_uniform_rhythm_mobile` — 375×720, admin: the same four cards are pairwise disjoint with ≈20px gaps; the two form cards column-stack per the ≤640px block (L6400–6403 — assert `#git-source-form` computed `flex-direction: column`) and the table scrolls **inside** its card (the `#git-sources-table-wrap` horizontal overflow is contained — the card's right edge is left of the viewport, no page-level horizontal scroll: `document.scrollingElement.scrollWidth <= 375`). + - `test_hidden_children_stay_hidden_in_flex_wrapper` — admin: `#git-sources-load-error` and `#git-source-env-note` are not visible (the `[hidden]` UA rule survives the flex parent — `offsetParent is None` / `is_visible() == False`); then force the load-error's `hidden` off via the page (attribute removal) and assert it **joins the rhythm** (visible, and its box sits ≥18px below the element above it) — the D2 both-sides pin; anonymous: `#git-sources-gate` visible, `#git-sources-content` hidden, no `/api/git-sources` call (the `test_git_sources_admin.py` anonymous contract, copied — the gate↔content flip is unchanged by the fix). + - `test_sources_page_a11y_and_no_cdn_basics` — the standard light pass (AGENTS rule 5/6, the `test_git_sources_admin.py` a11y test shape): landmarks on the view, the labeled form controls, the 3px `:focus-visible` outline on a table-row control (keyboard Tab), same-origin assets only (no external `