refactor(agents): migrate .agent/ planning tree to .agents/

Standardize on the .agents/ directory (shared with project skills):
phases/, user_stories/, reports/, screenshots/, validate.sh, and
phase-sessions/ + pipeline.log all move to .agents/ (git mv preserves
history; runtime artifacts move alongside).

Updates every reference in AGENTS.md, README.md, .gitignore, app
docstrings, and test story headers. Historical KB content in data/
and the runtime pipeline.log transcript are left untouched.
This commit is contained in:
2026-09-05 10:57:07 -04:00
parent 766702c750
commit dbf2af26c6
1118 changed files with 664 additions and 664 deletions
@@ -0,0 +1,180 @@
# Phase 25 — Background: No Motion, Only Fading Light
**Source:** Owner report (2026-08-25, chat): the background "jitters down
and to the right every second and it slowly blinks brighter and darker. It
should be smooth, fluxuating, dimming and brightening, but not moving.
Different bright spots should slowly fade in and out."
**Story:** `.agents/user_stories/background-no-motion.md` (created by task 02)
**Context:** `frontend/assets/styles.css` — the background block:
`body::before` (44px grid, 1px lines at 60% `--line` alpha, widened radial
mask, `animation: bg-grid-drift 60s linear infinite` → `0 0` →
`44px 44px` ≈ 0.73px/s down-right) and `body::after` (indigo + cyan radial
glows, `animation: bg-glow-breathe 14s ease-in-out infinite alternate` →
opacity 0.85↔1 + scale 1↔1.05). Both are
`position: fixed; inset: 0; z-index: -1; pointer-events: none`; `<html>`
owns the `var(--bg)` canvas, `<body>` stays transparent (no-occlusion
contract). Pins to adapt: `tests/unit/test_background_animation.py`,
`tests/e2e/test_background_animation.py`,
`tests/e2e/test_dark_tech_theme.py` (`test_animated_background` pins
60s/14s; `test_reduced_motion_honored` pins the two body pseudo-layers).
## Objective
Stop the background from moving entirely, and replace the uniform
whole-layer "blink" with **different bright spots that slowly fade in and
out**: the grid becomes a static texture, and three soft glow spots
(phase-08 colors/positions, plus a third spot) each run their own
slow, **opacity-only** fade cycle at a different period, so the
background's brightness fluxuates smoothly and irregularly — no blink, no
jitter, no motion.
## Root cause (found from the code, 2026-08-25)
1. **"Jitters down and to the right every second"** = `bg-grid-drift`:
44px/60s (≈0.73px/s) in the diagonal `44px 44px` direction (exactly
down-right). A 1px grid line translated sub-pixel-by-sub-pixel is
rasterized with per-frame stepping/shimmer — perceived as a once-per-
second jitter, not smooth drift. Phase 22 made the drift *visible*;
that is precisely why it now reads as jitter.
2. **"Slowly blinks brighter and darker"** = `bg-glow-breathe`: a uniform
whole-layer opacity swing 0.85↔1 over 14s (alternate) plus
`scale(1)↔scale(1.05)` — the entire background pulses in unison (the
scale adds a faint zoom). One synchronized pulse reads as a blink; the
owner wants independent spots instead.
Phase 22 served the phase-08 design intent (grid drift + whole-layer
breathe). The owner now supersedes that design intent — this is an
owner revision of a **design comment**, not of any LOCKED anchor: A1–A17
are untouched, and the phase-08 anchors this phase must still honor are
pure CSS / zero JS / no CDN / no new assets / no `filter: blur` (A11 +
the block's perf note).
## Owner direction (2026-08-25, verbatim)
> "It should be smooth, fluxuating, dimming and brightening, but not
> moving. Different bright spots should slowly fade in and out."
1. **No movement** — no grid drift, no `scale`/`transform`, no
`background-position` animation, anywhere in the background.
2. **Fluxuating brightness** — overall page brightness varies smoothly and
irregularly (not one synchronized pulse).
3. **Different bright spots** — multiple glow spots, each fading in and
out on its own slow cycle.
4. **The static grid stays** — the owner rejected the grid's *motion*, not
the grid; it remains as a still texture. (If the owner later wants the
grid gone, that is a follow-up, not this phase.)
## Design (pure CSS, zero JS — A11 anchor)
- `body::before` — grid: **remove** the `animation`; **delete**
`@keyframes bg-grid-drift`. Keep the 44px cells, 60% `--line` alpha
lines, and widened radial mask (the static texture).
- Three glow-spot layers, one soft radial gradient each, **opacity-only**
keyframes (`0%,100%` low → `50%` 1, `ease-in-out`, `infinite`), with
different durations + negative delays so the cycles are out of phase
(periods 26/34/42s → LCM 4641s, the composite pattern effectively never
repeats within a viewing session):
| layer | spot (gradient) | keyframes | cycle |
|---|---|---|---|
| `body::after` | indigo `rgb(109 120 242 / 0.14)`, circle 56rem at 12% 8% (phase-08) | `bg-glow-a` | 26s, low opacity 0.25 |
| `html::before` | cyan `rgb(34 211 238 / 0.10)`, circle 60rem at 88% 92% (phase-08) | `bg-glow-b` | 34s, delay −12s, low 0.20 |
| `html::after` | indigo `rgb(109 120 242 / 0.09)`, circle 52rem at 14% 86% | `bg-glow-c` | 42s, delay −23s, low 0.15 |
- `html::before` / `html::after` join `body::after` as background layers:
`<html>` is the root stacking context — its `z-index: -1`
pseudo-elements paint **above** the `var(--bg)` canvas and **below**
the transparent, non-stacking `<body>`'s content, so the no-occlusion
contract holds unchanged (verify in the E2E, not just assume).
- All four layers keep: `content: ""; position: fixed; inset: 0;
z-index: -1; pointer-events: none;`
- `prefers-reduced-motion: reduce` stills **all four** layers
(`animation: none`).
- No `filter` (phase-08 no-blur perf anchor), no JS, no new assets; the
WCAG palette and every text contrast pair are untouched (the layers
carry no text). Opacity-only keyframes stay compositor-friendly.
## Dependencies
- `08_story_dark_tech_theme` (complete) — the layers, the palette, the
pure-CSS / no-blur / no-CDN anchors, and the 60s/14s pins in
`tests/e2e/test_dark_tech_theme.py` this phase adapts.
- `22_background_animation` (complete) — the current implementation and
the unit/E2E pins this phase supersedes.
- `07_story_responsive_polish` (complete) — the 360px overflow pin (the
layers are `fixed; inset: 0` — they must add no width).
## Tasks
1. `01_still_background_css.md` — the CSS redesign (static grid + three
opacity-only glow fades) + unit source pins (new
`tests/unit/test_background_no_motion.py`; the phase-22 pins in
`tests/unit/test_background_animation.py` adapted).
2. `02_e2e_story_suite_commit.md` — `tests/e2e/test_background_no_motion.py`
(the story gate, isolated), the regression suites adapted
(`test_background_animation.py`, `test_dark_tech_theme.py`), the story
file, the phase report + screenshots, final validation, the single
atomic commit, phase move to `complete/`.
## Locked decisions
- **A11 untouched** — vanilla HTML/CSS/JS in git, no CDN, zero JS, system
fonts; the whole change is CSS. **No `filter`/`blur`** (phase-08 perf
anchor). No new assets. **No anchor changed** — the superseded spec is
the phase-08 *design intent* (grid drift + whole-layer breathe), not a
LOCKED decision; the owner's 2026-08-25 direction is recorded above as
the revision. **A16 honored** — one new story E2E suite + adapted
regressions. **A17 honored** — one atomic `--no-gpg-sign` commit.
## Testing & Quality
- **Unit (source-level, repo source-pin pattern):**
- New `tests/unit/test_background_no_motion.py` — pins the full new
contract (task 01, step 6).
- Adapted `tests/unit/test_background_animation.py` — the phase-22
drift/breathe pins flip to the new contract; the generic
layer-plumbing and no-blur/no-JS sections stay (task 01, step 7).
- **Integration:** none (no `app/` changes).
- **Coverage:** frontend-only; the >90% `app/` gate is unaffected (TOTAL
must stay ≥ the pre-change number).
- **E2E:** `tests/e2e/test_background_no_motion.py` (task 02), green
**in isolation** (prereq `podman compose up -d db`); regressions green
in isolation after adaptation: `test_background_animation.py`,
`test_dark_tech_theme.py`, `test_responsive_polish.py`.
- **Lint/types:** `uv run ruff check . && uv run pyright` clean.
## Completion Criteria
- [ ] No movement: the grid is static (`bg-grid-drift` gone, no
`animation` on `body::before`) and **no `bg-*` keyframe animates
anything but `opacity`** — audited in a real Chromium via
`document.styleSheets` (E2E test 3).
- [ ] Three distinct bright spots (`body::after`, `html::before`,
`html::after`) run distinct slow opacity fades (26s/34s/42s,
out of phase); the timelines advance; the layer opacity and a
clipped screenshot of the glow region measurably change within a
few seconds (E2E tests 2, 4, 5).
- [ ] Contracts hold: all four layers `fixed; inset: 0; z-index: -1;
pointer-events: none`; `<html>` keeps the `var(--bg)` canvas and
`<body>` stays transparent (no occlusion); `prefers-reduced-motion`
stills all four; no horizontal overflow at 360px (E2E tests 6–8).
- [ ] `.agents/reports/25_background_no_motion/` documents the two root
causes with before/after screenshot pairs
(`.agents/screenshots/25_background_no_motion/`).
- [ ] `uv run pytest` green; `uv run pytest --cov=app
--cov-report=term-missing` TOTAL ≥ pre-change number (gate >90%).
- [ ] `uv run pytest tests/e2e/test_background_no_motion.py -v --no-cov`
green in isolation; `test_background_animation.py`,
`test_dark_tech_theme.py`, `test_responsive_polish.py` green in
isolation after adaptation.
- [ ] `uv run ruff check . && uv run pyright` clean.
- [ ] UI Structure Check (AGENTS.md rule 5): layers stay behind content,
no text/contrast impact, no 360px overflow.
- [ ] `.agents/user_stories/background-no-motion.md` exists; the old
`background-animation.md` story carries a supersession note.
- [ ] One `--no-gpg-sign` commit staging **only this phase's files** (the
unrelated dirty `TODO.md` must NOT be staged);
`.agents/phases/todo/25_background_no_motion/` moved to
`.agents/phases/complete/`.
## Commit
```bash
git add -f .agents/phases/complete/25_background_no_motion \
.agents/user_stories/background-no-motion.md \
.agents/user_stories/background-animation.md \
.agents/reports/25_background_no_motion \
.agents/screenshots/25_background_no_motion
git add frontend/assets/styles.css tests/unit tests/e2e
git commit --no-gpg-sign -m "fix(ui): background no longer moves — static grid, three glow spots fading in and out on their own slow cycles (owner 2026-08-25)"
```