NO

nordeim/omni-study

Developer tools
0 stars Quality 51 Trend 20

A production-ready, self-hosted clone (functional superset) of the omni-study1.base44.app study-planning app, rebuilt as a single Next.

Overview

A production-ready, self-hosted clone (functional superset) of the omni-study1.base44.app study-planning app, rebuilt as a single Next.js application you fully own: your database, your AI keys, your deployment. The reference app is a study companion that keeps a student's whole life in one place — but it lives behind a hosted platform login with no source access. StudyFlow rebuilds every surface (and fills in the ones the reference left empty) as an open codebase: twenty views from the daily dashboard to a focus timer, all wired to real CRUD persistence through Prisma/SQLite. Visual parity was measured from the live reference (computed styles, not eyeballing), and the places where the framework changed underneath — Tailwind v4's engine differences chief among them — are documented and pinned by tests.

README

StudyFlow (Omni-Study)

StudyFlow — your study companion. A production-ready, self-hosted clone (functional superset) of the omni-study1.base44.app study-planning app, rebuilt as a single Next.js application you fully own: your database, your AI keys, your deployment.

Overview

The reference app is a study companion that keeps a student’s whole life in one place — but it lives behind a hosted platform login with no source access. StudyFlow rebuilds every surface (and fills in the ones the reference left empty) as an open codebase: twenty views from the daily dashboard to a focus timer, all wired to real CRUD persistence through Prisma/SQLite. Visual parity was measured from the live reference (computed styles, not eyeballing), and the places where the framework changed underneath — Tailwind v4’s engine differences chief among them — are documented and pinned by tests. Where the reference shows empty states, StudyFlow ships working features: an AI study assistant, a math solver that reads photographed problems, flashcard decks with a study mode, and a grade tracker with trend charts.

All 20 views (plus the login card, three mobile captures, seven dark-mode captures, the session-17 fresh-user evidence set — zero-data Analytics/Grade Tracker, the reference-measured empty-state hints, the Settings Profile/Notifications tabs, the mobile StudyGroups copy — the session-11 audit evidence pair, the session-12 accessibility evidence set — forced-colors renders, print pages, the focused skip link — and the session-13 resilience evidence set — the AI-chat failure rollback, the solver’s MIME guard toast, the remediated mobile drawer — and the session-14 hardening evidence set — the offline banner with the bounced-back composer, the AI rate-limit toast, the display-name guard) are in docs/screenshots/ — the standard 31 were refreshed after the session-11 theme-system remediation (node scripts/capture-studyflow.mjs); the S12 set via node scripts/capture-s12-evidence.mjs and the S13 set via node scripts/capture-s13-evidence.mjs, and the S14 set via node scripts/capture-s14-evidence.mjs, the S15 auth-flow set via node scripts/capture-s15-evidence.mjs (the login inline error, the signup form, the verify OTP screen, check-email, the calculator keyboard superset), and the S16 mobile set via node scripts/capture-s16-evidence.mjs (the auth sub-screens + the dashboard at 390×844 — the mobile-only responsive fixes; no default-media desktop visual changed in sessions 12–16; the prior pins are the byte-parity guarantee), the S17 fresh-user set via node scripts/capture-s17-evidence.mjs, and the S18 RUM set via node scripts/capture-s18-evidence.mjs (the beacon-live dashboard + mobile, the GET /api/rum aggregate, the un-instrumented login), and the S19 diagnostics set via node scripts/capture-s19-evidence.mjs (the /rum panel — desktop light + dark + mobile 390px), and the S20 v3 set via node scripts/capture-s20-evidence.mjs (the /rum panel with the per-card trend sparklines + the Export CSV action + the exported CSV head), and the S21 portability set via node scripts/capture-s21-evidence.mjs (the /export page — desktop light + dark + mobile 390px + the downloaded JSON envelope head), and the S22 boot-guard set via node scripts/capture-s22-evidence.mjs (the dev-server dashboard + /export + mobile 390px screenshots + the boot-guard evidence JSON — the negative spawn’s exit + message, the positive-control health, the dev-boot warn line), and the S23 import set via node scripts/capture-s23-evidence.mjs (the /export page with the Restore card — desktop light + dark + mobile 390px + the completed-import result state + the API round-trip evidence JSON — the first import’s summary, the idempotent re-import, the validation-error family, the rollback guard, the probe cleanup), and the S24 change-password set via node scripts/capture-s24-evidence.mjs (the Settings Profile tab with the Change password card — desktop light + dark + mobile 390px + the success-note state + the API round-trip evidence JSON — the guard family, the anon gate, the fresh-user rotation with the old-401/new-200 login pair, the session-survives proof, the demo restore), and the S25 account-deletion set via node scripts/capture-s25-evidence.mjs (the Settings Profile tab with the Danger zone card — desktop light + dark + mobile 390px + the revealed confirmation-form state + the API round-trip evidence JSON — the guard family, the anon gate, the fresh-user deletion with the DB-level cascade proof, the cleared-session me-401, the demo-intact proof), and the S26 email-change set via node scripts/capture-s26-evidence.mjs (the Settings Profile tab with the Email address card — desktop light + dark + mobile 390px + the success-note state with the live identity-block update + the API round-trip evidence JSON — the guard family incl. the same-email 400 + the duplicate 409, the anon gate, the fresh-user rotation with the DB-level emailVerified-stays-true proof + the old-401/new-200 login pair, the demo restore), and the S27 dark-sweep repair set (the before-state — MODE empty, 97 phantom flashbulbs + 7 phantom unreadable measured in LIGHT mode — and the after-state — MODE dark, all 20 views clean in real dark mode — with the guard’s negative-control exit, the restore proof, and the refreshed standing 31) in docs/screenshots/s27-dark-sweep-evidence.json, and the S28 connectivity-repair set (the before-state — the phantom “Login offline shows no/silent feedback” finding whose own measured bodyText contained the offline message — and the after-state — “Login offline announces the failure through the S15 inline alert (stays on the card, button recovered)” with findings 0 — with the reproduction probe, the negative-control exit, the gates, and the refreshed standing 31) in docs/screenshots/s28-connectivity-audit-evidence.json + the login-offline inline-alert capture s28-login-offline-inline-alert.png, and the session-29 doc-alignment refresh (the standing 31 re-captured from the remediated codebase — docs-only changes, zero app bytes moved) per docs/remediation-plan-session29.md, and the session-30 dependency-security evidence set (the bun-audit classification envelope with the two documented non-production advisories, the negative controls, the gates) in docs/screenshots/s30-dep-audit-evidence.json per docs/remediation-plan-session30.md

Key Features

Feature What it does
📊 Dashboard Time-aware greeting, conditional overdue alert banner, four stat cards, today’s tasks, upcoming exams & assignments
☀️ My Day Quick-add tasks, amber “Today’s Progress” card, Suggestions for overdue/upcoming tasks
✅ Tasks Lists (custom lists + Important), search, filters, full CRUD — priority (colored round checkbox), repeat, My Day toggle, subtasks
📅 Calendar Sunday-first month grid (dynamic week count) + day detail (border-l-4 colored rows), timeline agenda mode, icon+tab mode switch, legend
🔔 Events & Reminders Dark terminal-style panel: 7-day sections, recurring events, multi-reminders, color-coded
🏫 Timetable grid-cols-8 week table with full day heads + 60px hour rows, mobile day accordion, Grid Builder, alternating Week A/B, My Classes with today’s date
📚 Assignments / Exams Subject-tagged, urgency badges, status tracking — type + priority pills, interactive progress sliders; exam cards with subject color strips, durations, topics
🗒️ Notes Notebooks, tags, pinned notes, autosaving editor
🃏 Flashcards Two-panel view with colored deck swatches, card grid with difficulty badges, 3D-flip study mode, AI card generation
📝 Practice Tests Question builder (manual + AI generation), score recording with completion %
👥 Study Groups Members, next meeting, subject linking
📈 Grade Tracker Gradient summary card + target/chart icons, weighted averages per subject, GPA, SVG trend chart
🧠 Analytics Icon-block stat cards (Tasks Completed, Assignments, Focus Time, Upcoming Exams), axis-labeled area charts (7-day activity, focus gradient), grade trend, subject distribution
📁 Files Folders, uploads (≤ 2 MiB), links, grid/list views
🧮 Calculator Suite Basic, scientific (shunting-yard engine, no eval), GPA, unit converter + history
ƒ Math Solver AI step-by-step solutions — typed or photographed (vision)
✨ AI Assistant Chat with six study-tuned quick modes, persisted history
⏲️ Focus Timer Pomodoro/Deep Work presets, subject logging, session stats
🎨 Theme System Light/Dark/System + seven accent colors + emoji avatars, persisted per user — dark mode fully re-themes every surface (a superset: the reference’s own Dark option is a no-op), applies pre-paint (no light flash on load), themes the login route for dark-OS visitors, and System mode tracks OS theme changes live
♿ Accessibility Forced-colors (Windows High Contrast) state restoration (system-color outlines on the selected day/today/active tab/active nav, the slider progress gradient), a skip-to-content link (WCAG 2.4.1), print that forces light + keeps essential backgrounds (print-color-adjust: exact) + a timetable week table that fits the page, and prefers-reduced-motion support — all supersets (the reference has none)
🛡️ Resilience Failure-state hardening — every AI call carries a 120 s deadline (AbortSignal.timeout) so a hung backend never locks the composer; a failed AI send rolls the message back into the composer instead of orphaning it; the solver guards non-image files client-side; uploads reject nameless files; downloads speak RFC 5987 (filename*=UTF-8''…) so unicode filenames save correctly; verified at 300+ rows with zero main-thread blocking
📡 Connectivity A global offline banner (amber pill, screen-reader announced) the moment the network drops — renders nothing while online (byte-identical DOM); transport failures toast a human message (“You appear to be offline…”) instead of raw browser text; every per-request path recovers (bounced-back composer, dialogs that keep their forms, SPA navigation on cached data) — a superset (the reference has no offline handling)
🔒 Security hardening Per-user AI rate limits (20 req/15 min on all four LLM routes — 429 with Retry-After), download responses carry X-Content-Type-Options: nosniff + Cache-Control: private, no-store, whitespace-only display names rejected — alongside the scrypt/HMAC/rate-limited auth baseline and the AUTH_SECRET boot guard (session-22): a production boot without a real AUTH_SECRET — missing, empty, or the public dev fallback pasted in — REFUSES to start, exiting with the openssl rand -hex 32 remedy (dev/test keep the documented zero-config fallback with a one-line boot warning); weak-but-present secrets (< 32 chars) draw a boot warning
♿ AI accessibility The assistant transcript and the math solver’s solution are polite live regions (aria-live + aria-busy) — replies and solutions land announced for screen readers (WCAG 4.1.3); errors were already announced via the aria-live toast stack
🔐 Auth Email & password (scrypt + HMAC cookie sessions), rate-limited — with the reference’s full account journey: a real Create-your-account form, email verification (6-digit OTP, 15-minute one-shot codes), an unverified-login gate, a 3-state forgot-password flow (Reset your password → Check your email → the reset link), and inline form errors (the reference’s red/green alert pattern). No SMTP in the self-hosted context: the verification code and reset link surface to the actor directly (ADR-013). Session-24 adds the account-security rotation: a signed-in user changes their own password from Settings → Profile (current-password proof + the register policy + the must-differ rule, rate-limited — a pure superset; the reference has no password surface) (ADR-022). Session-25 completes the ownership arc with the exit: a signed-in user permanently deletes their own account from Settings → Profile → Danger zone (password proof + the typed word DELETE + the ONE cascade write wiping every collection; the session cookie dies with the account — a pure superset; the reference has no deletion surface) (ADR-023). Session-26 adds the account-identity rotation: a signed-in user changes their own login email from Settings → Profile → Email address (password proof + the typed-twice confirm guarding the typo lockout + must-differ/uniqueness guards, rate-limited; the session survives — a pure superset; the reference has no email-change surface) (ADR-024)
⌨️ Calculator keyboard The calculator accepts physical-keyboard input (digits, operators, Enter to evaluate, Backspace, Escape to clear) — a functional superset; the reference’s calculator is click-only. Keystrokes never leak into form fields (GPA rows, converter inputs are guarded)
⚡ Load stability (CWV) The authenticated shell pre-warms the active view’s data IN PARALLEL with the auth call — the first paint renders with final geometry (dashboard CLS 0.00, was 0.117–0.125; Lighthouse-class mobile preset: login LCP ~640 ms, dashboard LCP ~2.4 s — both CWV-good, vs the reference’s own 7.6–8.0 s login / 4.4 s + CLS 0.372 dashboard). Auth sub-screens render the reference’s responsive mobile geometry (20px headings, 44px Sign in, the 76px verify rhythm) — measured at 390×844 on both apps
🆕 Fresh-user journey A registered user’s first run now mirrors the reference’s measured zero state: Analytics and Grade Tracker render their stat cards at zero data (no empty-state blocks the reference never renders), every view’s empty-state hint carries the reference’s exact copy, Settings opens its five tabs with the reference’s per-tab headers — and the Profile tab persists School Name, Grade Level (6th Grade → Graduate), a 1–12 hour Daily Study Goal and the Account-created line; the Notifications tab carries the persisted Enable Notifications master toggle (session-17)
📡 RUM observability Real-user web-vitals monitoring (a pure superset — the reference has no field telemetry): the authed shell mounts an invisible beacon that reports TTFB/FCP/LCP/CLS/INP to the in-app POST /api/rum endpoint (batched, keepalive, failures swallowed); GET /api/rum answers p75 per metric + the recent samples for the owner. The beacon renders zero DOM — visual parity is untouched (session-18)
📊 RUM diagnostics panel The owner-facing surface for the beacon’s field data: the auth-gated /rum route renders the five Core Web Vitals at p75 with rating badges derived from the public CWV bounds, a samples line, the ten most recent events, and a Refresh action — themed (dark mode + the seven accents) through the production theme path, responsive to 390px, linked from NOWHERE (the shell’s 20-link parity stays byte-intact; the owner navigates directly — see DEPLOYMENT.md §8) (session-19). Session-20 v3: each sampled card carries a SPARKLINE trend line (the metric’s 20 most recent samples, oldest → newest — accent-themed SVG, edge cases unit-pinned) and an Export CSV action downloads the most recent 2000 events as a spreadsheet-ready RFC-4180 CSV (raw values, the S13/S14 download-header contract)
💾 Data portability The “own your data” exit: GET /api/export/data downloads the user’s COMPLETE content — all 20 collections (tasks, notes, decks, grades, files with payloads, …) in chronological order with ids/foreign keys intact — as a versioned JSON envelope (format: studyflow-data-export, version: 1; the hashless profile rides user; tokens, RUM telemetry, and the password hash are excluded by decision). The auth-gated /export page previews it: a per-collection count grid, the Download JSON action, and the format documentation — themed through the production path, responsive to 390px, linked from NOWHERE (the owner navigates directly — DEPLOYMENT.md §8.2) (session-21). Session-23 completes the round-trip: POST /api/import/data restores an export into the importing account — upsert-by-id (existing rows update to the file’s values, new ids create with the importer’s userId — the envelope is portable across accounts), all-or-nothing in ONE transaction (any failure rolls back completely), never deletes, content only (never identity); dangling optional FKs null (the schema’s own SetNull semantics), a dangling required cards.deckId is a rejected corrupt envelope, folders sort parents-first with cycle rejection — all pre-validated by the pure seam so Prisma’s P2003 never surfaces opaquely. The /export page’s Restore from a backup card drives it (file picker + the created/updated result report; a 10 MiB cap + a 10/15-min rate limit)
🐳 Docker deployment A production Dockerfile (multi-stage: prod-deps → standalone build → bun runtime) + docker-compose.yml with a SQLite volume, an auth-secret guard, an init profile (schema push + seed) and a healthcheck — one command from clone to container (docker compose --profile init up); the runtime layout is validated end-to-end outside Docker (session-18)

