fix(build): Containerfile builds again — relative module imports, all four pages and shared assets in the image
This commit is contained in:
@@ -1,114 +0,0 @@
|
||||
# Phase 22 — Animated Background: Make It Actually Animate
|
||||
|
||||
**Source:** `TODO.md` L5 — *"Fix background animation not working, just
|
||||
blinking"*
|
||||
**Story:** `.agent/user_stories/background-animation.md` (created by task 02)
|
||||
**Context:** `frontend/assets/styles.css` — the phase-08 animated
|
||||
background block (~lines 60–100): `body::before` (44px drifting grid,
|
||||
1px lines at ~35% `--line` alpha, radial mask
|
||||
`radial-gradient(120% 90% at 50% 0%, black 25%, transparent 78%)`,
|
||||
`animation: bg-grid-drift 60s linear infinite`) and `body::after` (two
|
||||
soft radial glows, `animation: bg-glow-breathe 14s ease-in-out infinite
|
||||
alternate`, opacity 0.65↔1 + scale 1↔1.05). Both layers are
|
||||
`position: fixed; inset: 0; z-index: -1; pointer-events: none`. `html`
|
||||
owns the `var(--bg)` canvas and `body` is `background: transparent`
|
||||
(~lines 42–54) — if any later rule occludes that, the layers vanish.
|
||||
The **phase-08 design comments are the spec** for what "working" means.
|
||||
|
||||
## Objective
|
||||
Owner report 2026-08-24: the background "just blinks" — i.e. the motion
|
||||
the phase-08 design promised (a slow, seamless grid drift + a gentle
|
||||
glow breathe) is not perceived; at most a flicker/blink is visible.
|
||||
Diagnose which layer(s) actually fail in a real Chromium viewport, fix
|
||||
the CSS, and leave a background that visibly and smoothly animates as
|
||||
designed — no blink, no static frame, no jank.
|
||||
|
||||
## Owner-confirmed (2026-08-24, roadmap A3)
|
||||
1. **Intended effect = the phase-08 design comments:** seamless 60s grid
|
||||
drift (one cell per loop) + 14s ease glow breathing. The fix serves
|
||||
that design, not a redesign.
|
||||
2. **Pure CSS, zero JS** (phase-08 anchor) — no animation JS, no new
|
||||
assets, no `filter: blur` (perf note in the block).
|
||||
|
||||
## Design / diagnostic plan
|
||||
The fix is found, not guessed — work through this checklist in a real
|
||||
Chromium window (dev server, full page, ~15s of observation):
|
||||
1. **Per-layer visibility:** toggle each pseudo-element (DevTools
|
||||
generated-content / a temp outline) and screenshot — is the grid
|
||||
visible at all? Is only the glow (the "blink" the user perceives)
|
||||
alive?
|
||||
2. **Grid layer:** sample `background-position` on `body::before` at two
|
||||
timestamps — is it actually moving? Is the radial mask fading the
|
||||
visible region so small that the 44px/60s drift is imperceptible?
|
||||
(If the drift is real but too faint: raise the grid line alpha and/or
|
||||
the mask's visible radius — smallest change that reads as "smooth
|
||||
drift".)
|
||||
3. **Glow layer:** is the 14s breathe reading as a *blink*? (If the
|
||||
opacity swing 0.65↔1 is perceived as pulsing: lengthen the period
|
||||
and/or narrow the opacity delta so it reads as breathing.)
|
||||
4. **Occlusion check:** confirm nothing later in `styles.css` (or in
|
||||
`html`/`body` rules) paints an opaque background over the
|
||||
`z-index: -1` layers — the phase-08 comment at ~line 42 is the
|
||||
contract.
|
||||
5. **Apply the fix in `styles.css`** — document the found root cause in
|
||||
the phase report (screenshot before/after in
|
||||
`.agent/screenshots/22_background_animation/`).
|
||||
|
||||
## Dependencies
|
||||
- `08_story_dark_tech_theme` (complete) — owns the layers, the palette,
|
||||
and the "pure CSS, zero JS" anchor this phase must respect.
|
||||
- `07_story_responsive_polish` (complete) — no new overflow at 360px
|
||||
(both layers are `fixed; inset: 0` — keep it that way).
|
||||
|
||||
## Tasks
|
||||
1. `01_fix_background_animation.md` — diagnosis + the CSS fix +
|
||||
source-level unit pins.
|
||||
2. `02_e2e_story_suite_commit.md` — `tests/e2e/test_background_animation.py`
|
||||
(the story gate, isolated), regression suites, story file, final
|
||||
validation, the single atomic commit, phase move to `complete/`.
|
||||
|
||||
## Locked decisions
|
||||
- **Phase-08 anchor honored** — pure CSS, zero JS, no `filter: blur`,
|
||||
WCAG AA palette untouched (background layers carry no text).
|
||||
**A11 untouched** — no CDN, no new assets. **A16 honored** — one new
|
||||
story E2E suite + adapted regressions. No anchor changed.
|
||||
|
||||
## Testing & Quality
|
||||
- **Unit (source-level, new `tests/unit/test_background_animation.py`,
|
||||
repo source-pin pattern):** `styles.css` still defines
|
||||
`@keyframes bg-grid-drift` and `@keyframes bg-glow-breathe`;
|
||||
`body::before` animates `bg-grid-drift` with `linear infinite`;
|
||||
`body::after` animates `bg-glow-breathe`; both layers remain
|
||||
`position: fixed; z-index: -1; pointer-events: none`; `html` keeps
|
||||
`background: var(--bg)` and `body` keeps `background: transparent`
|
||||
(the no-occlusion contract). Pin the **final** values the fix lands
|
||||
on (durations/opacities may move per the design plan).
|
||||
- **Integration:** none (no `app/` changes).
|
||||
- **Coverage:** frontend-only; the >90% `app/` gate is unaffected.
|
||||
- **E2E:** `tests/e2e/test_background_animation.py` (task 02), green
|
||||
**in isolation** (prereq `podman compose up -d db`).
|
||||
- **Lint/types:** `uv run ruff check . && uv run pyright` clean.
|
||||
|
||||
## Completion Criteria
|
||||
- [ ] In a real Chromium viewport, the background visibly and smoothly
|
||||
animates (grid drift + glow breathe) — screenshot before/after in
|
||||
the phase report; owner's "just blinking" perception gone.
|
||||
- [ ] Root cause documented in `.agent/reports/22_background_animation/`.
|
||||
- [ ] `uv run pytest` green; `uv run pytest --cov=app
|
||||
--cov-report=term-missing` ≥ today's number.
|
||||
- [ ] `uv run pytest tests/e2e/test_background_animation.py -v --no-cov`
|
||||
green in isolation; regressions green in isolation:
|
||||
`test_dark_tech_theme.py`, `test_responsive_polish.py`.
|
||||
- [ ] `uv run ruff check . && uv run pyright` clean.
|
||||
- [ ] UI Structure Check (AGENTS.md rule 5): layers stay behind content
|
||||
(`z-index: -1`, `pointer-events: none`), no text/contrast impact,
|
||||
no 360px overflow.
|
||||
- [ ] `.agent/user_stories/background-animation.md` exists.
|
||||
- [ ] One `--no-gpg-sign` commit (below);
|
||||
`.agent/phases/todo/22_background_animation/` moved to
|
||||
`.agent/phases/complete/`.
|
||||
|
||||
## Commit
|
||||
```bash
|
||||
git add -A .agent/ frontend/ tests/ && git commit --no-gpg-sign -m "fix(ui): animated background actually animates — grid drift and glow breathe per the phase-08 design"
|
||||
```
|
||||
@@ -1,68 +0,0 @@
|
||||
# Task 02 — E2E story suite, story file, validation, commit
|
||||
|
||||
**Phase:** `22_background_animation` · **Source:** `TODO.md` L5
|
||||
|
||||
## Objective
|
||||
The story gate: `tests/e2e/test_background_animation.py` proves both
|
||||
background layers are actually running animations (not just declared),
|
||||
plus regressions, story file, final validation, and the single atomic
|
||||
commit.
|
||||
|
||||
## Work
|
||||
1. `tests/e2e/test_background_animation.py` (new). The layers are CSS
|
||||
pseudo-elements, so assert via computed style + the Web Animations
|
||||
API (Chromium reports pseudo-element CSS animations through
|
||||
`element.getAnimations()`):
|
||||
1. `test_grid_layer_animation_running` —
|
||||
`getComputedStyle(document.body, "::before").animationName` is the
|
||||
grid-drift keyframe (final name from task 01), timing function
|
||||
`linear`, iteration count `infinite`; and a matching entry in
|
||||
`document.body.getAnimations()` with `playState === "running"`.
|
||||
2. `test_glow_layer_animation_running` — same for `"::after"` with
|
||||
the glow-breathe keyframe; `playState === "running"`.
|
||||
3. `test_animations_advance` — sample `animation.currentTime` (or
|
||||
the `getAnimations()` entry's `currentTime`) for both layers,
|
||||
wait ~500ms (`page.wait_for_timeout`), assert both advanced —
|
||||
the animations are truly running, not paused.
|
||||
4. `test_background_layers_contracts` — both pseudo-elements:
|
||||
`position: fixed`, `z-index: -1`, `pointer-events: none`;
|
||||
`document.documentElement` computed `background-color` is the
|
||||
palette bg (the canvas stays on `html`); `document.body` computed
|
||||
`background-color` is `rgba(0, 0, 0, 0)` (no occlusion).
|
||||
5. `test_no_horizontal_overflow_with_layers` (regression, 360px) —
|
||||
viewport 360px: `document.documentElement.scrollWidth <=
|
||||
clientWidth` (the phase-07 pin, replicated locally).
|
||||
2. `.agent/user_stories/background-animation.md` (new) — story file per
|
||||
the repo format: goal, the bug report verbatim from `TODO.md` L5, the
|
||||
owner-confirmed A3 decisions + the found root cause (from task 01's
|
||||
report), E2E mapping table.
|
||||
3. Run the suite **in isolation** (prereq `podman compose up -d db`):
|
||||
`uv run pytest tests/e2e/test_background_animation.py -v --no-cov`.
|
||||
4. Regressions, in isolation, one command each:
|
||||
- `uv run pytest tests/e2e/test_dark_tech_theme.py -v --no-cov`
|
||||
- `uv run pytest tests/e2e/test_responsive_polish.py -v --no-cov`
|
||||
5. Final validation: `uv run pytest` green; `uv run pytest --cov=app
|
||||
--cov-report=term-missing` ≥ today's number (>90% gate);
|
||||
`uv run ruff check . && uv run pyright` clean.
|
||||
6. **UI Structure Check** (AGENTS.md rule 5): layers stay behind
|
||||
content, no text/contrast impact, no overflow at 360px.
|
||||
7. Finish the phase report (`.agent/reports/22_background_animation/` —
|
||||
E2E results + the task-01 screenshots).
|
||||
8. Commit (one atomic commit) and move the phase:
|
||||
```bash
|
||||
git add -A .agent/ frontend/ tests/
|
||||
git commit --no-gpg-sign -m "fix(ui): animated background actually animates — grid drift and glow breathe per the phase-08 design"
|
||||
mv .agent/phases/todo/22_background_animation .agent/phases/complete/
|
||||
```
|
||||
|
||||
## Testing & Quality
|
||||
- Story suite green **in isolation**; both regression suites green in
|
||||
isolation; full unit+integration suite green; `app/` coverage at or
|
||||
above today's number (>90%); ruff + pyright clean.
|
||||
|
||||
## Completion Criteria
|
||||
- [ ] `test_background_animation.py` 5/5 in isolation.
|
||||
- [ ] Regressions (dark tech theme, responsive polish) green in
|
||||
isolation.
|
||||
- [ ] Story file + phase report (with screenshots) exist.
|
||||
- [ ] One `--no-gpg-sign` commit; phase directory in `complete/`.
|
||||
@@ -1,91 +0,0 @@
|
||||
# Task 01 — Fix the build: relative imports, tag removal, full stage-1 asset coverage
|
||||
|
||||
**Phase:** `23_containerfile_build` · **Source:** `TODO.md` L6 —
|
||||
*"Fix Containerfile build not working"*
|
||||
|
||||
## Objective
|
||||
Make `podman build -f Containerfile .` succeed and ship the **complete**
|
||||
frontend in the image: all four pages, all four bundled page modules,
|
||||
the classic `markdown.js`, and the minified `styles.css` — with
|
||||
`header.js` evaluated exactly once per page.
|
||||
|
||||
## Work
|
||||
1. **Reproduce the failure** and record it in
|
||||
`.agent/reports/23_containerfile_build/` (log excerpt):
|
||||
- fast: `npx -y esbuild@0.25.5` on a copy of `frontend/` → the
|
||||
`Could not resolve "/assets/header.js"` error (root cause, already
|
||||
reproduced during conversion);
|
||||
- authoritative: `podman build -f Containerfile .` → stage 1 fails
|
||||
at the same line.
|
||||
2. **`frontend/assets/{app,sources,document,login}.js`** — change the
|
||||
header import from absolute URL to relative (one line each; the
|
||||
specifiers are currently `from "/assets/header.js"`):
|
||||
```js
|
||||
import { … } from "./header.js";
|
||||
```
|
||||
(owner-confirmed A4 — relative over esbuild alias; dev-server
|
||||
behavior is unchanged since the files are side-by-side and the
|
||||
module URL resolves to the same file.)
|
||||
3. **Remove the four redundant direct `header.js` tags** (owner-confirmed
|
||||
A4-2 — the single-evaluation design from `00_phase.md`):
|
||||
- `frontend/index.html` (~line 119) —
|
||||
`<script type="module" src="/assets/header.js"></script>`;
|
||||
- `frontend/sources.html` (~line 125), `frontend/document.html`
|
||||
(~line 80), `frontend/login.html` (~line 67) — same tag.
|
||||
- Update the surrounding HTML comments that describe the
|
||||
header-before-page-script load order (e.g. index.html ~lines
|
||||
115–119): the order is now guaranteed by the page script's own
|
||||
`import` (hoisted, evaluated before the page script body calls
|
||||
`initSharedHeader()`).
|
||||
4. **`Containerfile` stage 1** — cover the whole app (keep the pinned
|
||||
`esbuild@0.25.5` and the existing flags):
|
||||
```dockerfile
|
||||
RUN mkdir -p /out/assets \
|
||||
&& esbuild ./assets/app.js --bundle --minify --format=esm --target=es2022 --outfile=/out/assets/app.js \
|
||||
&& esbuild ./assets/sources.js --bundle --minify --format=esm --target=es2022 --outfile=/out/assets/sources.js \
|
||||
&& esbuild ./assets/document.js --bundle --minify --format=esm --target=es2022 --outfile=/out/assets/document.js \
|
||||
&& esbuild ./assets/login.js --bundle --minify --format=esm --target=es2022 --outfile=/out/assets/login.js \
|
||||
&& esbuild ./assets/markdown.js --minify --outfile=/out/assets/markdown.js \
|
||||
&& esbuild ./assets/styles.css --minify --outfile=/out/assets/styles.css \
|
||||
&& cp ./index.html ./sources.html ./document.html ./login.html /out/
|
||||
```
|
||||
(`markdown.js` is a classic script — minify only, **no** `--bundle`;
|
||||
it exposes globals used by the pages.)
|
||||
5. **Verify locally (no podman):** with esbuild 0.25.5, all four module
|
||||
bundles + the markdown minify succeed on the real `frontend/` (not a
|
||||
copy — the copy was only for the diagnosis).
|
||||
6. **`podman build -f Containerfile .`** → green.
|
||||
7. **Image smoke test** (results + log excerpt into the report dir):
|
||||
- throwaway Postgres 17 + pgvector (`podman compose up -d db` and
|
||||
point the container at it, or a one-off container with the same
|
||||
env as `compose.yaml`);
|
||||
- run the built image (migrations run via the entrypoint);
|
||||
- `GET /`, `/sources.html`, `/document.html`, `/login.html` → 200;
|
||||
- `GET /assets/app.js` → 200, minified (single-line-ish), and
|
||||
contains the header code (e.g. the `clearChatStorage` function
|
||||
body); `GET /assets/markdown.js`, `/styles.css`, the other three
|
||||
page modules → 200;
|
||||
- No CDN rule: none of the four served pages contain an `http(s)://`
|
||||
`src`/`href` asset reference.
|
||||
- Teardown the throwaway containers when done.
|
||||
8. **Dev-server regression check** (the tag removal touches dev page
|
||||
load — confirm boot order still holds): `uv run uvicorn
|
||||
app.main:app --reload`, load all four pages, check the sign-out
|
||||
binding exists exactly once (DevTools: no duplicate listener — one
|
||||
`POST /api/logout` per click) and `initSharedHeader()` ran. (The
|
||||
isolated E2E regressions run in task 02.)
|
||||
|
||||
## Testing & Quality
|
||||
- Steps 5–8 above; `uv run ruff check . && uv run pyright` clean
|
||||
(no Python changes, but keep the gate green).
|
||||
|
||||
## Completion Criteria
|
||||
- [ ] The recorded build failure is fixed at the root cause (relative
|
||||
imports) — not masked by an alias/patch.
|
||||
- [ ] All four direct `header.js` tags removed + comments updated; the
|
||||
page scripts' `import "./header.js"` is the only header load.
|
||||
- [ ] Stage 1 produces: 4 HTML pages, 4 bundled modules, minified
|
||||
`markdown.js`, minified `styles.css`.
|
||||
- [ ] `podman build` green; image smoke all-200 + No CDN + single
|
||||
header evaluation; dev-server boot unchanged (step 8).
|
||||
- [ ] Log/screenshot evidence in `.agent/reports/23_containerfile_build/`.
|
||||
Reference in New Issue
Block a user