From ba88ef7547d070e67337d24359bf184cde0b114f Mon Sep 17 00:00:00 2001 From: Hector Alvarez Date: Sun, 13 Sep 2026 16:25:32 -0500 Subject: [PATCH 1/7] chore(cline): initialize memory bank --- memory-bank/activeContext.md | 32 ++++++++++++++++++++++ memory-bank/productContext.md | 34 ++++++++++++++++++++++++ memory-bank/progress.md | 36 +++++++++++++++++++++++++ memory-bank/projectbrief.md | 41 ++++++++++++++++++++++++++++ memory-bank/systemPatterns.md | 50 +++++++++++++++++++++++++++++++++++ memory-bank/techContext.md | 47 ++++++++++++++++++++++++++++++++ 6 files changed, 240 insertions(+) create mode 100644 memory-bank/activeContext.md create mode 100644 memory-bank/productContext.md create mode 100644 memory-bank/progress.md create mode 100644 memory-bank/projectbrief.md create mode 100644 memory-bank/systemPatterns.md create mode 100644 memory-bank/techContext.md diff --git a/memory-bank/activeContext.md b/memory-bank/activeContext.md new file mode 100644 index 0000000..91d35d2 --- /dev/null +++ b/memory-bank/activeContext.md @@ -0,0 +1,32 @@ +# Active Context + +Updated: 2026-09-13 + +## Current focus + +**Repo housekeeping** on branch `chore/repo-housekeeping` (off `dev`): + +1. ✅ Initialize memory bank (this directory) — commit 1. +2. README corrections (HK-1): drop `robots.txt` from `src/public/` tree (it's + build-time generated), note sitemap/robots generation, add alarm tests to + the tests tree, add `tasks/` to the repo-layout table. +3. CI actions bump (HK-2): checkout@v7, setup-node@v7, setup-buildx@v4, + login@v4, build-push@v7, hadolint-action@v3.5.0 (majors verified 2026-09-13). +4. Husky guard (HK-3): skip lint-staged when no staged file matches its globs + (silences "could not find staged files" on workflow/env-only commits). +5. `tasks/` close-out: sprint.md reflects US-001 merged + deployed; + backlog HK items resolved. + +## Environment state + +- `dev` = `origin/dev` = `223d41f`; local `main` reset to `origin/main` + (`c7ecdca`, post-rebase-merge). Working tree clean. +- US-001 deployed (Release #5 success); homepage may serve up to 2 h of edge + cache; `healthz` verified 200. + +## Next steps after housekeeping + +- Owner: production audio check (alarm tone) → US-001 fully Done. +- Pick next epic: US-002 (recurring alarms) vs US-004 (multi-clock). +- Decide HK-5 (nginx 1.27 re-pin) timing — Dockerfile + digest bump + local + `nginx -t`. diff --git a/memory-bank/productContext.md b/memory-bank/productContext.md new file mode 100644 index 0000000..ffcc05b --- /dev/null +++ b/memory-bank/productContext.md @@ -0,0 +1,34 @@ +# Product Context + +## Why + +A personal productivity site: a clean, dark, always-visible clock for a wall +screen or a spare tab, growing into timer and alarms so one page replaces +several utilities. Hosted under the taylormadetech.net domain. + +## Problems it solves + +- No-install clock/timer/alarm with persistence (localStorage, no accounts). +- Distraction-free: no ads, no login, no tracking; single static page. +- Self-hosted learning vehicle for a production-grade deploy pipeline + (GHCR images, webhook deploys, Cloudflare Tunnel, edge caching). + +## How it should work + +- **Clock** — always ticking, 12-hour format with AM/PM, seconds shown, + timezone-aware, dev marker only in dev builds. Tab title mirrors the time. +- **Timer** — manual duration entry + presets, start/pause/reset, survives + reload via `localStorage`, beep + visual overlay on completion. +- **Alarms (US-001)** — create/name/enable/disable/delete multiple alarms, + one-shot firing with 9-minute snooze, ring overlay with Snooze/Dismiss, + keyboard dismissal (Esc/Enter), persisted across reloads. +- **Limitation (documented in-panel)** — alarms/timer ring only while the tab + is open; background-tab throttling can delay rings up to ~1 minute. No + OS/browser notifications (out of scope per US-001). +- **404 page** — served by the edge with a real 404 status; separate CSS file + because the CSP forbids inline styles. + +## UX notes + +- Dark theme, large digits, responsive; keyboard-friendly controls. +- Persistence is per-browser; no server state at all (static app). diff --git a/memory-bank/progress.md b/memory-bank/progress.md new file mode 100644 index 0000000..c296477 --- /dev/null +++ b/memory-bank/progress.md @@ -0,0 +1,36 @@ +# Progress + +## Status + +US-001 (Multiple Alarms) is **deployed to production** (PR #13 merged +2026-09-13 21:17Z; Release run #5 succeeded; `healthz` verified 200). +Site is live at `clock.taylormadetech.net`. + +## What works + +- Clock (tick, title mirror, timezone), timer (presets, persistence, beep), + alarms (multi, snooze, persistence, ring overlay) — 135 unit tests green. +- Full CI gate: prettier, eslint, typecheck, vitest+coverage, build, + `nginx -t` × 2, compose config × 2, hadolint × 4. +- Automated deploys: `main` → GHCR → webhook → swap with auto-rollback. +- Cloudflare edge caching of `index.html` (2 h Cache Rule; `/healthz` + uncacheable) per ADR 0006. + +## Remaining / known issues + +- Production audio QA: alarm tone not yet verified by a human (jsdom can't + test audio) — last open US-001 DoD item. +- Edge-cached homepage can lag up to 2 h behind a deploy (expected; judge + freshness via `/healthz` or hashed assets). +- CI actions pinned to Node-20-runtime majors (HK-2 — fix in progress). +- `nginxinc/nginx-unprivileged:1.27-alpine` is an EOL mainline branch + (HK-5 candidate: re-pin to current stable + digest). +- npm minor drift: eslint/prettier/lint-staged/TS patch bumps available; + vite 7+/vitest 5 majors deliberately deferred (not housekeeping). +- Multi-clock scaffold (`clock-registry.ts`) unwired (backlog US-004). +- FLIP helper duplicated in timer/alarm UI (backlog US-003). + +## Next epic decision (owner) + +US-002 recurring alarms vs US-004 multi-clock (US-004 wants US-003 item 1 +first). See `tasks/backlog.md` + `tasks/sprint.md`. diff --git a/memory-bank/projectbrief.md b/memory-bank/projectbrief.md new file mode 100644 index 0000000..4aa95c5 --- /dev/null +++ b/memory-bank/projectbrief.md @@ -0,0 +1,41 @@ +# Project Brief — Simple Clock + +A single-page digital clock website (clock + countdown timer + alarms) built +with TypeScript and Vite, served from a hardened container stack behind +Cloudflare Tunnel. Live at `clock.taylormadetech.net`. + +## Goals + +- Always-accurate time display, zero-friction UX (no accounts, no tracking). +- Countdown timer with presets; multiple named alarms with snooze and + persistence (US-001, deployed 2026-09-13). +- Fully automated, unattended production deploys (`main` → GHCR → webhook). +- Small attack surface: unprivileged containers, no inbound ports, CSP without + `unsafe-inline`. + +## Structure (folder-per-artifact monorepo) + +- `web/` — Vite app, tests, both app container images +- `nginx/` — edge reverse-proxy image (security headers, real client IP, `/healthz`) +- `webhook/` — deploy webhook image (listens for release trigger, runs deploy) +- `scripts/` — host-side helpers (deploy, tunnel bridge, bootstrap) +- `tasks/` — backlog + sprint tracking +- `docs/` — ADRs, runbooks, user stories + +## Constraints (see ADRs 0001–0006) + +- Both containers listen on 8080; host port mappings must target 8080. +- Base images pinned by digest; bump tag + digest together, deliberately. +- Production ingress is Cloudflare Tunnel only — dev and prod tunnels use + different tokens. +- `sitemap.xml` / `robots.txt` are build-time generated from `VITE_SITE_URL`. +- Edge caching: `index.html` cached via Cloudflare Cache Rule (origin + `cdn-cache-control` is ignored on the free plan); `/healthz` never cached. + +## Success criteria + +- `make check` green (lint + typecheck + tests) before any merge. +- Deploys require no manual host work; rollback is automatic on failed health + check. +- Alarms ring while the page is open (documented limitation: no background-tab + rings, no OS notifications). diff --git a/memory-bank/systemPatterns.md b/memory-bank/systemPatterns.md new file mode 100644 index 0000000..9bf573f --- /dev/null +++ b/memory-bank/systemPatterns.md @@ -0,0 +1,50 @@ +# System Patterns + +## App architecture (feature-based, see README) + +- **Pure cores** (`features/*/*.core.ts`, `shared/time`, `shared/audio`): all + business logic, zero DOM deps, fully unit-tested. Pure functions over + explicit state (`computeClockTick`, timer/alarm state machines). +- **UI layers** (`*.ui.ts`): query DOM, bind events, render from the core. +- **Adapters** (`*.storage.ts`, `shared/audio/beep.ts`): isolate side effects + (`localStorage`, `AudioContext`); platform deps are injectable for mocking. +- **Composition root** (`app/app.ts`): the ONLY place that knows page element + IDs; `main.ts` calls `bootstrap()`. `app/config.ts` holds build-time config + (mode, storage keys). +- Features are consumed via barrels (`@/features/clock`), path alias `@/` → + `src/`. `shared/dom/query.ts` provides typed querySelector helpers. +- Known duplication: FLIP panel-open helper duplicated between `timer.ui.ts` + and `alarm.ui.ts` (backlog US-003 item 1 → `shared/dom/`). +- `features/clock/clock-registry.ts`: multi-clock scaffold, not wired (US-004). + +## Container / pipeline architecture + +- **web**: dev image (Vite HMR, source-mounted) and prod image (build → + static serve on nginx-unprivileged). **nginx** edge: security headers, + `/healthz`, real client IP. **webhook**: receives release POST, runs + `scripts/deploy.sh` against the host through the Docker socket. +- CI (`.github/workflows/ci.yml`): prettier, eslint, typecheck, vitest with + coverage, build, `nginx -t` on both configs, `docker compose config` on both + stacks, hadolint on all four Dockerfiles. +- Release (`release.yml`): on CI success on `main` → build/push GHCR images + (`latest` + `sha-`, provenance + SBOM) → POST to deploy webhook. +- Git history: linear, rebase-merge only; PRs `dev` → `main`. + +## Conventions + +- Tests mirror `src/` one-to-one under `web/tests/unit/` (Vitest + jsdom); + behavior-documenting tests over DOM re-queries. +- Prettier: tabs, single quotes, semicolons, width 80, trailing commas. + ESLint: `consistent-type-imports`, no unused vars (`_` prefix), `no-console` + (error/warn only). +- Husky pre-commit: lint-staged on staged `web/` files. Never `--no-verify`. +- Conventional commits, scoped; each commit builds and passes checks alone. + +## Security invariants + +- Edge CSP has no `unsafe-inline`; every page (incl. 404) links a CSS file. +- Security headers live only in `nginx/nginx.conf`; the app container knows + nothing of the outside world. +- `.env` / `.env.prod` are secrets — never edit, echo, or copy tokens between + dev and prod tunnels. +- Prod deploys fire automatically on `main`; never run prod targets unasked. diff --git a/memory-bank/techContext.md b/memory-bank/techContext.md new file mode 100644 index 0000000..70cb55b --- /dev/null +++ b/memory-bank/techContext.md @@ -0,0 +1,47 @@ +# Tech Context + +## Stack + +- **TypeScript** + **Vite 6** (vanilla DOM — no framework), Node ≥22 + (`web/.nvmrc`: 22). +- **Vitest 4 + jsdom** for tests; `@vitest/coverage-v8` for coverage. +- ESLint (flat config, `web/eslint.config.js`), Prettier, husky + lint-staged. +- Containers: `node:22-alpine`, `nginxinc/nginx-unprivileged:1.27-alpine` + (EOL mainline branch — see backlog HK-5), `golang:1.24-alpine`, `alpine:3.21` + — all digest-pinned (ADR 0005). Deployer: `github.com/adnanh/webhook` + pseudo-version pin. + +## Commands + +All npm commands run inside `web/`; make targets from the repo root. + +| Purpose | Command | +| :--- | :--- | +| Full quality gate (before declaring done) | `make check` (lint + typecheck + test) | +| Dev stack up/down/health | `make dev-up` / `make dev-down` / `make dev-health` | +| Fast host-only dev loop | `cd web && npm run dev` | +| Format check (CI-enforced) | `cd web && npm run format:check` | +| Coverage | `cd web && npm test -- --coverage` | +| Production build | `cd web && npm run build` → `web/dist/` | + +CI-relevant changes (compose, Dockerfiles, `nginx.conf`) must be validated +locally with the same checks CI runs (hadolint via `hadolint/hadolint` image, +`nginx -t` via the nginx image, `docker compose config -q`). + +## Setup facts + +- Dev edge at `http://localhost:8100` (compose); opt-in dev tunnel with its + own token. Prod stack uses GHCR images + prod tunnel + webhook. +- `VITE_SITE_URL` is the single source for canonical/OG URLs and the generated + `sitemap.xml` / `robots.txt` (see `web/vite.config.ts`). Unset → falls back + to `http://localhost:8100`. +- Env files: `.env` (dev), `.env.prod` (prod secrets) — gitignored, never + edit/echo. `.env.sample` documents the variables. + +## Constraints + +- Both containers listen on 8080; host mappings must target 8080. +- No `unsafe-inline` CSP (ADR 0002): no inline `