Architecture

Layer Technology Version Purpose
Framework Next.js (App Router) 16.4.0 One page + 20 path rewrites (SPA parity with the reference)
UI runtime React 19 Client view switching inside a single app shell
Language TypeScript 5 (strict) End-to-end types, no any
Styling Tailwind CSS 4 (CSS-first @theme) v3-pinned palette/shadow tokens for reference parity
Components Radix UI (shadcn-style) current Accessible primitives themed via CSS vars
Database SQLite via Prisma ORM 6.19.3 Zero-config, single file at repo-root db/
State Zustand 5 App/theme/data stores; URL is the routing truth
Validation Zod 4.6.5 Every API boundary
AI z-ai-web-dev-sdk 0.0.18 Server-only: chat, math solve, vision
Unit tests Vitest 5 Pure seams (router, date, calculator, auth, schemas, view collections)
E2E tests Playwright 1.63 Production standalone build + isolated db/e2e.db
Runtime Bun ≥ 1.3 bun run dev, scripts, seed
flowchart TB
    B["Browser — app shell (sidebar + view router)"] --> G["Next.js 16 route handlers /api/*"]
    G --> V["Zod validation"]
    V --> C["CRUD delegate factory (userId-scoped)"]
    C --> P[("Prisma 6 + SQLitedb/custom.db")]
    G --> AI["z-ai-web-dev-sdk(chat / math / vision)"]
    G --> S["HMAC cookie sessionsscrypt + rate limit"]

File Hierarchy

📂 src/
├── 📂 app/
│   ├── 📄 page.tsx              ← the whole app: auth gate + shell + view switcher
│   ├── 📄 login/page.tsx        ← reference-parity login card
│   ├── 📄 globals.css           ← Tailwind v4 @theme tokens + trap pins
│   └── 📂 api/                  ← auth, health, 16 entity CRUDs, AI, files, settings
├── 📂 components/
│   ├── 📂 ui/                   ← button, card, dialog, select, tabs, toast, …
│   ├── 📂 layout/               ← sidebar (fixed glass) + mobile drawer chrome
│   └── 📂 views/                ← the 20 view components + shared primitives
├── 📂 lib/
│   ├── 📄 db-path.ts            ← the DATABASE_URL contract (unit-tested)
│   ├── 📄 db.ts                 ← Prisma singleton with env resolution
│   ├── 📄 auth.ts               ← scrypt, HMAC sessions, rate limiter
│   ├── 📄 router.ts             ← 20-view ↔ PascalCase-path map
│   ├── 📄 data.ts               ← client cache + typed mutations
│   ├── 📄 calculator.ts         ← shunting-yard engine, GPA, converter
│   ├── 📄 theme.ts              ← 7 accents, RGB-triplet tokens
│   └── 📂 server/               ← http helpers + entity CRUD delegates
├── 📂 prisma/
│   ├── 📄 schema.prisma         ← 24 models; DATABASE PATH CONTRACT header
│   └── 📄 seed.ts               ← idempotent demo data
├── 📂 tests/
│   ├── 📄 *.test.ts             ← Vitest unit layer (834 tests)
│   └── 📂 e2e/                  ← Playwright specs (299 specs)
└── 📂 docs/
    ├── 📄 Tailwind-V4-Validation-Report.md  ← the five documented v3→v4 traps
    ├── 📄 remediation-plan.md   ← session-2 parity audit: evidence, fixes, non-gaps
    ├── 📄 remediation-plan-session3.md ← session-3 mobile-chrome & token audit
    ├── 📄 remediation-plan-session4.md ← session-4 text-metrics & login audit
    ├── 📄 remediation-plan-session5.md ← session-5 interactive-chrome & view-body audit
    ├── 📄 remediation-plan-session6.md ← session-6 populated-state & row-design audit
    ├── 📄 remediation-plan-session7.md ← session-7 lightly-probed-views & flashcards audit
    ├── 📄 remediation-plan-session8.md ← session-8 deep-chrome/layout audit (16 gap families)
    ├── 📄 remediation-plan-session9.md ← session-9 data-state/chart-axes/icon audit (19 gap families)
    ├── 📄 remediation-plan-session10.md ← session-10 dark-mode consistency audit (the @theme inline token trap + 6 tint families)
    ├── 📄 remediation-plan-session11.md ← session-11 theme-system audit (pre-paint boot script + System tracking + accent-dark re-themes)
    ├── 📄 remediation-plan-session12.md ← session-12 accessibility audit (forced-colors state restoration + print + skip link + reduced-motion)
    ├── 📄 remediation-plan-session13.md ← session-13 resilience audit (AI failure states + upload edge cases; data-volume stress verified green)
    ├── 📄 remediation-plan-session14.md ← session-14 hardening audit (connectivity UX + settings guards + security headers/limits + AI live regions)
    ├── 📄 remediation-plan-session15.md ← session-15 auth-flow audit (inline errors + signup/verify/forgot/reset + calculator keyboard superset)
    ├── 📄 remediation-plan-session16.md ← session-16 CWV/mobile-geometry audit (load-stability pre-warm + responsive auth sub-screens + fresh copy sweep)
    ├── 📂 screenshots/          ← dev-server captures of every view (light + dark)
    └── 📄 DEPLOYMENT.md         ← production notes

Quick Start

Requires Bun ≥ 1.3 (or npm with Node ≥ 22 — swap bun for npx/npm run).

git clone https://github.com/nordeim/omni-study.git
cd omni-study
bun install

# Database — .env ships with DATABASE_URL="file:../db/custom.db";
# db/ lives at the repo root (git-ignored, recreated by these two commands):
bun run db:push
bun run db:seed

bun run dev

Verify setup:

  1. curl http://localhost:3000/api/health → {"status":"ok","db":"up","app":"studyflow"}
  2. Open → redirects to /login
  3. Sign in with the demo account — [email protected] / Demo1234! — you land on the dashboard with seeded subjects, tasks, exams, notes, decks and grades.

Environment Variables

Copy .env.example to .env (the defaults above already work locally):

Variable Required Purpose
DATABASE_URL yes "file:../db/custom.db" — resolved by src/lib/db-path.ts to /db/custom.db at runtime; the db:* scripts set it inline for the Prisma CLI (schema-anchored). Keep the value in sync with prisma/schema.prisma’s header note.
AUTH_SECRET prod HMAC key for session cookies. Generate with openssl rand -hex 32; a dev-only fallback is used when unset — and enforced at boot in production (the server exits with the remedy message instead of silently signing forgeable sessions).
NEXT_PUBLIC_SITE_URL no Canonical origin for metadata/sitemap. Defaults to http://localhost:3000.

Testing

bun run test        # Vitest — 834 unit tests (router, theme, date, calculator incl. mapPhysicalKey, auth incl. the AI rate-limit budget + the verification/reset seams, validation incl. verify/forgot/reset schemas + the S24 change-password rotation seam + the S25 deletion seam + the S26 email-rotation seam, db-path, site-url, theme-cache + boot-script contract + content-disposition + api-timeout + the offline transport-error mapping, rum-diagnostics — the CWV thresholds/panel display seam + the S20 sparkline geometry + the CSV builder, data-export — the S21 envelope/manifest/secret-guard seam, env-check — the S22 AUTH_SECRET boot-guard seam, data-import — the S23 envelope-validation/FK-pre-emption/folders-topo-sort seam, dep-audit — the S30 bun-audit parser/classifier seam, dev-preflight — the S31 dev-server boot-stability classifier seam, suite-verdict — the S32 standing-suite verdict seam [the anti-phantom shape-error guard + the three documented non-gap signatures] + the S33 mobile-sweep classifier [the light-mode 390×844 drawer-navigation sweep] + the S34 midband-sweep classifier [the light-mode 768×1024 drawer-navigation sweep — the md breakpoint, the 391–1023px band's representative width, with the chrome vehicle-contract class] + the S35 landscape-sweep classifier [the light-mode 844×390 phone-landscape drawer-navigation sweep — the HEIGHT axis the standing surface never varied, with the per-view heading-clearance key + the drawerNavScrollable short-viewport contract class] + the S36 dark-mobile-sweep classifier [the dark-mode 390×844 drawer-navigation sweep — the MODE axis at the mobile viewport the standing surface never varied, with the per-view flashbulbs/unreadable detector keys + the drawer dark record + the INDEPENDENT dark-detection counting] + the S37 accent-light-sweep classifier [the accent × light 390×844 sweep — the ACCENT axis in LIGHT mode the standing surface never varied, with the ALL-ACCENTS guard + the per-accent applied self-verification + the three signature-strict non-gap exemptions (the CTA-face, the calendar adjacent-month slate-300 days re-verified live on the reference, the yellow-500 priority-pill vocabulary)] + the S38 accent-dark-sweep classifier rebuild [the accent × dark MOBILE 20-view matrix — the pre-S38 mobile pass swept only 6 views; the ALL-ACCENTS + ALL-VIEWS + drawer-present anti-phantom guards, every view recorded explicitly on both surfaces, the per-accent applied self-verification, the mode/mobileViewport contracts, and the mobile scrollWidth overflow class] + the S39 accent-midband-sweep classifier [the accent × mid-band 768×1024 drawer-navigation sweep — the mid-band × accent cell never probed + the navigation-interaction gap: the accent sweeps navigate by direct goto, so the drawer's active-link tint was only ever read on the Dashboard link; with the ALL-ACCENTS + ALL-VIEWS + drawer-present guards, the vehicle-contract class, and the TWO interaction finding classes (the tint rot — activeTint "none", the S8 no-var class; the active-state mismatch — activeLabel ≠ the view)] + the S40 accent-landscape-sweep classifier [the accent × landscape 844×390 drawer-navigation sweep — the landscape × accent cell never probed (landscape-sweep is light+violet only) and the S39 interaction proof extended to the SHORT viewport, with the S35 short-viewport contracts (headingClearance + drawerNavScrollable) folded into the S39 accent shape] + the S41 accent-desktop-sweep classifier [the desktop light-accent sidebar-navigation sweep at 1280×800 — the documented S37 bracketed non-gap: accent-dark covers 1280 dark only, and the SIDEBAR-vehicle active-link tint was verified at ZERO views, ZERO accents, in either mode; with the INVERTED vehicle contract (sidebarVisible true + hamburgerVisible false), the sidebar-own scroll contract (sidebarNavScrollable at 800 height), the per-view headingOffset key, and the interaction finding classes on the DESKTOP vehicle]) + the S42 accent-volume-sweep classifier [the data-volume × dark × accent stress — the S13 volume profile (300/200/250) swept through the theme space's dark-accent half: the volume counts EXACT at the seam (a seed-volume envelope is its own rot class), the ALL-ACCENTS + ALL-VIEWS guards (the 7 volume-affected views — the S13 set), the per-view S13 measurement family (nodes/mainNodes/longTaskTotal ≤ 1500ms/clickLatencyMs ≤ 1500ms), the S38 dark detector family (flashbulbs always findings; the CTA-face exemption ONLY — the light-mode calendar-day + yellow-pill exemptions do NOT apply in dark), the interaction evidence in DARK (the never-read sidebar-tint cell), and the /api/tasks ≤ 500KB B1 bound]
bun run build       # REQUIRED before e2e — the suite boots the standalone server
bun run test:e2e    # Playwright — 299 specs against the production build on :3100

E2E notes: the suite pushes and seeds an isolated db/e2e.db (never touches custom.db), signs the demo user in once via a setup project (the login endpoints are rate-limited — 10 attempts/IP/15 min), and asserts the reference-measured design pins (sidebar geometry, v3 shadow-sm, canvas gradient, view-title model, stat-card text metrics, empty-state design, brand-gradient stops, login chrome, the session-5 interactive-chrome pins, the session-6 populated-row pins, the session-7 flashcards/timetable/analytics/dialog pins, the session-8 deep-chrome/layout pins, the session-9 data-state/chart-axes pins, the session-10 dark-mode consistency pins — the shadcn token utilities, banner/amber/clock/today/chart dark re-themes, the dark mobile drawer, and a light-mode byte-parity regression guard — the session-11 theme-system pins — pre-paint dark application, the dark-OS login, runtime System tracking, the theme-color meta, the accent-surface dark re-themes, print emulation, and a light regression guard — the session-12 accessibility pins — forced-colors state outlines + the bold active nav + the slider gradient, the skip link’s Tab order/focus/Enter landing, the beforeprint/afterprint light-forcing, print-color-adjust: exact on the essential surfaces, the timetable print width reset, reduced-motion collapse, and a light regression guard — the session-13 resilience pins — the AI-chat failure rollback + recovery, the solver’s client-side MIME guard with zero outbound requests, the nameless-upload rejection, and the RFC 5987 download header — and the session-14 hardening pins — the offline banner’s appear/disappear + online absence (byte-parity guard), the human offline toast, the whitespace display-name 400 + trim-on-save, the AI-route per-user rate limit (20 then 429 with Retry-After), the download nosniff + private no-store headers, and the AI transcript/solver aria-live regions — and the session-15 auth-flow pins — the inline login-error alert with its measured chrome, the signup form’s reference shape, the register→verify→signed-in journey via the surfaced code, the unverified-login gate, the forgot→check-email→reset-link→new-password journey, and the calculator’s physical-keyboard superset including the form-field guard — and the session-22 boot-guard pins — the standalone production server spawned WITHOUT AUTH_SECRET exits non-zero with the actionable message (the openssl rand -hex 32 remedy), and the positive control: the suite’s own properly-configured boot answering /api/health — and the session-24 change-password pins — the anon 401 gate, the wrong-current/same-password/short-new 400 family (pre-write: the demo hash untouched), and the fresh-user rotation round-trip: register → sign in → the panel rotates → the old password login 401 → the new password login 200 → the session survives — and the session-25 account-deletion pins — the anon 401 gate, the wrong-password/wrong-confirmation/short-password 400 family (pre-write: the demo account untouched, still signing in), and the fresh-user terminal journey: register → sign in through the UI → the panel reveals the Danger zone form → the wrong-word early inline error → the deletion → the redirect to /login → the old-credentials login 401 → the cleared session’s me 401 — and the session-26 email-change pins — the anon 401 gate, the wrong-password/mismatched-confirm/short-password/same-email 400 family + the duplicate-email 409 (pre-write: the demo account untouched, still signing in), and the fresh-user identity rotation: register → sign in through the UI → the panel’s Email address card → the mismatched-confirm early inline error → the change → the success note + the identity block showing the NEW email (the live store update, both surfaces) → the session survives me 200 with the new email → the old-email login 401 → the new-email login 200). Full gate order: lint → typecheck → test → build → test:e2e.

API Reference

All endpoints are same-origin JSON; sessions ride the sf_session cookie. 🔒 = requires authentication.

Endpoint Methods Description
/api/health GET Liveness + DB probe (used by the e2e webServer gate + the Docker healthcheck)
/api/rum POST/GET 🔒 The RUM hook (session-18): POST a batched web-vitals report (upsert on sessionId+metric — final value wins, 1000/15-min per-user budget); GET answers p75 per metric + recent samples + the per-metric trends series (session-20) — the data behind the /rum diagnostics panel (session-19)
/api/rum/export GET 🔒 The CSV export (session-20): the user’s most recent 2000 RUM events as a spreadsheet-ready text/csv download (RFC-4180 escaping, raw values, the attachment + nosniff + private/no-store header contract)
/api/export/data GET 🔒 The full-data export (session-21): the user’s complete content across the 20 collections as a versioned JSON envelope download (chronological, ids/FKs intact, the hashless profile, the attachment + nosniff + private/no-store header contract) — the data behind the /export page
/api/import/data POST 🔒 The data import / restore (session-23): POST a studyflow-data-export envelope to restore it into the importing account — upsert-by-id in ONE all-or-nothing transaction (never deletes; content only, never identity; the importer’s userId re-attached to every row), the seam’s envelope validation (dangling optional FKs nulled, required cards.deckId rejected, folders sorted parents-first with cycle rejection), a 10 MiB body cap (413), a 10/15-min per-user rate limit (429), and the per-collection created/updated report as the response
/api/auth/login / register / logout / me POST/GET Rate-limited auth; no user enumeration. register creates an UNVERIFIED account + a 6-digit code (no auto-login — S15/ADR-013); login gates unverified accounts behind the reference’s inline copy
/api/auth/verify-email / resend-verification POST The OTP flow: match the latest unexpired 15-min code → mark verified + sign in; resend re-issues for unverified accounts (uniform 200 otherwise)
/api/auth/forgot-password / reset-password POST The 3-state reset flow: always-200 request (reset URL in the response — the no-SMTP delivery), 30-min one-shot token consumption
/api/auth/change-password POST 🔒 The account-security rotation (session-24/ADR-022): { currentPassword, newPassword } — the current password verified against the stored scrypt hash FIRST (a wrong current → 400, pre-write), the register family’s min-8/max-200 policy on both fields, the must-differ rule (the schema’s refine), a 10/15-min per-user rate limit (429 + Retry-After); the stateless session SURVIVES the owner’s own rotation (documented — revoking other sessions is ADR-022’s rejected alternative)
/api/auth/delete-account POST 🔒 The ownership exit (session-25/ADR-023): { password, confirmation: "DELETE" } — the password verified against the stored scrypt hash FIRST (a wrong password → 400, pre-write), the typed-confirmation rule (the schema’s refine — case-exact), a 10/15-min per-user rate limit (429 + Retry-After), then the ONE cascade write: user.delete wipes every collection (all 22 user relations carry onDelete: Cascade — subjects, tasks, notes, decks, grades, files, chat, RUM events, tokens); the session cookie is cleared with the account (maxAge 0 — the logout mechanism)
/api/auth/change-email POST 🔒 The account-identity rotation (session-26/ADR-024): { password, newEmail, confirmEmail } — the password verified against the stored scrypt hash FIRST (a wrong password → 400, pre-write), the confirm-match rule (the schema’s refine), then the route-side guards: must-differ (posting the current address → 400) + uniqueness (an address owned by ANY account, verified or not → 409), a 10/15-min per-user rate limit (429 + Retry-After), then the ONE write: user.update moves the login identifier (lowercased; emailVerified stays true — the password proof IS the verification); the session SURVIVES (stateless HMAC over the user id — the S24 contract)
/api/tasks (+ /[id]) GET POST PATCH DELETE 🔒 Tasks — lists, importance, due dates
/api/task-lists, /api/subjects, /api/assignments, /api/exams, /api/events, /api/timetable, /api/notebooks, /api/notes, /api/flashcards/decks, /api/flashcards/cards, /api/practice-tests, /api/study-groups, /api/grades, /api/focus-sessions, /api/holidays GET POST PATCH DELETE 🔒 Ownership-scoped CRUD via the delegate factory
/api/files (+ /[id], /folders, /link) GET POST DELETE 🔒 Multipart upload ≤ 2 MiB (nameless files rejected), link-files, download with RFC 5987 filenames
/api/settings/preferences PATCH 🔒 Theme mode, accent, avatar, display name + the S17 study-profile fields (school, grade level, 1–12h study goal, notifications master)
/api/calculator/history GET POST DELETE 🔒 Persisted calculations
/api/ai/chat, /api/ai/messages POST GET DELETE 🔒 Study assistant (server-side SDK only; 20 req/user/15 min)
/api/ai/generate-cards POST 🔒 AI flashcard generation for a deck
/api/ai/generate-questions POST 🔒 AI practice-test question generation
/api/math/solve POST 🔒 Text or photographed problems (vision; 20 req/user/15 min)

Design System

Measured from the live reference (see docs/Tailwind-V4-Validation-Report.md for the full trap log):

Token Value Usage
Primary #8b5cf6 (violet-500) via --sf-primary RGB triplet Active states, accents
Primary CTAs linear-gradient(to right, violet-500, indigo-600) + v3 shadow — accent-aware via .sf-gradient (session-5 pin) Every primary action button
Task rows standalone rounded-xl cards, slate-200 border, 24px round priority-colored checkbox, slate-700 titles, due/repeat pills, hover-revealed actions (session-6 pin) Tasks + My Day
Dashboard rows bare p-4 gap-4 rows, 20px round checkbox, title-only (session-6 pin) Today’s Tasks
Assignment rows 24px round checkbox, blue due-in pill, priority/type pills, gradient progress slider + violet % label (session-6 pin) Assignments
Exam cards 3-col grid, h-2 subject color strip, amber urgency badge, icon detail rows, type footer (session-6 pin) Exams
Calendar cells Sunday-first aspect-square centered cells (dynamic week count — 35 for five-week months), 4-color dots, SELECTED = solid accent fill + TODAY = 100-level tint (both hover-stripped), simple p-6 month card, legend + border-l-4 day-detail rows (session-6/S8-C/S9-E pins) Calendar
Flashcards two-panel layout: w-80 border-r deck sidebar with colored 40px icon blocks, deck color swatches, lg:grid-cols-3 card grid with difficulty pills, 3D-flip study mode (session-7 pin) Flashcards
Timetable grid grid-cols-8 week table, full day-name heads + text-lg dates, 60px hour rows with “7 AM” labels, mobile day accordion (session-7 pin) Timetable
Analytics stats r12 border-0 cards, 48px tinted icon blocks (violet/blue/green/orange-100), fraction values (session-7 pin) Analytics
View titles h1 text-2xl font-bold text-slate-800 + 24px accent icon (MyDay/FocusTimer are 30px) Every view header (session-4 pin)
Form controls gray-200 borders, gray-950 text, gray-400 placeholders, h-9 transparent inputs, v3 shadow-sm (session-5 pin) Dialogs, selects, tabs, outline buttons
Dialogs max-w-md (448px), rounded-lg (8px), 36px inputs (session-5 pin) Every modal
Empty states 80px rounded-2xl gradient block (violet-100 → indigo-100), 40px accent icon, 20px/600 h3, 16px hint Every empty view (session-4 pin)
Events panel slate-900 rounded-2xl shadow-2xl terminal panel, cyan New Event link, CW calendar-week label Events view (session-5 pin)
Strong #7c3aed (violet-600) Links (“View All”), active nav text
Card white, 1px solid #f1f5f9, radius 16px Every surface
Shadow 0 1px 2px 0 rgb(0 0 0 / 0.05) v3 shadow-sm, pinned in @theme
Canvas linear-gradient(to right bottom, #f8fafc, #fff, #f5f3ff4d) App background
Sidebar 260px fixed, rgba(255,255,255,0.8) + blur(20px) + white/50 hairline (.glass) Hidden below lg
Mobile app bar 64px fixed .glass, brand + live clock (“03:43 AM”) Content starts at 80px (both apps)
Mobile drawer 288px white panel, shadow-2xl, 20% blurred backdrop, icon nav items No footer (reference-exact)
Clock chip violet-50 → indigo-50 gradient (rgb(245,243,255) → rgb(238,242,255)), violet-700 time Accent-aware via --sf-primary-softest(-adjacent)
Brand gradients chips + CTA end indigo-600 (rgb(79,70,229)); avatar runs violet-400 → indigo-500 (the lighter pair) Accent-aware via --sf-primary-gradient-to / -avatar-* tokens
Accents violet · blue · emerald · amber · pink · red · teal (session-5 migration — the reference’s swatch faces) Settings → Appearance
Font system sans stack Matches reference body styles

Six accent themes × Light/Dark/System × 24 emoji avatars, all persisted to the user record.

Deployment

The app builds to a standalone server (output: "standalone" in next.config.ts):

bun run build
bun run start        # NODE_ENV=production, serves .next/standalone

For production use an absolute DATABASE_URL (see docs/DEPLOYMENT.md §4) and set AUTH_SECRET. Deploy keys never live in the repo — pushes use the docs/ssh_git_wrapper_v3.py wrapper (see docs/how-to-git-push-using-ssh-wrapper_SKILL.md).

Troubleshooting

Symptom Cause Fix
{"status":"degraded","db":"down"} on /api/health No database file yet bun run db:push && bun run db:seed
Prisma CLI created the db one directory too high .env-loaded relative URLs anchor at the CWD Use the bun run db:* scripts (they set DATABASE_URL inline, schema-anchored) — never raw prisma db push with the repo-root .env
E2E webServer timeout bun run build not run since the last change Rebuild, then bun run test:e2e
Dev server dies silently in low-memory containers OOM-killed (next-server ~1.6 GB RSS) Close extra headless browsers; rerun bun run dev
Login fails after many attempts Rate limiter (10/IP/15 min) Wait or restart dev; specs share ONE login by design
space-y-* gaps look wrong next to mt-* children Tailwind v4 selector rewrite (trap 4) Use flex gap-*; the codebase convention bans margin utilities inside space-*
The standalone production server exits at boot with AUTH_SECRET is required in production The session-22 boot guard — the server refuses to sign session cookies with the public dev fallback That is the guard working: generate a secret (openssl rand -hex 32), set AUTH_SECRET in the environment, restart (DEPLOYMENT.md §4)

Contributing

The verification gate before any push: bun run lint && bun run typecheck && bun run test && bun run build && bun run test:e2e. Conventions that differ from defaults: Tailwind v4 CSS-first (no tailwind.config.js — theme lives in src/app/globals.css @theme); gap-* layout only (never space-y/x with margin-carrying children); Zustand stores over Context; every API payload through its Zod schema in src/lib/validation.ts; z-ai-web-dev-sdk imported only inside route handlers.

Project Status

Phase Status Deliverables
Reference recon (all 20 views, computed-style tokens) ✅ Complete Traps + measured tokens in docs/Tailwind-V4-Validation-Report.md
Codebase build (20 views, 24 models, 40+ endpoints) ✅ Complete src/**, prisma/**
Test suites (93 unit + 91 e2e) ✅ Complete tests/**
Visual parity verification ✅ Complete docs/screenshots/**, docs/remediation-plan*.md
Session-2 parity remediation (radius trap, icons, avatar, clock) ✅ Complete docs/remediation-plan.md
Session-3 mobile-chrome & token remediation (fixed glass app bar + live clock, drawer mirror, greeting map, accent token completeness) ✅ Complete docs/remediation-plan-session3.md
Session-4 text-metrics & second-order remediation (v4 blur trap, stat-card metrics, reference empty states, view-title h1+icon model, login card, measured gradient stops, heading hierarchy) ✅ Complete docs/remediation-plan-session4.md
Session-5 interactive-chrome & view-body remediation (gradient CTAs, Events dark panel + repeat/reminders, FocusTimer/Settings/Calculator/Timetable/Notes/GradeTracker/Files/MyDay/AI reworks, gray form-control palette, dialog chrome, emerald/amber accents) ✅ Complete docs/remediation-plan-session5.md
Session-6 populated-state remediation (task card rows + priority/repeat/myDay/subtasks, MyDay amber progress card + Suggestions, dashboard bare rows, assignment sliders, exam card grid, calendar grid/legend/day-detail — measured against live reference data) ✅ Complete docs/remediation-plan-session6.md
Session-7 lightly-probed-views remediation (Flashcards two-panel view + deck colors + card grid + 3D study mode + AI card generation, Study Groups/Practice Tests measured dialogs + AI question generation, Timetable grid-cols-8 week grid + mobile accordion + week-alignment bug fix, Analytics icon-block stat cards + 7-day charts, Grade Tracker icons, Notes title input) ✅ Complete docs/remediation-plan-session7.md
Session-8 deep-chrome/layout remediation (nav icon set + active-gradient stop + tagline + 36px collapse button, dashboard flush divide-y rows, Calendar selected/today semantics + simple month card, MyDay header block + bare suggestions, Timetable today-header tint, Tasks two-pane, Notes/Study Groups two-pane reworks, Analytics per-card icons + donut + workload, Calculator/MathSolver/AI/FocusTimer/Settings icon-and-chrome fixes, Assignments/Exams search icons + slim exam cards, dead-utility sweep) ✅ Complete docs/remediation-plan-session8.md
Session-9 data-state/chart-axes remediation (dashboard overdue banner + slim section empty states, MyDay amber greeting empty state + bare quick-add row, Tasks outline filter button + empty My Lists, Calendar Sunday-first grid + icon/tab mode switch, Files house breadcrumb + grid3x3 toggle, text-only Grid Builder, Assignments Active-default slim rows, Exams orange Tomorrow badge, Analytics area charts with axes/gridlines, FocusTimer/Settings/Notes icon fixes, Flashcards deck-row ellipsis menu, AI wand-sparkles composer) ✅ Complete docs/remediation-plan-session9.md
Session-10 dark-mode consistency remediation (the @theme inline literal-token trap — shadcn base tokens now route through hsl(var(--x)) runtime indirection so every bg-card/bg-popover/bg-muted/border-input utility re-themes in dark; dark washes for the overdue banner, MyDay amber card, timetable/calendar today tints, sidebar clock chip, settings selected cards; dark SVG chart axes/fills; the reference’s Dark option measured as a platform no-op → the clone’s full dark mode is the documented superset) ✅ Complete docs/remediation-plan-session10.md
Session-11 theme-system remediation (the theme-application lifecycle root cause — a pre-paint boot script + localStorage cache kills the dark-mode FOUC and themes the login route; System mode tracks OS changes at runtime; theme-color meta sync; the accent-dark re-themes for the active nav, ViewAll links, timetable mobile today chip, avatar swatch; minimal print styles) ✅ Complete docs/remediation-plan-session11.md
Session-12 accessibility remediation (forced-colors state restoration with system-color outlines, print that forces light + keeps essential backgrounds, the skip-to-content link, reduced-motion support) ✅ Complete docs/remediation-plan-session12.md
Session-13 resilience remediation (AI failure states — the optimistic-message rollback + the 120 s deadlines; upload edge cases — nameless-file rejection + RFC 5987 download filenames; data-volume stress verified green at 300+ rows) ✅ Complete docs/remediation-plan-session13.md
Session-14 hardening remediation (connectivity — the global offline banner + the human transport-failure message; security — per-user AI rate limits + download nosniff/no-store headers; settings — the whitespace display-name guard; AI a11y — transcript + solver live regions; 14 stale pre-clone scripts removed) ✅ Complete docs/remediation-plan-session14.md
Session-15 auth-flow remediation (inline login errors — the reference’s red/green alert pattern; the full account journey — Create-your-account form, 6-digit email verification with an unverified-login gate, 3-state forgot-password + reset deep-links; the calculator’s physical-keyboard superset with a guarded listener; Files search + pre-1.0 sweep verified as non-gaps) ✅ Complete docs/remediation-plan-session15.md
Session-17 fresh-user remediation (the reference’s zero-data stat cards on Analytics/GradeTracker, the Settings depth — Profile school/grade/study-goal/notifications fields + per-tab headers + zero-empty states, the seven-view empty-state hint copy family, the mobile StudyGroups copy, zero-subject registrations) ✅ Complete docs/remediation-plan-session17.md
Session-18 RUM + Docker (the web-vitals beacon → POST/GET /api/rum with p75 aggregates, the Dockerfile + compose one-command deployment with the validated runtime layout) ✅ Complete docs/remediation-plan-session18.md
Session-19 RUM diagnostics panel (the auth-gated /rum surface — the five CWV at p75 with public-bound rating badges, the recent-samples table, the pure display seam, zero nav linkage) ✅ Complete docs/remediation-plan-session19.md
Session-20 RUM panel v3 (the per-card sparkline trends + the CSV export route — the additive trends aggregate field, the RFC-4180 seam, the S13/S14 download-header contract) ✅ Complete docs/remediation-plan-session20.md
Session-21 data portability (the full-data export — the versioned JSON envelope via GET /api/export/data, the 20-collection manifest + the row normalizer + the secret guard, the server-gated /export count-grid page, zero nav linkage) ✅ Complete docs/remediation-plan-session21.md
Session-22 AUTH_SECRET boot guard (the production fail-fast — the pure env-check seam, the instrumentation.ts boot hook with the validated process.exit mechanism, the single-source DEV_FALLBACK_SECRET refactor, the 64-hex e2e secret) ✅ Complete docs/remediation-plan-session22.md
Session-23 data import (the restore — the pure data-import seam with the FK pre-emption + the folders topological sort, the POST /api/import/data upsert-by-id all-or-nothing transaction, the /export page’s Restore card, the 10 MiB cap + rate limit) ✅ Complete docs/remediation-plan-session23.md
Session-24 change-password flow (the account-security rotation — the changePasswordSchema seam with the must-differ refine, the POST /api/auth/change-password current-password-proof route, the Settings Profile tab’s Change password card, the stateless session-survives contract) ✅ Complete docs/remediation-plan-session24.md
Session-25 account-deletion flow (the ownership exit — the deleteAccountSchema seam with the typed-DELETE refine, the POST /api/auth/delete-account password-proof cascade route, the Settings Profile tab’s Danger zone card with the two-step reveal, the session-dies-with-the-account contract) ✅ Complete docs/remediation-plan-session25.md
Session-26 email-change flow (the account-identity rotation — the changeEmailSchema seam with the confirm-match refine, the POST /api/auth/change-email password-proof route with the must-differ/uniqueness guards, the Settings Profile tab’s Email address card with the live identity-block update through the theme store, the session-survives contract) ✅ Complete docs/remediation-plan-session26.md
Session-27 dark-sweep audit-tool repair (the tooling-integrity fix — the standing dark-mode audit had silently rotted after the S11 theme lifecycle: the forced-class + stale sf-theme-mode key measured LIGHT mode and reported 97 phantom flashbulbs; now sets dark through the settings API, self-verifies the precondition with a loud non-zero exit, rides the applied mode in the JSON envelope, and restores light — the self-verifying-precondition doctrine for every probe) ✅ Complete docs/remediation-plan-session27.md
Session-28 connectivity-audit repair (the tooling-integrity fix — the A4 login-offline probe had silently rotted after the S15 auth-flow change: it still asserted the S14-era toast design while S15 replaced the login card’s errors with the reference’s measured INLINE role=alert, so the audit emitted a phantom “no/silent feedback” finding whose own measured evidence disproved it; now asserts the designed channel — the S15 inline alert scoped to the auth card, the user stays on the card, the button recovers — with the loud genuine-failure criterion kept; validated with the reproduction probe + the negative control; the standing 31 refreshed) ✅ Complete docs/remediation-plan-session28.md
Session-29 documentation-drift repair (the doc-alignment fix — every standing audit re-ran GREEN with zero code findings and the reference re-swept UNCHANGED, so the sweep’s genuine defect family was the prose layer: the doc claims had drifted under additive change — the schema’s 24 models were documented as “22” (written at S18, forgetting the S15 token pair) in CLAUDE/PAD/README and “21” (the pre-S15 list) in SKILL; PAD’s “registration seeds three starter subjects” contradicted ADR-015’s zero-subjects, its “ships no Dockerfile” contradicted ADR-016’s shipped Dockerfile + compose, its “no reduced-motion yet” contradicted ADR-010’s resolved block, and its tree listed a src/hooks/ that never existed; SKILL §11 carried thirteen-session-stale test counts (106/194 vs 549) and §5 named the pre-S4 backdrop-blur-sm where the code ships backdrop-blur-xs; all layers re-aligned against the executable truth, with the “all 22 user relations” claims verified correct and left untouched; AP-74 records the lesson) ✅ Complete docs/remediation-plan-session29.md
Session-30 dependency-security audit (the never-audited surface — pre15-preflight measures outdated-count only; its “no action” verdict stood while bun audit carried 2 HIGH advisories: braces <=3.0.3 [dev-only via eslint-config-next; 3.0.3 IS the latest npm version — no upstream patch exists] and deepmerge-ts <8.0.0 [CLI-only via @prisma/config’s exact 7.1.5 pin; never in the standalone trace] — both verified NON-PRODUCTION against the 13-root .next/standalone/node_modules trace. The repair: the standing bun scripts/dep-audit.mjs probe — parses bun’s text report, classifies every advisory by RUNTIME REACHABILITY against the standalone trace, FAILS loudly (exit 1) on anything runtime-reachable, documents the dev/CLI family with evidence, and self-verifies its preconditions [trace exists + newer than bun.lock, else exit 1 with no findings]; the pure seam src/lib/dep-audit.ts + 15 unit pins; validated with the negative controls [a fabricated runtime advisory exits 1; the stale-trace precondition exits 1 with zero findings]) ✅ Complete docs/remediation-plan-session30.md
Session-31 dev-server boot-stability preflight (the environment precondition no probe verified — a stale Turbopack .next/dev cache put /login into a sustained full-page reload loop [17+ main-frame navigations per 15 s; React never hydrated] that SURVIVED a clean daemon restart while /api/health stayed 200 — the audits then failed OPAQUELY, a 30 s click timeout naming the disabled login button instead of the cache; the S9 lesson’s “restart the daemon” remedy proved INSUFFICIENT — only rm -rf .next/dev clears it. The repair: the standing bun scripts/dev-server-preflight.mjs preflight — loads /login, counts same-path navigations over an 8 s post-load window, fills the demo credentials and waits for the submit button to enable [NEVER submits — zero login requests], classifies through the pure src/lib/dev-preflight.ts seam [green / reload-loop / no-hydration / unreachable, each with its honest class-specific remedy], exits 1 loudly on every failure class; 11 unit pins + the negative controls [dead port → unreachable; a scratch self-reloading login-shaped page → reload-loop with the cache-clear remedy; healthy server → GREEN]) ✅ Complete docs/remediation-plan-session31.md
Session-32 standing-suite orchestrator (the operating procedure that existed only in prose — bun scripts/standing-suite.mjs: the S31 preflight enforced first, each standing audit spawned EXACTLY ONCE with its stdout classified through the unit-pinned suite-verdict seam; the anti-phantom guard [a missing key on a known audit is a LOUD shape-error, never a silent 0] — five of sixteen envelopes had been mis-parsed by careful operators, and the S29/S31 double-run phantom recurred a third time; the genuine defect the strict reading surfaced: the ai-a11y-audit’s D2 probe leaked a probe pair into the demo chat DB on EVERY run [unroute-before-release order — fixed to reload-first per the S14 lesson + a closing Prisma cleanup; 4 stale pairs purged, dark/accent sweeps re-green], plus the :3200 standalone auto-start with ephemeral AUTH_SECRET and the demo-theme closing guard) ✅ Complete docs/remediation-plan-session32.md
Session-33 mobile light-mode sweep (the manual 390×844 walkthrough every session ran as prose, encoded as a standing probe — node scripts/mobile-sweep.mjs, the suite’s 16th audit stage: all 20 views navigated THROUGH THE DRAWER with per-view identity-heading [h1-or-h2, the S8-H mobile-stack pattern], no-horizontal-overflow [scrollWidth ≤ 390 — the S16 mobile-only-gap class], drawer-closes-on-navigation, fixed glass app bar, zero console errors; self-verifying preconditions [viewport asserted, light mode through the settings API]; the genuine defect the first run surfaced: the clone’s Notes mobile stack had LOST the identity heading the reference renders [its own two-pane overflows to 416px at 390 — the clone’s no-overflow stack is the superset, but the heading was missing; fixed with the StudyGroups S8-H h2 pattern, desktop bytes untouched]; the reference’s own mobile overflow documented on 5 views [Notes 416, Timetable 395, Flashcards 391, Calculator 429, MathSolver 417] where the clone stays 390) ✅ Complete docs/remediation-plan-session33.md
Session-34 mid-band light-mode sweep (the 391–1023px viewport continuum between the pinned widths — the whole band the standing surface never probed: mobile pins 390, desktop pins 1280/1440, the lg pin covers only the sidebar appearance at 1024 — encoded as a standing probe: node scripts/midband-sweep.mjs, the suite’s 17th audit stage, at 768×1024 the md breakpoint [the switchiest width in the band: the Timetable swaps its mobile accordion for the min-w-[900px] scroll canvas, grids go multi-column, headers go flex-row] and iPad portrait; all 20 views navigated THROUGH THE DRAWER [the navigation vehicle through the whole band — the sidebar is lg:flex] with the S33 assertion family + the chrome vehicle-contract class [sidebar hidden + hamburger visible at 768 — a broken lg contract reads non-green at the seam]; this session’s exploration validated the whole band clean [640/767/768/1023 × 20 views, findings 0]; AP-79 — a viewport band between pinned widths is a blind band) ✅ Complete docs/remediation-plan-session34.md
Session-35 phone-landscape short-viewport sweep (the HEIGHT axis the standing surface never varied — every pinned viewport was portrait-tall [390×844, 768×1024, 1280×800, 1440×900, 1024×800 — heights 800–1024]; the phone-landscape class [844×390, a real and common device class] had zero coverage, and its height-dependent structures — the drawer nav whose scroll is the ONLY path to links 8–20 at 390 height [measured: scrollHeight 1068 vs clientHeight 301], the fixed h-16 app bar at 16% of the viewport, the pt-* heading clearance under it, the h-[calc(100vh-8rem)] panes at 262px — were guaranteed by nothing — encoded as a standing probe: node scripts/landscape-sweep.mjs, the suite’s 18th audit stage, at 844×390 the iPhone-class landscape; all 20 views navigated THROUGH THE DRAWER with the S33/S34 assertion family + the per-view heading-clearance pin + the short-viewport contract [drawerNavScrollable MUST be true — the nav geometry class this audit owns]; this session’s exploration validated the whole landscape band clean [740×360, 844×390, 932×430 × 20 views + the 320×568 edge, findings 0]; AP-80 — an axis never varied is an axis never verified) ✅ Complete docs/remediation-plan-session35.md
Session-36 dark-mode mobile sweep (the MODE axis at the mobile viewport the standing surface never varied — dark × mobile was covered for 6 of 20 views [accent-dark-sweep’s mobile pass], 14 views had NEVER been rendered in dark at the stacked mobile layout by any standing audit, and every light mobile sweep is mode-rot-guarded by construction — encoded as a standing probe: node scripts/dark-mobile-sweep.mjs, the suite’s 19th audit stage, the S33 mobile sweep’s dark sibling at 390×844; all 20 views navigated THROUGH THE DRAWER in dark mode [PATCHed through the settings API — the real lifecycle, self-verified] with the S33/S35 structural family + the dark-sweep detector family [flashbulbs/unreadable] extended to the mobile chrome + the drawer’s own dark rendering [the dark:bg-slate-950 panel]; the dark detections counted INDEPENDENTLY of the findings array at the seam; this session’s exploration validated the whole dark-mobile family clean [dark × 390×844, dark × 768×1024, dark × 844×390, findings 0 everywhere]; AP-81 — a mode never varied at a viewport is a blind combo) ✅ Complete docs/remediation-plan-session36.md
Session-37 accent × light sweep (the ACCENT axis in LIGHT mode the standing surface never varied — the accent-dark-sweep varies the accent ONLY in dark [its own name is the blind spot], every light pin rides the default violet, and the only light × non-violet rendering in any standing artifact was ONE desktop calculator e2e view; NO standing audit had ever rendered ANY view in a non-violet accent in light mode — encoded as a standing probe: node scripts/accent-light-sweep.mjs, the suite’s 20th audit stage; for every accent [7 — the ALL-ACCENTS seam guard], PATCHed through the settings API and SELF-VERIFIED on ``: all 20 views at 390×844 with the identity heading + scrollWidth + the LIGHT contrast detector family [unreadable/lowcontrast — the flashbulb class does not apply on the light canvas] + zero console errors, plus the drawer per accent [the active-link 10%-alpha accent gradient]; three signature-strict non-gap exemptions at the seam [the CTA-face, the calendar adjacent-month slate-300 days — re-verified LIVE on the reference this session, the yellow-500 priority-pill vocabulary]; the reference’s accent picker saves but never re-themes — a SUPERSET-quality contract; this session’s exploration validated the whole accent-light family clean [390×844, 1280×800, 768×1024 × 7 accents × 20 views]; AP-82 — an accent never varied in light is a blind half of the theme space) ✅ Complete docs/remediation-plan-session37.md
Session-38 accent × dark mobile 20-view matrix (the documented S37 next-step: the accent-dark family’s own mobile pass swept only 6 of 20 views — MOBILE_VIEWS — so 14 views had NEVER been rendered in dark × accent at the stacked mobile layout by any standing audit [mobile/midband/landscape: LIGHT + violet-only; dark-mobile: violet-only; accent-light: LIGHT-only]; the accent-dark-sweep probe EXTENDED to all 20 mobile views per accent at 390×844 + the drawer per accent, with the omission-means-clean recording RETIRED — every view recorded explicitly on both surfaces, the per-accent applied self-verification, the mode/mobileViewport contracts, and the mobile scrollWidth overflow class; the seam rebuilt on the S37 doctrine with the ALL-ACCENTS + ALL-VIEWS + drawer-present guards [a missing accent/view/drawer is a LOUD shape-error, never green-by-omission]; the drawer-open failure now a FINDING, never a swallow; the exploration validated the whole family clean [7 accents × 20 views + the drawer, 0 flashbulbs, 0 non-exempt unreadable, 0 overflow]; AP-83 — an audit whose envelope cannot prove its own coverage cannot be green) ✅ Complete docs/remediation-plan-session38.md
Session-39 accent × mid-band drawer-navigation sweep (the S38 summary’s named next step — “the drawer-navigation × accent interaction at mid-band”: the mid-band 768×1024 × accent cell of the theme-space matrix was ZERO-coverage [midband-sweep is light+violet only; the accent sweeps are 390/1280 only], AND the navigation-interaction gap — the accent sweeps navigate by direct goto, so the drawer’s active-link accent tint was only ever read on the DASHBOARD link; the tint on a NON-Dashboard view, applied through the real navigation lifecycle [aria-current moves → the .sf-nav-active 10%-alpha gradient re-tints], was verified at ZERO views, ZERO accents, in either mode — encoded as a standing probe: node scripts/accent-midband-sweep.mjs, the suite’s 22nd audit stage [20-min budget]; for every accent [7 — the ALL-ACCENTS guard], PATCHed + SELF-VERIFIED, all 20 views navigated THROUGH THE DRAWER with the S34 structural family + the S37 light contrast family + the per-view interaction evidence [activeLabel MUST equal the view — the active state follows the navigation; activeTint MUST read a gradient — “none” is the S8 no-var rot class, a FINDING] + the drawer record per accent with the tint on the FINAL navigated view; the dark × mid-band combos remain the documented S36 non-gap family; AP-84 — an interaction never varied is an interaction never verified) ✅ Complete docs/remediation-plan-session39.md
Session-40 accent × landscape drawer-navigation sweep (the S39 summary’s named next step — “the landscape × accent drawer-navigation combo (the 844×390 sibling)”: the landscape 844×390 × accent cell of the theme-space matrix was ZERO-coverage [landscape-sweep: light+violet only, zero accentColor references; the accent sweeps: 390/768/1280 only] AND the S39 navigation-interaction proof [activeLabel/activeTint per view through the drawer] lived at 768×1024 only — encoded as a standing probe: node scripts/accent-landscape-sweep.mjs, the suite’s 23rd audit stage [20-min budget]; for every accent [7 — the ALL-ACCENTS guard], PATCHed + SELF-VERIFIED, all 20 views navigated THROUGH THE DRAWER with the S35 short-viewport contracts [the heading CLEARANCE under the fixed 64px bar at 390 height; the nav scrollable — re-measured per accent, the only path to links 8–20] + the S37 light contrast family + the per-view interaction evidence + the drawer record per accent with the tint on the FINAL navigated view; the dark × landscape combos remain the documented S36 non-gap family; AP-86 — a contract proven at one viewport is not proven at another: the height-dependent structures ride their own vehicle contract) ✅ Complete docs/remediation-plan-session40.md
Session-41 desktop light-accent sidebar-navigation sweep (the S40 summary’s named next step — “the desktop light-accent family (currently the documented bracketed non-gap)”): the desktop 1280×800 × light × accent cell was ZERO-coverage [accent-dark-sweep covers 1280×800 in DARK only; the light accent sweeps are 390/768/844 only] AND the SIDEBAR’s active-link accent tint (the desktop navigation vehicle) had never been read on a NON-Dashboard view through the real navigation lifecycle by ANY standing artifact — in either mode [the e2e pins read the tint on the Dashboard link only, violet only; accent-dark’s desktop pass navigates by direct goto] — encoded as a standing probe: node scripts/accent-desktop-sweep.mjs, the suite’s 24th audit stage [20-min budget]; for every accent [7 — the ALL-ACCENTS guard], PATCHed + SELF-VERIFIED, all 20 views navigated THROUGH THE SIDEBAR (the vehicle contract INVERTS: sidebarVisible true + hamburgerVisible false) with the sidebar-own scroll contract [the nav MUST be scrollable at 800 height — measured scrollHeight 1068 vs clientHeight 534, the only path to links ~13–20] + the per-view headingOffset [the content clears the fixed 260px sidebar — the lg:ml-[260px] contract] + the S37 light contrast family + the per-view interaction evidence read DIRECTLY while each view is active + the sidebar record per accent (the aside-scoped detectors + the tint on the FINAL navigated view); the desktop dark accent family belongs to accent-dark-sweep; AP-87 — a vehicle never varied is a vehicle never verified: the drawer-vehicle proofs (S39/S40) left the desktop vehicle’s interaction unverified in every mode) ✅ Complete docs/remediation-plan-session41.md
Session-42 data-volume × dark × accent stress (the S41 summary’s named next step — “the data-volume × dark × accent stress”: the cross-axis cell the standing surface never probed — the S13 data-volume audit runs LIGHT + VIOLET only [zero theme references] and every theme-space sweep [S27–S41] runs at SEED volume, so the densest renderings [300 task rows / the 250-event terminal panel / the 200-note list] had never carried a theme variation, AND the SIDEBAR’s active-link tint had never been read through the navigation lifecycle in DARK mode [S41 proved it in light; accent-dark’s goto pass never reads it] — encoded as a standing probe: node scripts/accent-volume-sweep.mjs, the suite’s 25th audit stage [25-min budget]; the probe inserts the S13 profile through Prisma and COUNT-VERIFIES the insert (the volume self-verification precondition — a sweep that silently measured seed volume must never run), then for every accent [7 — the ALL-ACCENTS guard] sweeps the 7 volume-affected views navigated THROUGH THE SIDEBAR with the S41 desktop contracts + the interaction evidence in DARK (the never-read cell) + the S38 dark detector family + the S13 volume measurement family (the 1500ms long-task budget, the click→heading 1500ms interactivity budget) + the sidebar record per accent + the /api/tasks ≤ 500KB B1 bound; every failure path cleans up its marker rows (the finally-race fix — the cleanup completes BEFORE the AP-85 flush-callback exit); the volume × LIGHT family is the documented bracketed non-gap; AP-88 — a contract proven at seed volume is not proven at volume) ✅ Complete docs/remediation-plan-session42.md
Session-43 data-volume × LIGHT × accent stress (the S42 summary’s own first named next candidate — the documented bracketed non-gap, now closed): the light half of the volume × theme cross-axis was ZERO-coverage [the S13 data-volume audit runs light + violet only — zero theme references; the S37–S41 light-accent sweeps run at seed volume; the S42 accent-volume-sweep runs dark only], so the densest renderings (the 300-row Tasks list, the 250-event terminal panel, the 200-note list) had never carried an accent in LIGHT, the S13 volume budgets had never been measured under an accent in light, and the SIDEBAR’s active-link tint had never been read in LIGHT at VOLUME (S41 proved light at seed; S42 dark at volume). Closed with the accent-volume-light-sweep probe (7 accents × the 7 volume views, navigated through the sidebar at 1280×800 under the COUNT-verified 300/200/250 profile, the LIGHT checker with the THREE signature-strict light exemptions, the S13 budgets + the /api/tasks B1 bound) + the classifyAccentVolumeLightSweep seam + 32 unit pins (518 → 550, 841 green total) + the runner’s 25th audit stage (the suite now 27 rows) ✅ Complete scripts/accent-volume-light-sweep.mjs + src/lib/suite-verdict.ts + tests/suite-verdict.test.ts
Session-44 dark × mobile × volume stress (the S43 summary’s own first named next candidate: the documented bracketed non-gap, now closed): the dark × mobile × volume cell was ZERO-coverage [the S13 data-volume audit runs 1280×800 light+violet only; every mobile-viewport sweep runs at seed volume; the S42/S43 volume × accent sweeps run desktop only — the viewport axis their blind spot], so the densest renderings had never been rendered in dark at the stacked mobile layout under ANY accent, the S13 budgets had never been measured at the mobile viewport under a theme variation, and the DRAWER’s active-link tint had never been read in DARK at MOBILE at VOLUME. Closed with the accent-volume-dark-mobile-sweep probe (7 accents × the 7 volume views, navigated through the drawer at 390×844 in dark under the COUNT-verified 300/200/250 profile, the MOBILE vehicle contract + the drawer-own nav-scroll contract, the mobile structural family at volume, the DARK checker with the CTA-face exemption only, the S13 budgets + the /api/tasks B1 bound, the interaction evidence via the pendingView pattern) + the classifyAccentVolumeDarkMobileSweep seam + 33 unit pins (550 → 583, 874 green total) + the runner’s 26th audit stage (the suite now 28 rows) ✅ Complete scripts/accent-volume-dark-mobile-sweep.mjs + src/lib/suite-verdict.ts + tests/suite-verdict.test.ts
Session-45 light × mobile × volume stress (the S44 summary’s own first named next candidate: the documented bracketed non-gap, now closed): the light × mobile × volume cell was ZERO-coverage [the S13 data-volume audit runs 1280×800 light+violet only; every mobile-viewport sweep runs at seed volume; the S42/S43 volume × accent sweeps run desktop only; the S44 dark-mobile volume sweep is dark only — the mode axis its blind spot], so the densest renderings had never been rendered in LIGHT at the stacked mobile layout under ANY accent, the S13 budgets had never been measured at the mobile viewport in light under a theme variation, and the DRAWER’s active-link tint had never been read in LIGHT at MOBILE at VOLUME. Closed with the accent-volume-light-mobile-sweep probe (7 accents × the 7 volume views, navigated through the drawer at 390×844 in light under the COUNT-verified 300/200/250 profile, the MOBILE vehicle contract + the drawer-own nav-scroll contract, the mobile structural family at volume, the LIGHT checker with the THREE signature-strict light exemptions [the CTA-face + the calendar adjacent-day + the yellow priority-pill], the S13 budgets + the /api/tasks B1 bound, the interaction evidence via the pendingView pattern, the light-mode hydration gate) + the classifyAccentVolumeLightMobileSweep seam + 35 unit pins (583 → 618, 909 green total) + the runner’s 27th audit stage (the suite now 29 rows) ✅ Complete scripts/accent-volume-light-mobile-sweep.mjs + src/lib/suite-verdict.ts + tests/suite-verdict.test.ts
Session-46 dark × mid-band × volume stress (the S45 summary’s own first named next candidate — the mid-band/landscape × volume cells, the dark half first): the dark × mid-band × volume cell was ZERO-coverage [the S13 data-volume audit runs 1280×800 light+violet only; the S34/S39 mid-band sweeps run LIGHT at SEED volume; the S42–S45 volume × accent sweeps run 1280/390 only; NO dark sweep runs at 768 at all], so the densest renderings had never been rendered in DARK at the 768×1024 stacked layout under ANY accent, the S13 budgets had never been measured at the mid-band viewport in dark under a theme variation, and the DRAWER’s active-link tint had never been read through the real navigation lifecycle in DARK at MID-BAND at VOLUME. Closed with the accent-volume-dark-midband-sweep probe (7 accents × the 7 volume views, navigated through the drawer at 768×1024 in dark under the COUNT-verified 300/200/250 profile, the MID-BAND vehicle contract + the drawer-own nav-scroll contract at the mid-band geometry [scrollHeight 1068 vs clientHeight ~935, measured — links ~17–20 below the fold], the below-lg structural family at volume, the DARK checker with the CTA-face-only exemption, the S13 budgets + the /api/tasks B1 bound, the interaction evidence via the pendingView pattern, the shell-presence guard from birth) + the classifyAccentVolumeDarkMidbandSweep seam + 37 unit pins (618 → 655, 946 green total) + the runner’s 28th audit stage (the suite now 30 rows) ✅ Complete scripts/accent-volume-dark-midband-sweep.mjs + src/lib/suite-verdict.ts + tests/suite-verdict.test.ts
Session-47 light × mid-band × volume stress (the S46 summary’s own first named next candidate — the S47 mirror): the light × mid-band × volume cell was ZERO-coverage [the S13 data-volume audit runs 1280×800 light+violet only; the S34/S39 mid-band sweeps run LIGHT at SEED volume; the S42–S45 volume × accent sweeps run 1280/390 only; the S46 dark-midband volume sweep is DARK only — the mode axis its blind spot], so the densest renderings had never been rendered in LIGHT at the 768×1024 stacked layout under ANY accent at volume, the S13 budgets had never been measured at the mid-band viewport in light under a theme variation, and the DRAWER’s active-link tint had never been read through the real navigation lifecycle in LIGHT at MID-BAND at VOLUME. Closed with the accent-volume-light-midband-sweep probe (7 accents × the 7 volume views, navigated through the drawer at 768×1024 in light under the COUNT-verified 300/200/250 profile, the MID-BAND vehicle contract + the drawer-own nav-scroll contract at the mid-band geometry, the below-lg structural family at volume, the LIGHT checker with the THREE signature-strict light exemptions [the CTA-face + the calendar adjacent-day + the yellow priority-pill; NO flashbulbs — the class exists only in dark], the light-mode hydration gate, the S13 budgets + the /api/tasks B1 bound, the interaction evidence via the pendingView pattern, the shell-presence guard from birth) + the classifyAccentVolumeLightMidbandSweep seam + 38 unit pins (655 → 693, 984 green total) + the runner’s 29th audit stage (the suite now 31 rows) ✅ Complete scripts/accent-volume-light-midband-sweep.mjs + src/lib/suite-verdict.ts + tests/suite-verdict.test.ts
Session-48 dark × landscape × volume stress (the S47 summary’s own named next candidate — the landscape × volume cells, the dark half first, the LAST viewport band): the dark × landscape × volume cell was ZERO-coverage [the S13 data-volume audit runs 1280×800 light+violet only; the S35/S40 landscape sweeps run LIGHT at SEED volume; the S42–S45 volume × accent sweeps run 1280/390 only; the S46/S47 mid-band volume sweeps run 768×1024 only; NO dark sweep runs at 844 at all], so the densest renderings had never been rendered in DARK at the 844×390 landscape layout under ANY accent at volume, the S13 budgets had never been measured at the landscape viewport in dark under a theme variation, and the DRAWER’s active-link tint had never been read through the real navigation lifecycle in DARK at LANDSCAPE at VOLUME. Closed with the accent-volume-dark-landscape-sweep probe (7 accents × the 7 volume views, navigated through the drawer at 844×390 in dark under the COUNT-verified 300/200/250 profile, the LANDSCAPE vehicle contract + the drawer-own nav-scroll contract at the landscape geometry [scrollHeight ~1068 vs clientHeight ~301 — links ~8–20 below the fold], the below-lg structural family at volume [the bar covers 16% of the 390px viewport], the DARK checker with the CTA-face exemption ONLY [flashbulbs always findings], the dark-mode precondition through the REAL lifecycle with the greeting-heading initial gate, the S13 budgets + the /api/tasks B1 bound, the interaction evidence via the pendingView pattern, the shell-presence guard from birth) + the classifyAccentVolumeDarkLandscapeSweep seam + 40 unit pins (693 → 733, 1024 green total) + the runner’s 30th audit stage (the suite now 32 rows) ✅ Complete scripts/accent-volume-dark-landscape-sweep.mjs + src/lib/suite-verdict.ts + tests/suite-verdict.test.ts
Session-49 light × landscape × volume stress (the S48 summary’s own named next candidate — the S49 mirror, completing the volume × theme × viewport cross-axis’s LAST band): the light × landscape × volume cell was ZERO-coverage [the S13 data-volume audit runs 1280×800 light+violet only; the S35/S40 landscape sweeps run LIGHT at SEED volume; the S42–S45 volume × accent sweeps run 1280/390 only; the S46/S47 mid-band volume sweeps run 768×1024 only; the S48 landscape volume sweep is DARK only], so the densest renderings had never been rendered in LIGHT at the 844×390 landscape layout under ANY non-violet accent at volume, the S13 budgets had never been measured at the landscape viewport in light under an accent variation, and the DRAWER’s active-link tint had never been read through the real navigation lifecycle in LIGHT at LANDSCAPE at VOLUME. Closed with the accent-volume-light-landscape-sweep probe (7 accents × the 7 volume views, navigated through the drawer at 844×390 in light under the COUNT-verified 300/200/250 profile, the LANDSCAPE vehicle contract + the drawer-own nav-scroll contract at the landscape geometry [scrollHeight ~1068 vs clientHeight ~301 — links ~8–20 below the fold], the below-lg structural family at volume [the bar covers 16% of the 390px viewport], the LIGHT checker with the THREE signature-strict light exemptions [the CTA-face + the calendar adjacent-day + the yellow priority-pill; NO flashbulbs — the class exists only in dark], the light-mode hydration gate [the greeting heading], the S13 budgets + the /api/tasks B1 bound, the interaction evidence via the pendingView pattern, the shell-presence guard from birth) + the classifyAccentVolumeLightLandscapeSweep seam + 40 unit pins (733 → 773, 1064 green total) + the runner’s 31st audit stage (the suite now 33 rows — the volume × theme × viewport cross-axis COMPLETE: all EIGHT halves) ✅ Complete scripts/accent-volume-light-landscape-sweep.mjs + src/lib/suite-verdict.ts + tests/suite-verdict.test.ts
Session-50 dark × landscape × SEED sweep for the 13 volume-invariant views (the S46 plan’s carried note + the S49 plan’s own non-gap entry): the 13 views that render no task/note/event rows had never been rendered in dark at 844×390 under ANY accent [the S35/S40 landscape sweeps run LIGHT only; the S36/S38 dark sweeps run 390×844 + 1280×800 only; the S48/S49 volume sweeps navigate the 7 volume views only] — closed by scripts/accent-dark-landscape-seed-sweep.mjs (7 accents × 13 views navigated through the drawer at 844×390 dark at seed, the dark checker [flashbulbs always findings; the CTA-face exemption ONLY], the drawer-own nav-scroll contract, the 20-link census, the pendingView interaction evidence) + the classifyAccentDarkLandscapeSeedSweep seam + 32 unit pins (773 → 805, 1096 green total) + the runner’s 32nd audit stage (the suite now 34 rows) ✅ Complete docs/remediation-plan-session50.md
Session-51 CWV × volume stress — the RUM-beacon cross-axis (the S45–S50 summaries’ named last standing candidate): the beacon’s five field metrics [TTFB/FCP/LCP/CLS + the INP proxy] had NEVER been measured under data volume — the S16 cwv audit runs login+dashboard at seed only, the S13 volume audit never reads LCP/CLS/FCP, the S42–S49 volume sweeps observe longtask entries only — closed by scripts/cwv-volume-stress.mjs (the 7 volume-affected views × 2-run medians at 390×844 under the Lighthouse throttle against the :3200 standalone, the COUNT-verified 300/200/250 profile, the INP proxy re-anchored to the 300-row Tasks mount) + the classifyCwvVolumeStress seam (the volume-EXACT/throttle/ALL-VIEWS guards; the LCP ≤ 4400 parity anchor [the reference’s own seed dashboard measures 4416–4432ms under the same preset] · CLS ≤ 0.1 · FCP ≤ 1500 · TTFB ≤ 500 · longTask ≤ 2000 · INP ≤ 3000 budget families) + 29 unit pins (805 → 834, 1125 green total) + the runner’s 33rd audit stage (the suite now 35 rows) ✅ Complete docs/remediation-plan-session51.md
Session-52 collapsed-sidebar parity — the reference SHIPPED a collapsible sidebar since the S19–S51 sweeps (re-measured live: w-20/80px icon rail ↔ w-[260px], the chevron toggle in the header in both states, the collapsed footer completely empty, main ml-20 reflow, localStorage.sidebarOpen persistence, and the MOBILE DRAWER sharing the state — icon-only links when collapsed) and the clone’s session-8-era implementation had drifted on six surfaces plus one functional bug (the main offset NEVER shifted — a 184px dead zone): closed by the four-file fix (page.tsx main lg:ml-[260px] ↔ lg:ml-20; sidebar.tsx w-20 + the header/footer restructure; nav-items.tsx left-aligned px-4 icons + the aria-label superset; mobile-chrome.tsx the drawer state sharing) + the corrected 76→80px navigation pin + 8 new e2e pins in tests/e2e/s52-sidebar-collapse.spec.ts (the geometry/alignment/footer/persistence/drawer-sharing/rotated-glyph families; 291 → 299 specs, 1133 green total) ✅ Complete docs/remediation-plan-session52.md
Session-53 post-S52 full-suite confirmation — the S52 summary’s own named next step: the first full standing-suite run after the collapsed-sidebar app-code change, plus the standing arrival audit (the fresh-clone 1133-green baseline re-confirmed, the preflight + drawer-check GREEN, the live 390×844 walkthrough GREEN — the mobile navigation menu working as expected, NO Tailwind v4 bug found [traps 4/7/9 verified live], the reference re-swept UNCHANGED since S52 with BOTH sidebar states re-measured per the new protocol [expanded 260/260, collapsed 80/80 + the empty footer + icon-only left-aligned links + the rotate-180 glyph + the shared drawer state], the Mode-C review zero findings, the dep-audit GREEN) → the standing suite GREEN 35/35 (~48 min — the S31 preflight + the 33 standing audits + the theme-restore closing guard); ZERO findings → ZERO code changes by design (the smallest safe diff is no diff on a verified-green production codebase); the 35-row evidence envelope + 5 dev-server screenshots saved; the count anchors (1133/834/299) untouched ✅ Complete docs/remediation-plan-session53.md
Session-54 S53+1 stability confirmation — the standing arrival audit plus the full-suite re-confirmation on a codebase with ZERO app-code changes since the S52 four-file fix (S53 closed zero-findings/zero-code; the two commits since verified doc-only by git show --stat): the 1133-green baseline re-confirmed byte-for-byte, the preflight + drawer-check GREEN, the live 390×844 walkthrough GREEN — the mobile navigation menu working as expected, NO Tailwind v4 bug found [traps 4/7/9 verified live], the reference re-swept UNCHANGED since S52/S53 with BOTH sidebar states re-measured [expanded 260/260/1020; collapsed 80/80/1200 + the chevron-only header + the empty footer + icon-only left-aligned links svgX 33 + the rotate-180 glyph + the shared 288px drawer state] + the four standing anchors [title, greeting format, 20-view order, five Settings tabs], the Mode-C review zero findings, the dep-audit GREEN — plus the FRESH dep-registry re-check: braces latest published = the lockfile’s own 3.0.3 [the fix release does not exist], deepmerge-ts 8.0.2 requires the Prisma major [8.0.0-rc.22 RC-only] — both advisories registry-evidenced as unfixable upstream, the upstream-wait position re-confirmed → the standing suite GREEN 35/35; ZERO findings → ZERO code changes by design (the S53 precedent); the 35-row evidence envelope + the dev-server screenshot set saved; the count anchors (1133/834/299) untouched ✅ Complete docs/remediation-plan-session54.md
Session-55 the date-fragile calendar pins + the drawer-panel measurement race — the first arrival to ever run the full e2e gate on a UTC 15th found TWO test-suite defects (zero app-code findings): F1 — the two calendar “deselect today” pins (parity-session8 + dark-mode) hardcoded clicking day 15, which clicks the ALREADY-SELECTED today whenever the UTC date is the 15th, so the today-tint never appears and both specs time out at 45 s (reproduced 3/3 full runs at 297/299; no prior session ever hit it because the full playwright gate never ran on a UTC 15th — S54’s baseline ran Oct 14 hour-23 UTC, and the post-midnight standing suite is agent-browser-based), fixed by reading the “other” day from the grid’s own [data-today] marker (today===15 → click 16 — never clicking today, runner-clock-independent); F2 — the S52-F drawer-panel width pin measured getBoundingClientRect() immediately after toBeVisible(), racing the panel’s 0.25 s sf-slide-in entry animation (scale(0.98) → ~282 px mid-flight vs the 288 px w-72, 282−288 =6 breaching the <2 px tolerance — flaked 2/~10 runs), fixed with expect.poll on the settled width (the S8 settled-value doctrine; the standing drawer-check was always immune — it reads getComputedStyle().width, transform-independent); both fixes test-code only, the assertions unchanged, the app untouched (the animation is parity behavior) — the mobile navigation verified working on BOTH apps at 390×844 with NO Tailwind v4 bug [traps 4/7/9 verified live], the reference re-swept UNCHANGED since S52/S53/S54 [the four anchors + BOTH sidebar states + the shared 288px drawer], the Mode-C review zero app-code findings, the dep-audit GREEN + the fresh registry re-check [braces 3.0.3 is still the latest published; deepmerge-ts 8.0.2 still needs the RC-only Prisma 8] → the gates restored to 1133 green (lint ✓ tsc ✓ 834 unit ✓ build ✓ cold-db 299 e2e ✓ — the 45 s timeouts gone, the s52 spec 6/6 in the flake loop) and the standing suite re-run as the confirmation
Session-56 the S55+1 stability confirmation + the registry-landscape shift — the standing arrival audit on the S55-closed codebase (exactly one doc-only commit since: f8f1204 = session_148, verified by git show --stat) read ZERO findings on every axis [baseline gates 1133 green byte-for-byte — lint ✓ tsc ✓ 834 unit ✓ build ✓ cold-db 299 e2e ✓ 4.6 min, the S55 date-fragility fix runner-clock-independent by construction; the preflight + drawer-check GREEN; the live 390×844 walkthrough GREEN — the mobile navigation working, NO Tailwind v4 bug (traps 4/7/9 verified live clean), both desktop sidebar states + the shared drawer measured to the pin; the reference re-swept UNCHANGED since S52/S53/S54/S55 — the four anchors + BOTH sidebar states + the shared 288px drawer, the preference restored; the Mode-C review zero findings; the dep-audit GREEN] → zero code changes by design (the S53/S54 precedent: the smallest safe diff is no diff on a verified-green production codebase), with the session’s ONE development being the dep-registry shift: the deepmerge-ts advisory (7.1.5 via [email protected] › @prisma/config) is NOW fixable on a stable line — the 6.x line ended at 6.19.3 while the stable Prisma 7.10.1 carries @prisma/[email protected] → [email protected] (braces unchanged: 3.0.3 = latest, the fix release does not exist) — the judgment call: the major ORM upgrade 6→7 is the highest-risk change class for THIS repo (the ADR-002 three-anchor DATABASE_URL contract, the generator story, the .env auto-load paths — every surface pinned), and the advisory is non-production by construction (CLI-surface only, 0 runtime-reachable), so the upgrade is recorded as the named next-step candidate for a DEDICATED session (owner decision), not an in-session drive-by; the standing suite re-run as the confirmation + the fresh evidence captures (the 35-row envelope + 5 dev-server screenshots + 2 reference captures) ✅ Complete docs/remediation-plan-session56.md
Session-57 the AP-90 pre-mount gate race — the two-spec e2e hardening: the fresh-clone baseline run (the workspace had been reset — the S52/S53/S55 rebuild recipe) produced the project’s first observed e2e flake in two sessions at 298/299: F1 — the S8-A nav-icons pin failed with “Expected length: 20 / Received length: 0” (911 ms, no timeout — the race signature), root-caused to the documented AP-90 class: page.tsx applies the theme (loadFromUser → --sf-primary) BEFORE await dataPromise (the initial-collections pre-warm) while the shell flips off only after BOTH settle, so the spec's `hydrated()` theme gate is satisfiable while the Splash shows and the non-auto-waiting `evaluateAll` reads 0 icons under full-run load (the design itself is parity behavior — the no-CLS Splash→shell replacement; the gate was the stale expectation, per S28/ADR-026 a finding whose own evidence disproves an app defect); F2 — the same latent class in the S12-B first-Tab-stop pin (the gotoMode mode wait satisfiable at first paint by the pre-hydration boot script → a pre-mount Tab lands on); BOTH reproduced RED deterministically via a throwaway diagnostic delaying only the five Dashboard pre-warm endpoints by 600 ms (F1: shell false / icons 0 — byte-identical to the live failure; F2: BODY "Loading StudyFlow…"), fixed with the documented AP-90 guard — waitForFunction(() => !!document.querySelector("aside nav")) folded into both specs (test-code only, +14 lines, assertions and pin counts unchanged, zero app code), proven GREEN under the IDENTICAL delay (F1: 20/20 icons; F2: A "Skip to main content"), the diagnostic deleted, the full cold e2e re-run 299 passed = 1133 green restored — the arrival audit otherwise GREEN on every axis [preflight + drawer-check; the live 390×844 walkthrough — the mobile navigation working, NO Tailwind v4 bug (traps 4/7/9 verified live clean), both desktop sidebar states + the shared drawer measured to the pin; the reference re-swept UNCHANGED since S52–S56 (the four anchors + BOTH sidebar states + the shared 288px drawer, the preference restored); the Mode-C review zero app-code findings; the dep-audit GREEN; the registry re-check — braces unchanged, the stable Prisma 7.10.1 fix path re-confirmed with the latest dist-tag now on the Prisma 8 RC, the S56 judgment call re-affirmed]; the standing suite re-run as the confirmation + the fresh evidence captures (the 35-row envelope + 5 dev-server screenshots + 2 reference captures) ✅ Complete docs/remediation-plan-session57.md
Session-58 the S57+1 stability confirmation — the standing arrival audit on the S57-closed codebase (exactly two doc-only commits since: 1e103c8 = session_154 + the worklog note, ebedc7e = session_155, both verified by git show --stat) read ZERO findings on every axis → zero code changes by design (the S53/S54/S56 precedent: the smallest safe diff is no diff on a verified-green production codebase). The session’s centerpiece: the S57 two-spec AP-90 hardening got its first fresh-clone full-run confirmation — the cold-db e2e read 299/299 (4.5 min) under the same full-run load class that first exposed the race (S57’s baseline had read 298/299), the shell-presence guards holding with zero flake exactly as the S57 GREEN proof predicted — 1133 green re-confirmed byte-for-byte (lint ✓ tsc ✓ 834 unit ✓ [20 files, 3.52 s] build ✓). The standing checks all GREEN: the preflight + drawer-check; the live 390×844 walkthrough (login → scrollWidth 390 → the drawer 20 links in the reference order → nav 1068 vs 755 → the active link violet-600 + the violet→indigo 10% gradient + the trailing violet-500 dot → Notes via the drawer with auto-close → Escape-close) — the mobile navigation working, NO Tailwind v4 bug (traps 4/7/9 verified live clean); both desktop sidebar states + the shared mobile drawer measured to the pin (expanded 260/260/1020; collapsed 80/80/1200, svgX 33, the empty footer, the rotate-180 glyph, persisted; the shared drawer 288px, icon-only svgX 32, 988 vs 755); the reference re-swept UNCHANGED since S52–S57 (the four anchors + BOTH sidebar states + the shared 288px drawer with the reference’s own blur(4px)/rgba backdrop; the preference restored); the Mode-C review zero app-code findings (the static scan clean, the SDK server-only in the 4 AI routes); the dep-audit GREEN; the registry re-check unchanged (braces 3.0.3 = latest, unfixable upstream; the stable Prisma 7.10.1 fix path re-confirmed — the 7.x line still ends at 7.10.1, the latest dist-tag still on the Prisma 8 RC; the S56 judgment call re-affirmed); scandihaven unchanged (d4789c3). ONE probe lesson recorded (self-inflicted, not a finding): a geometry-based burger locator matched the HIDDEN desktop expand button at 390×844 (display:none reports a zero rect, 0 < 60 passes), silently flipping the stored preference — the correct probe filters on visibility (offsetParent !== null), after which the shared collapsed drawer read byte-identical to every documented pin; the standing suite re-run as the confirmation (GREEN 35/35) + the fresh evidence captures (the 35-row envelope + 5 dev-server screenshots + 2 reference captures) ✅ Complete docs/remediation-plan-session58.md
Session-59 the S58+1 stability confirmation — the standing arrival audit on the S58-closed codebase (exactly two doc-only commits since: 9d32a77 = session_158 + the ticked plan checkbox + the worklog note, 6ad6585 = session_159, both verified by git show --stat) read ZERO findings on every axis → zero code changes by design (the S53/S54/S56/S58 precedent: the smallest safe diff is no diff on a verified-green production codebase). The baseline gates re-confirmed byte-for-byte on the fresh clone: lint ✓ tsc ✓ 834 unit ✓ (20 files, 3.66 s) build ✓ cold-db e2e 299/299 (4.5 min) = 1133 green. The standing checks all GREEN: the preflight + drawer-check; the live agent-browser 390×844 walkthrough (login → scrollWidth 390 → sidebar hidden → burger visible → the drawer 20 links in the reference order → nav scrollable 1068 vs 755 → the active link violet-600 rgb(124,58,237) + the violet→indigo 10% gradient + the trailing violet-500 dot rgb(139,92,246) → Notes via the drawer with auto-close → Escape-close) — the mobile navigation menu is working as expected; NO Tailwind v4 bug found (traps 4/7/9 verified live clean: the nav UL display flex / gap 4px / column / zero margin children; the backdrop blur(4px); bg-black/20 serializing oklab(0 0 0 / 0.2) — the documented v4 form); both desktop sidebar states + the shared mobile drawer state measured to the pin (expanded 260/260/1020; collapsed 80/80/1200 with 20 icon-only links svgX 33, the empty footer, the glyph rotate: 180deg, sf-sidebar-collapsed persisted; the drawer sharing: the 288px panel, 44px icon-only links, svgX 32, nav 988 vs 755); the reference re-swept UNCHANGED since S52–S58 (the four anchors — the title, the hour-8 ‘Good morning’ greeting, the 20-view href order byte-identical, the five Settings tabs — plus BOTH sidebar states re-measured per the protocol, plus the shared collapsed drawer: the 288px panel, 44px icon-only links, svgX 32, nav 912 vs 744, the reference’s own blur(4px) + rgba(0,0,0,0.2) backdrop; the reference’s Escape-close absence re-verified as the clone’s documented superset; the stored preference restored to ‘true’ before session close); the Mode-C code review ZERO app-code findings (the static scan clean — zero TODO/FIXME/: any/console.log, the single eval( hit = the documented prose comment in calculator.ts:4, the one dangerouslySetInnerHTML = the documented THEME_BOOT_SCRIPT, the SDK server-only in the 4 AI routes); the standing dep-audit exit 0 with the 2 documented non-production advisories); the registry re-check UNCHANGED (braces 3.0.3 = latest — unfixable upstream; the stable Prisma 7.10.1 fix path re-confirmed — the 7.x line still ends at 7.10.1 [7.9.1/7.10.0/7.10.1], no newer 7.x shipped, the latest dist-tag still on the Prisma 8 RC, prev = 7.10.1; the S56 judgment call re-affirmed: the major upgrade stays the dedicated-session candidate, owner decision); scandihaven unchanged (d4789c3). ONE capture lesson recorded (self-inflicted, not a finding): the first expanded-desktop screenshot was taken ~3 s after the login click with only the chrome geometry verified in-frame and caught the shell mid-settle (the AP-90 class striking the evidence capture itself — 28,770 differing px vs the S58 counterpart, the CTA region showing ghost text); diagnosed empirically (a same-state capture minutes later reproduced the delta vs the first but read only 162 px vs the settled shot) and re-captured with the CONTENT element verified in-frame — the expanded capture then read 492 px / +196 bytes vs S58, within the documented ±240 clock/data class; the lesson: gate evidence captures on the target content element, not only on the chrome geometry. THE CONFIRMATION: the standing suite GREEN 35/35, ZERO total findings (~70 min; the S31 preflight, the 33 standing audits, the theme-restore closing guard light/violet). The evidence: the 35-row envelope + 5 dev-server screenshots (the collapsed capture byte-identical at 216,954 bytes) + 2 reference captures. Docs aligned: the session-59 remediation plan (the findings table + the executed ToDo list + the execution log), session_160 (narrative) + session_161 (Chinese summary), the README session-59 row, the worklog entry. The count anchors (1133/834/299) untouched — zero code changes ✅ Complete docs/remediation-plan-session59.md
Session-60 the S59+1 stability confirmation — the standing arrival audit on the S59-closed codebase (exactly two doc-only commits since: 45a816f = session_162, the S59 process log, + the ticked plan checkbox + the worklog note; 83a0b05 = session_163, the S59 narrative; both verified by git show --stat) read ZERO findings on every axis → zero code changes by design (the S53/S54/S56/S58/S59 precedent: the smallest safe diff is no diff on a verified-green production codebase). The baseline gates re-confirmed byte-for-byte on the fresh clone: lint ✓ tsc ✓ 834 unit ✓ (20 files, 3.70 s) build ✓ cold-db e2e 299/299 (4.7 min) = 1133 green. The standing checks all GREEN: the preflight + drawer-check; the live agent-browser 390×844 walkthrough (login → scrollWidth 390 → sidebar hidden → burger visible → the drawer 20 links in the reference order → nav scrollable 1068 vs 755 → the active link violet-600 rgb(124,58,237) + the violet→indigo 10% gradient + the trailing violet-500 dot rgb(139,92,246) 6px → Notes via the drawer with auto-close → Escape-close) — the mobile navigation menu is working as expected; NO Tailwind v4 bug found (traps 4/7/9 verified live clean: the nav UL display flex / gap 4px / column / zero margin children; the backdrop backdrop-filter: blur(4px) — probed on the backdrop-filter property per the v4 notched-blur contract; bg-black/20 serializing oklab(0 0 0 / 0.2) — the documented v4 form); both desktop sidebar states + the shared mobile drawer state measured to the pin (expanded 260/260/1020; collapsed 80/80/1200 with 20 icon-only links svgX 33 [the x-position probe per the S52 protocol], the empty footer, the glyph rotate: 180deg, sf-sidebar-collapsed persisted; the drawer sharing: the 288px panel, 44px icon-only links, svgX 32, nav 988 vs 755); the reference re-swept UNCHANGED since S52–S59 (the four anchors — the title, the hour-17 ‘Good evening’ greeting [the evening bucket 17–23, correct per the bucket map], the 20-view href order byte-identical, the five Settings tabs — plus BOTH sidebar states re-measured per the protocol, plus the shared collapsed drawer: the 288px panel, 44px icon-only links, svgX 32, nav 912 vs 744, the reference’s own blur(4px) + rgba(0,0,0,0.2) DIV backdrop; the reference’s Escape-close absence re-verified as the clone’s documented superset; the stored preference restored to ‘true’ before session close); the Mode-C code review ZERO app-code findings (the static scan clean — zero TODO/FIXME/: any/console.log, the single eval( hit = the documented prose comment in calculator.ts:4, the one dangerouslySetInnerHTML = the documented THEME_BOOT_SCRIPT, the SDK server-only in the 4 AI routes); the standing dep-audit exit 0 with the 2 documented non-production advisories); the registry re-check UNCHANGED (braces 3.0.3 = latest — unfixable upstream; the stable Prisma 7.10.1 fix path re-confirmed — the 7.x stable line verified 7.9.0/7.9.1/7.10.0/7.10.1, still ends at 7.10.1, no newer 7.x shipped, the latest dist-tag still on the Prisma 8 RC with prev = 7.10.1; the S56 judgment call re-affirmed: the major upgrade stays the dedicated-session candidate, owner decision); scandihaven unchanged (d4789c3). THE CONFIRMATION: the standing suite re-run as the confirmation + the fresh evidence captures (the 35-row envelope + the 5 dev-server screenshots [gated on the content element per the S59 capture lesson] + the 2 reference captures [the desktop expanded gated on the CTA verified in-frame at x1092/y46/155×36]). Docs aligned: the session-60 remediation plan (the findings table + the executed ToDo list + the execution log), session_164 (narrative) + session_165 (Chinese summary), the README session-60 row, the worklog entry. The count anchors (1133/834/299) untouched — zero code changes ✅ Complete docs/remediation-plan-session60.md
Session-61 the S60+1 stability confirmation — the standing arrival audit on the S60-closed codebase (exactly two doc-only commits since: f986301 = session_166, the S60 process log, + the ticked plan checkbox + the worklog note; 44b9528 = session_167, the S60 narrative; both verified by git show --stat) read ZERO findings on every axis → zero code changes by design (the S53/S54/S56/S58/S59/S60 precedent: the smallest safe diff is no diff on a verified-green production codebase). The baseline gates re-confirmed byte-for-byte on the fresh clone: lint ✓ tsc ✓ 834 unit ✓ (20 files, 3.58 s) build ✓ cold-db e2e 299/299 (4.6 min) = 1133 green. The standing checks all GREEN: the preflight + drawer-check; the live agent-browser 390×844 walkthrough (login → scrollWidth 390 → sidebar hidden → burger visible → the drawer 20 links in the reference order → nav scrollable 1068 vs 755 → the active link violet-600 rgb(124,58,237) + the violet→indigo 10% gradient on the .sf-nav-active label-wrapper span + the trailing violet-500 dot rgb(139,92,246) 6px → Notes via the drawer with auto-close → Escape-close) — the mobile navigation menu is working as expected; NO Tailwind v4 bug found (traps 4/7/9 verified live clean: the nav UL display flex / gap 4px / column / zero margin children; the backdrop backdrop-filter: blur(4px) — probed on the backdrop-filter property per the v4 notched-blur contract; bg-black/20 serializing oklab(0 0 0 / 0.2) — the documented v4 form); both desktop sidebar states + the shared mobile drawer state measured to the pin (expanded 260/260/1020; collapsed 80/80/1200 with 20 icon-only links svgX 33 [the x-position probe per the S52 protocol], the empty footer, the glyph rotate: 180deg, sf-sidebar-collapsed persisted; the drawer sharing: the 288px panel, 44px icon-only links, svgX 32, nav 988 vs 755); the reference re-swept UNCHANGED since S52–S60 (the four anchors — the title, the hour-21 ‘Good evening’ greeting [the evening bucket 17–23, correct per the bucket map], the 20-view href order byte-identical, the five Settings tabs — plus BOTH sidebar states re-measured per the protocol, plus the shared collapsed drawer: the 288px panel, 44px icon-only links, svgX 32, nav 912 vs 744, the reference’s own blur(4px) + rgba(0,0,0,0.2) DIV backdrop; the reference’s Escape-close absence re-verified as the clone’s documented superset; the stored preference restored to ‘true’ before session close); the Mode-C code review ZERO app-code findings (the static scan clean — zero TODO/FIXME/: any/console.log, the single eval( hit = the documented prose comment in calculator.ts:4, the one dangerouslySetInnerHTML = the documented THEME_BOOT_SCRIPT, the SDK server-only in the 4 AI routes); the standing dep-audit exit 0 with the 2 documented non-production advisories); the registry re-check UNCHANGED (braces 3.0.3 = latest — unfixable upstream; the stable Prisma 7.10.1 fix path re-confirmed — the 7.x stable line still ends at 7.10.1, no newer 7.x shipped, the latest dist-tag still on the Prisma 8 RC with prev = 7.10.1 and the next tag an 8.0.0-rc.10 RC channel; the S56 judgment call re-affirmed: the major upgrade stays the dedicated-session candidate, owner decision); scandihaven unchanged (d4789c3). THE CONFIRMATION: the standing suite re-run as the confirmation — run 1 (21:22 UTC) read 34/35 with the ai-a11y stage exiting 1 on a Node.js fatal-error footer (the transient heap-OOM class: the documented ~4 GB container ceiling, striking after the heaviest stages — the audit re-run standalone read exit 0 / findings [] twice; zero code findings), the dev daemon restarted cleanly + the FULL suite re-launched (run 2, 22:35 UTC) — GREEN 35/35, ZERO total findings, zero code changes between the runs + the fresh evidence captures (the 35-row envelope with the run-1 incident recorded in the suiteRuns context field + the 5 dev-server screenshots [gated on the content element per the S59 capture lesson — the collapsed capture byte-identical to S60’s at 216,857 bytes; the expanded’s −747-byte delta classified by pixel-diff localization: 751 px (0.073%) fully at the sidebar footer’s sf-clock-chip with the ENTIRE mid-content band (y 80-639) reading ZERO differing pixels — the deterministic byte-stable proof] + the 2 reference captures [the desktop expanded gated on the CTA verified in-frame at x1093/y46/155×36]). Docs aligned: the session-61 remediation plan (the findings table + the executed ToDo list + the execution log), session_168 (narrative) + session_169 (Chinese summary), the README session-61 row, the worklog entry. The count anchors (1133/834/299) untouched — zero code changes ✅ Complete docs/remediation-plan-session61.md
Session-62 the S61+1 stability confirmation — the standing arrival audit on the S61-closed codebase (three doc-only commits since: f2ff393 = session_170, the S61 process log, + the ticked plan checkbox + the worklog note; 4e987e1 = session_171, +90; 45252a8 = session_172, the S61 narrative, +89; all verified by git show --stat) read ZERO findings on every axis → zero code changes by design (the S53/S54/S56/S58/S59/S60/S61 precedent: the smallest safe diff is no diff on a verified-green production codebase). The baseline gates re-confirmed byte-for-byte on the fresh clone: lint ✓ tsc ✓ 834 unit ✓ (20 files, 3.56 s) build ✓ cold-db e2e 299/299 (4.6 min) = 1133 green. The standing checks all GREEN: the preflight + drawer-check; the live agent-browser 390×844 walkthrough (login → scrollWidth 390 → sidebar hidden → burger visible [the visibility-filtered locator: aria-label “Open navigation menu”, offsetParent !== null] → the drawer 20 links in the reference order → nav scrollable 1068 vs 755 → the active link violet-600 rgb(124,58,237) + the violet→indigo 10% gradient on the .sf-nav-active label-wrapper span + the trailing violet-500 dot rgb(139,92,246) 6px → Notes via the drawer with auto-close → Escape-close) — the mobile navigation menu is working as expected; NO Tailwind v4 bug found (traps 4/7/9 verified live clean: the nav UL display flex / gap 4px / column / zero margin children; the backdrop backdrop-filter: blur(4px) — probed on the backdrop-filter property per the v4 notched-blur contract; bg-black/20 serializing oklab(0 0 0 / 0.2) — the documented v4 form); both desktop sidebar states + the shared mobile drawer state measured to the pin (expanded 260/260/1020; collapsed 80/80/1200 with 20 icon-only links svgX 33 [the x-position probe per the S52 protocol], the empty footer, the glyph rotate: 180deg, sf-sidebar-collapsed persisted; the drawer sharing: the 288px panel, 44px icon-only links, svgX 32, nav 988 vs 755); the reference re-swept UNCHANGED since S52–S61 (the four anchors — the title, the hour-0 ‘Good morning’ greeting [the morning bucket 0–11, correct per the bucket map], the 20-view href order byte-identical, the five Settings tabs — plus BOTH sidebar states re-measured per the protocol, plus the shared collapsed drawer: the 288px panel, 44px icon-only links, svgX 32, nav 912 vs 744, the reference’s own blur(4px) + rgba(0,0,0,0.2) DIV backdrop; the reference’s Escape-close absence re-verified as the clone’s documented superset — via the GEOMETRY probe: the reference’s drawer root carries NO role=dialog, so the semantic-selector probe falsely read ‘closed’; the corrected probe reads the 288px panel + backdrop presence; the stored preference restored to ‘true’ before session close); the Mode-C code review ZERO app-code findings (the static scan clean — zero TODO/FIXME/: any/console.log, the single eval( hit = the documented prose comment in calculator.ts:4, the one dangerouslySetInnerHTML = the documented THEME_BOOT_SCRIPT, the SDK server-only in the 4 AI routes); the standing dep-audit exit 0 with the 2 documented non-production advisories); the registry re-check UNCHANGED (braces 3.0.3 = latest — unfixable upstream; the stable Prisma 7.10.1 fix path re-confirmed — the 7.x stable line still ends at 7.10.1 [7.8.0/7.9.0/7.9.1/7.10.0/7.10.1], no newer 7.x shipped, the latest dist-tag still on the Prisma 8 RC with prev = 7.10.1 and the next tag an 8.0.0-rc.10 RC channel; the S56 judgment call re-affirmed: the major upgrade stays the dedicated-session candidate, owner decision); scandihaven unchanged (d4789c3). THE CONFIRMATION: the standing suite re-run as the confirmation — GREEN 35/35, ZERO total findings, ZERO incidents on run 1 (~63 min wall; the S31 preflight, the 33 standing audits, the theme-restore closing guard light/violet; the S61 run-1 heap-OOM class did NOT recur — the clean close of the interactive browser sessions before the launch held) + the fresh evidence captures (the 35-row envelope + the 5 dev-server screenshots [gated on the content element per the S59 capture lesson] + the 2 reference captures [the desktop expanded gated on the CTA verified in-frame at x1093/y46/155×36]). The capture-delta classification: the S62 set vs the S61 counterparts reads +1195/+647/+370/+321/+283 bytes — classified per the S59/S60/S61 evidence discipline (pixel-diff localization + y-band histogram + DOM probes at the differing positions) as fully the clock/data family, widened by TWO deterministic crossings (the greeting bucket evening→morning: y 40-79 ~6,400 px, ‘Good evening’→‘Good morning’; the calendar day Oct 16→17: y 80-119 1,344 px, ‘Friday, October 16’→‘Saturday, October 17’; plus the live clocks — the sf-clock-chip + the app-bar clock), with the mid-content band EXCLUDING the date row reading at most ~123 scattered px at the date-bearing stat-card chips — no diffuse mid-settle. Docs aligned: the session-62 remediation plan (the findings table + the executed ToDo list + the execution log), session_173 (narrative) + session_174 (Chinese summary), the README session-62 row, the worklog entry. The count anchors (1133/834/299) untouched — zero code changes ✅ Complete docs/remediation-plan-session62.md
Session-63 the S62+1 stability confirmation — the standing arrival audit on the S62-closed codebase (three doc-only commits since: d2ffa22 = the S62 evidence commit [the plan + the 35-row envelope + 7 screenshots + session_173/174 + the README row + the worklog entry]; 3f447de = session_175, the S62 process log, + the ticked plan checkbox + the worklog note; fdd028a = session_176, +89; all verified by git show --stat) read ZERO findings on every axis → zero code changes by design (the S53/S54/S56/S58/S59/S60/S61/S62 precedent: the smallest safe diff is no diff on a verified-green production codebase). The baseline gates re-confirmed byte-for-byte: lint ✓ tsc ✓ 834 unit ✓ (20 files, 3.59 s) build ✓ cold-db e2e 299/299 (4.6 min) = 1133 green. The standing checks all GREEN: the preflight + drawer-check; the live agent-browser 390×844 walkthrough (login → scrollWidth 390 → sidebar hidden → burger visible [the visibility-filtered locator: aria-label “Open navigation menu”, offsetParent !== null] → the drawer 20 links in the reference order → nav scrollable 1068 vs 755 → the active link violet-600 rgb(124,58,237) + the violet→indigo 10% gradient on the .sf-nav-active label-wrapper span + the trailing violet-500 dot rgb(139,92,246) 6×6px → Notes via the drawer with auto-close → Escape-close) — the mobile navigation menu is working as expected; NO Tailwind v4 bug found (traps 4/7/9 verified live clean: the nav UL display flex / gap 4px / column / zero margin children; the backdrop backdrop-filter: blur(4px) — probed on the backdrop-filter property per the v4 notched-blur contract; bg-black/20 serializing oklab(0 0 0 / 0.2) — the documented v4 form); both desktop sidebar states + the shared mobile drawer state measured to the pin (expanded 260/260/1020; collapsed 80/80/1200 with 20 icon-only links svgX 33 [the x-position probe per the S52 protocol], the glyph rotate: 180deg, sf-sidebar-collapsed persisted; the drawer sharing: the 288px panel, 44px icon-only links, svgX 32, nav 988 vs 755, all 20 links aria-named — the clone’s accessible-name superset); the reference re-swept UNCHANGED since S52–S62 (the four anchors — the title, the hour-2 ‘Good morning’ greeting [the morning bucket 0–11, correct per the bucket map], the 20-view href order byte-identical, the five Settings tabs — plus BOTH sidebar states re-measured per the protocol, plus the shared collapsed drawer: the 288px panel, 44px icon-only links, svgX 32, nav 912 vs 744, the reference’s own blur(4px) + rgba(0,0,0,0.2) DIV backdrop; the reference’s Escape-close absence re-verified as the clone’s documented superset via the GEOMETRY probe — the S62 doctrine, first-probe correct; the stored preference restored to ‘true’ before session close); the Mode-C code review ZERO app-code findings (the static scan clean — zero TODO/FIXME/: any/console.log, the single eval( hit = the documented prose comment in calculator.ts:4, the one dangerouslySetInnerHTML = the documented THEME_BOOT_SCRIPT, the SDK server-only in the 4 AI routes); the standing dep-audit exit 0 with the 2 documented non-production advisories); the registry re-check UNCHANGED (braces 3.0.3 = latest — unfixable upstream; the stable Prisma 7.10.1 fix path re-confirmed — the 7.x stable line still ends at 7.10.1, no newer 7.x shipped, the latest dist-tag still on the Prisma 8 RC with prev = 7.10.1 and the next tag an 8.0.0-rc.10 RC channel; the S56 judgment call re-affirmed: the major upgrade stays the dedicated-session candidate, owner decision, stable at 7.10.1 for an eighth consecutive re-check); scandihaven unchanged (d4789c3). THE CONFIRMATION: the standing suite re-run as the confirmation — GREEN 35/35, ZERO total findings, ZERO incidents on run 1 (the S31 preflight, the 33 standing audits, the theme-restore closing guard light/violet; every row findings: 0; failed: [], aborted: false) + the fresh evidence captures (the 35-row envelope s63-standing-suite-evidence.json + the 5 dev-server screenshots [gated on the content element per the S59 capture lesson] + the 2 reference captures [the desktop expanded gated on the CTA verified in-frame at x1093/y46/155×36]). The capture-delta classification: the S63 set vs the S62 counterparts within the documented clock/data family (the same-session greeting bucket + calendar day — no bucket or day crossing between S62 and S63 captures; the live clocks the only movers; classified per the S59–S62 evidence discipline). Docs aligned: the session-63 remediation plan (the findings table + the executed ToDo list + the execution log), session_177 (narrative) + session_178 (Chinese summary), the README session-63 row, the worklog entry. The count anchors (1133/834/299) untouched — zero code changes ✅ Complete docs/remediation-plan-session63.md
Session-64 the S63+1 stability confirmation — the standing arrival audit on the S63-closed codebase (three doc-only commits since: 09d3fba = the S63 evidence commit [the plan + the 35-row envelope + 7 screenshots + session_177/178 + the README row + the worklog entry]; 65d3ae1 = session_179, the S63 process log, + the worklog note; 117f23b = session_180, +94, the owner’s S63 conversation transcript; all verified by git show --stat) read ZERO findings on every axis → zero code changes by design (the S53/S54/S56/S58/S59/S60/S61/S62/S63 precedent: the smallest safe diff is no diff on a verified-green production codebase). The baseline gates re-confirmed byte-for-byte: lint ✓ tsc ✓ 834 unit ✓ (20 files, 3.58 s) build ✓ cold-db e2e 299/299 (4.5 min) = 1133 green. The standing checks all GREEN: the preflight + drawer-check; the live agent-browser 390×844 walkthrough (login → scrollWidth 390 → sidebar hidden → burger visible [the visibility-filtered locator: aria-label “Open navigation menu”, offsetParent !== null] → the drawer 20 links in the reference order → nav scrollable 1068 vs 755 → the active link violet-600 rgb(124,58,237) + the violet→indigo 10% gradient on the .sf-nav-active label-wrapper span + the trailing violet-500 dot rgb(139,92,246) 6×6px → Notes via the drawer with auto-close → Escape-close) — the mobile navigation menu is working as expected; NO Tailwind v4 bug found (traps 4/7/9 verified live clean: the nav UL display flex / gap 4px / column / zero margin children; the backdrop backdrop-filter: blur(4px) — probed on the backdrop-filter property per the v4 notched-blur contract; bg-black/20 serializing oklab(0 0 0 / 0.2) — the documented v4 form); both desktop sidebar states + the shared mobile drawer state measured to the pin (expanded 260/260/1020; collapsed 80/80/1200 with 20 icon-only links svgX 33 [the x-position probe per the S52 protocol], the glyph rotate: 180deg, sf-sidebar-collapsed persisted; the drawer sharing: the 288px panel, 44px icon-only links, svgX 32, nav 988 vs 755, all 20 links aria-named — the clone’s accessible-name superset); the reference re-swept UNCHANGED since S52–S63 (the four anchors — the title, the hour-3 ‘Good morning’ greeting [the morning bucket 0–11, correct per the bucket map], the 20-view href order byte-identical, the five Settings tabs — plus BOTH sidebar states re-measured per the protocol, plus the shared collapsed drawer: the 288px panel, 44px icon-only links, svgX 32, nav 912 vs 744, the reference’s own blur(4px) + rgba(0,0,0,0.2) DIV backdrop; the reference’s Escape-close absence re-verified as the clone’s documented superset via the GEOMETRY probe — the S62 doctrine, first-probe correct; the stored preference restored to ‘true’ before session close); the Mode-C code review ZERO app-code findings (the static scan clean — zero TODO/FIXME/: any/console.log, the single eval( hit = the documented prose comment in calculator.ts:4, the one dangerouslySetInnerHTML = the documented THEME_BOOT_SCRIPT, the SDK server-only in the 4 AI routes); the standing dep-audit exit 0 with the 2 documented non-production advisories); the registry re-check UNCHANGED (braces 3.0.3 = latest — unfixable upstream; the stable Prisma 7.10.1 fix path re-confirmed — the 7.x stable line still ends at 7.10.1, no newer 7.x shipped, the latest dist-tag still on the Prisma 8 RC with prev = 7.10.1 and the next tag an 8.0.0-rc.10 RC channel; the S56 judgment call re-affirmed: the major upgrade stays the dedicated-session candidate, owner decision, stable at 7.10.1 for a ninth consecutive re-check); scandihaven unchanged (d4789c3). THE CONFIRMATION: the standing suite re-run as the confirmation — GREEN 35/35, ZERO total findings, ZERO incidents on run 1 (the S31 preflight, the 33 standing audits, the theme-restore closing guard light/violet; every row findings: 0; failed: [], aborted: false) + the fresh evidence captures (the 35-row envelope s64-standing-suite-evidence.json + the 5 dev-server screenshots [gated on the content element per the S59 capture lesson] + the 2 reference captures [the desktop expanded gated on the CTA verified in-frame at x1093/y46/155×36]). The capture-delta classification: the S64 set vs the S63 counterparts within the documented clock/data family (the same-session greeting bucket + calendar day — no bucket or day crossing between S63 and S64 captures; the live clocks the only movers; classified per the S59–S63 evidence discipline). Docs aligned: the session-64 remediation plan (the findings table + the executed ToDo list + the execution log), session_181 (narrative) + session_182 (Chinese summary), the README session-64 row, the worklog entry. The count anchors (1133/834/299) untouched — zero code changes ✅ Complete docs/remediation-plan-session64.md
Docs (README / AGENTS / CLAUDE / PAD / SKILL) ✅ Complete repo root

No license file is present in this repository; treat the code as proprietary to the repo owner.

View this README on GitHub

Recommended Tools

Try a different keyword or remove a filter.

Install

npx skillfish add nordeim/omni-study