From 66865bc5ccb8f8979911791e5ab362c43f02d3b8 Mon Sep 17 00:00:00 2001 From: Matt Batchelder Date: Thu, 10 Sep 2026 09:54:40 -0400 Subject: [PATCH] Remove internal planning/deploy docs from public repo --- DEPLOY.md | 77 -- docs/internal/BASELINE.md | 100 -- docs/internal/PLAN.md | 67 - docs/internal/TEST-REPORT.md | 252 ---- docs/internal/migration-baseline-schema.sql | 1372 ------------------- 5 files changed, 1868 deletions(-) delete mode 100644 DEPLOY.md delete mode 100644 docs/internal/BASELINE.md delete mode 100644 docs/internal/PLAN.md delete mode 100644 docs/internal/TEST-REPORT.md delete mode 100644 docs/internal/migration-baseline-schema.sql diff --git a/DEPLOY.md b/DEPLOY.md deleted file mode 100644 index 7ee8f30..0000000 --- a/DEPLOY.md +++ /dev/null @@ -1,77 +0,0 @@ -# Project E — Deploy Guide - -Production runs as a docker-compose stack on the deploy host (`10.0.0.52`). The stack has four services: PostgreSQL, the Hono API, the Vite SPA served by Caddy, and the Bun worker. **Komodo now owns builds and deploys** — Gitea Actions runs quality+e2e only. - -## Architecture - -``` -Internet → :3000 (SPA container, Caddy) - ├── /api/* → api:3000 (Hono/Bun) - ├── /mcp* → api:3000 (Hono/Bun) - └── /* → index.html (SPA fallback) - -API (:3001, direct) → PostgreSQL (:5432) -Worker → PostgreSQL -``` - -Caddy (in the `spa` container) serves the built SPA and reverse-proxies `/api/*` and `/mcp*` to the `api` service. The API is also exposed directly on :3001 for debugging. - -## Services - -| Service | Container | Image | Ports | Notes | -|---------|-----------|-------|-------|-------| -| `db` | `project-e-db` | `postgres:16-alpine` | 5432 | Data in `project-e-pg-data` volume | -| `api` | `project-e-api` | `git.buzzbee.dev/BuzzbeeSCD/projecte-api:{tag}` | 3001 → 3000 | Hono on Bun | -| `spa` | `project-e-spa` | `git.buzzbee.dev/BuzzbeeSCD/projecte-spa:{tag}` | 3000 → 80 | Built Vite SPA + Caddy | -| `worker` | `project-e-worker` | `git.buzzbee.dev/BuzzbeeSCD/projecte-worker:{tag}` | none | Bun worker | - -All services share the `project-e-network` bridge and restart unless stopped. - -## CI/CD pipeline - -### Gitea Actions (quality + e2e only) - -The workflow lives at `.gitea/workflows/ci.yml` and runs on the self-hosted runner `projecte-runner`. It runs only quality checks and e2e tests: - -- **`quality`** — runs on every push and PR: typecheck → web build → docker compose build -- **`e2e`** — runs after quality: ephemeral Postgres → db:migrate → Playwright tests - -### Komodo (build + deploy) - -Komodo manages image builds and stack deploys: - -- **Build trigger:** push a `v*` tag → Gitea webhook fires → Komodo builds images → pushes to Gitea registry -- **Deploy trigger:** Komodo procedure `release-projecte` builds all three images, then deploys the stack on `10.0.0.52` -- **Webhook URL:** `https://komodo.example.com/listener/github/repo/{id}/build` -- **Rollback:** re-deploy a previous version tag via Komodo UI or API - -## Deploying - -### Primary: Komodo - -1. Push a version tag: `git tag v1.0.0 && git push origin v1.0.0` -2. Komodo builds images and deploys automatically -3. Verify: `docker ps` on .52, health checks - -### Break-glass: deploy.sh - -If Komodo is unavailable, `script/deploy.sh` still works: - -```bash -ssh projecte -cd /opt/app/ProjectE -export PROJECTE_IMAGE_TAG=v1.0.0 -bash script/deploy.sh -``` - -This pulls images from the registry and redeploys. It no longer builds — that's Komodo's job. - -## Rollback - -1. In Komodo UI: Deployments → projecte → select previous version tag → Redeploy -2. Or via API: `POST /execute/DeployStack` with the previous image tag -3. Verify health checks pass - -## Secrets - -Komodo manages runtime secrets (POSTGRES_PASSWORD, AUTH_SECRET, etc.) as Komodo variables. The host `.env` is kept as break-glass fallback only. diff --git a/docs/internal/BASELINE.md b/docs/internal/BASELINE.md deleted file mode 100644 index e276119..0000000 --- a/docs/internal/BASELINE.md +++ /dev/null @@ -1,100 +0,0 @@ -# Project E — UI Redesign Baseline - -**Date:** 2026-07-31 -**Server hostname:** `projecte` (10.0.0.204) -**Branch:** `redesign/ui-v2` (from `integration/ux-28-gaps`, commit `c1e0a08`) -**Bun version:** 1.3.14 -**DB schema snapshot:** `.migration-baseline-schema.sql` (1372 lines) - -## Disk before cleanup -- 20G total, 11G used, 9.1G free -- `node_modules`: 415M (root) + 294K (apps/web) -- `.next` build outputs: 109M -- Total repo size: 575M - -## Disk after cleanup -- 20G total, 12G used, 9.0G free -- Repo size: 48M (node_modules, .next, tsbuildinfo removed) -- ~527 MB freed from build artifacts - -## Container state after T0 -| Container | Status | -|-----------|--------| -| `project-e-db` | Running (healthy) — PostgreSQL 16 | -| `project-e-worker` | Running — Node.js worker | -| `project-e-web` | **Stopped & removed** — Next.js (big-bang per user decision) | - -## URL status -- http://10.0.0.204:3000 — **DOWN** (until Phase 7 deploys the new SPA) - -## What T1 (Phase 1 Scaffold) needs to do next -1. Create the Vite SPA scaffold in `apps/web/` (replacing the old Next.js app) -2. Create the Hono API scaffold in `apps/api/` -3. Set up Bun workspace in root `package.json` -4. Wire up the new `docker-compose.yml` for the new stack -5. Reference `.migration-baseline-schema.sql` for the DB schema the new API must preserve -6. Use `bun` (v1.3.14) for all package management and runtime -7. Keep `db` and `worker` containers running - -## T1 — Phase 1 Scaffold (completed) - -### Legacy code moved -- Old Next.js `apps/web/` → `apps/web-legacy/` (preserved for T2-T8 reference) - -### New apps created -| App | Path | Tech | Port | -|-----|------|------|------| -| SPA | `apps/web/` | Vite 5 + React 19 + TanStack Router/Query + shadcn/ui | :3000 (dev) | -| API | `apps/api/` | Hono 4 + Bun | :3001 | -| Worker | `apps/worker/` | Bun + Drizzle | — | - -### Shared infrastructure -- `db/client.ts` — shared Drizzle client (postgres-js driver) -- `db/` — root-level directory (shared by api + worker) -- `bunfig.toml` — Bun package manager config -- Root `package.json` — Bun workspaces monorepo (`apps/*`, `packages/*`) - -### Docker -- `Dockerfile.api` — Bun base, copies apps/api + db + packages -- `Dockerfile.worker` — Bun base, copies apps/worker + db + packages -- `Dockerfile.spa` — Multi-stage: Bun build → Caddy serve -- `Caddyfile` — Serves dist on :80, reverse-proxies /api/* and /mcp to api:3000 -- `docker-compose.yml` — 4 services: db, api, spa, worker (old web service removed) - -## Phase 7 — Deploy (completed) - -**Deploy time:** 2026-08-01T02:40Z -**Commit:** `fc59431140329bd93223dfae3c72406d9e02646e` -**Branch:** `redesign/ui-v2` - -### Image SHAs -| Image | SHA256 | -|-------|--------| -| `projecte-api` | `fc46878d1618bb3a0d6a43d645a21cad14caa1bb6eaf773e4e32579a268adc10` | -| `projecte-spa` | `1cced4e61fb35d31df53225b3c8f5863a3d581b82ed961c13286bd5e3f940f48` | -| `projecte-worker` | `7fc57170100f6cf6b12439835f5700383c7c4570eecf3c4ab3f06fa739d10d07` | - -### Compose file SHA -`bb21417829c866048509f81881304e81b5e29430270a278e0b3a77caa68f8b1b` - -### Verification results -| Check | Result | -|-------|--------| -| `docker compose ps` — all 4 services running, db healthy | ✅ | -| `curl :3000/api/health` — 200 + `{"status":"ok"}` | ✅ | -| `curl :3000/` — 200 + HTML | ✅ | -| Login (user@example.com) — token received | ✅ | -| `/api/domains` — 2 domains returned | ✅ | -| `/mcp` — endpoint reachable (requires API key) | ✅ | -| All 13 page routes return 200 | ✅ (13/13) | - -### Container state -| Container | Status | Ports | -|-----------|--------|-------| -| `project-e-db` | Running (healthy) | :5432 | -| `project-e-api` | Running | :3001 | -| `project-e-spa` | Running | :3000 | -| `project-e-worker` | Running | — | - -### URL -- http://10.0.0.204:3000 — **LIVE** (SPA served by Caddy, API reverse-proxied) diff --git a/docs/internal/PLAN.md b/docs/internal/PLAN.md deleted file mode 100644 index f69750f..0000000 --- a/docs/internal/PLAN.md +++ /dev/null @@ -1,67 +0,0 @@ -Project E — Full Review, Functional Completion & CI/CD Plan -What we know -Actual stack: Vite + React 19 + TanStack Router/Query + shadcn/ui SPA (apps/web) · Hono/Bun API (apps/api) · Bun worker · Drizzle/Postgres. README/AGENTS.md describe a Next.js app that no longer exists — docs are stale. -Deployment: docker-compose on the projecte host (10.0.0.204): db, api, spa (Caddy), worker. Secrets come from a host .env (already/soon exists). Remote = git.buzzbee.dev (Gitea). -CI/CD: No workflows exist yet. Will add Gitea Actions (.gitea/workflows/) using runner label projecte-runner, deploy = docker compose on the same host, triggered by push to main + manual dispatch. -Phase 0 — CI/CD foundation (do first, so everything after ships through it) -.gitea/workflows/ci.yml with two jobs: -quality (every push + PR): bun install --frozen-lockfile → typecheck (api + worker + web) → vite build → docker compose build (catches Dockerfile breakage early). -deploy (push to main + workflow_dispatch): docker compose build → apply migrations → docker compose up -d → health checks (SPA 200, /api/health DB ping, login smoke via POST /api/auth/credentials). -Deploy job runs on projecte-runner; uses host .env for secrets; keeps previous image tags for rollback. -Migration story — currently broken: db:push doesn't apply drizzle/*.sql, README references a non-existent drizzle/0000_postgres.sql, and 0005 is hand-written. Reconcile: verify the 5 migration files reproduce the .migration-baseline-schema.sql schema, make a single idempotent db:migrate script, and call it in the deploy job before up -d. -Docs refresh: rewrite README, AGENTS.md, DEPLOY.md to describe the real stack, deploy flow, and Gitea Actions pipeline. -Conventions to fix first (they gate everything): implement requireWorkspaceAccess() per AGENTS.md, a shared recordActivity wrapper that always derives workspaceId correctly, and a shared error helper — then phases 1–4 build on them. -Phase 1 — Critical functional fixes (app must stop lying) -# Fix Where -1 Calendar delete — useApiMutation("delete", "", …) → DELETE /api 404. Use DELETE /calendar/events/{id} + wire drag/resize handlers + enable withDragAndDrop calendar.tsx:173 -2 Canvas persistence — blocks are local state; handleSave sends only {name}. Add canvas-card CRUD endpoints (API), persist blocks (content/type/order), load on open, save on debounce, route detail page through it canvas.tsx:243, API routes/canvas.ts -3 Logout — no POST /api/auth/logout; cookie never cleared. Add route that clears the session cookie; client already calls it in 3 places API routes/auth.ts, sidebar.tsx -4 Graph edge write/delete 500 — recordActivity({ workspaceId: "" }) → invalid uuid. Derive domain from the edge API routes/graph.ts:150,190 -5 Analytics fake data — replace Math.random() with real endpoints; fix /analytics/habits domain scoping; make /analytics/projects real; real CSV export analytics.tsx:137, API routes/analytics.ts -6 Task board DnD — add droppable columns, persist status change + order via API tasks.tsx:215 -7 Notes editor — wire installed Tiptap (replace contentEditable): autosave debounce, placeholder fix, stop stopPropagation killing shortcuts, render HTML on detail page (use editor output, not raw innerHTML text) notes.tsx, notes/$id.tsx -8 Passkey security hole — authenticates without verifying signature. Either implement real WebAuthn verification or remove the passkey surface API routes/auth.ts:115 -9 Domain/owner scoping (IDOR) — add owner/domain checks to all by-ID GET/PATCH/DELETE routes; scope /api/search and /api/export to active domain all API routes -10 Command palette routing — singular vs plural types → non-existent /search/{id}. Fix type→route map; fix @mention agent fetch (response is {items}, q ignored) command-palette.tsx:313 -11 Agent activity SSE handler — wrong event type match + data.payload doesn't exist; would crash on match agents/activity.tsx:54 -12 Graph fly-to — centerAt(undefined, undefined) on raw nodes (no x/y). Track positions via onNodeDrag or search the rendered graph data graph.tsx:157 -Phase 2 — UX & polish pass (current shadcn look, elevated) -Rebuild login on the design system: Input/Button/Label, autofocus, loading/disabled state, error styling, branding mark. (Worst screen in the app today.) -Feedback everywhere: mount Sonner ; add success/error toasts to every create/update/delete mutation; inline error handling on failed mutations. -State coverage: shared LoadingState (skeleton), EmptyState (icon + text + CTA), ErrorState components; apply to every page (calendar, graph, analytics, settings tabs, search, agents…). -Settings honesty: implement density (CSS variable that actually changes spacing), wire sidebarPos to reposition the sidebar, add real reduce-motion CSS — or remove the controls. -Typography: add a real font via --font-sans (self-hosted or Google Fonts) — currently browser-default only. -Consolidate color maps: one shared src/lib/status-colors.ts (status/priority/entity colors as semantic tokens) replacing 8+ divergent copies; make accent theming actually visible. -Unify detail routes (tasks/$id, habits/$id, projects/$id, notes/$id, canvas/$id) with the app design language; make list UIs link to them. -Dashboard: fix sort=dueDate→due_date (widget shows wrong tasks), align realtime invalidation keys with widget query keys, make widget grid responsive. -Responsive: notes/graph/settings panes on mobile, dashboard grid stacking, calendar height. -A11y: aria-labels on icon/scale buttons, radiogroup for mood/energy, fix dangerouslySetInnerHTML snippet XSS surface (sanitize ), notification badge label. -Phase 3 — Secondary feature completion -Canvas: persist block editor (from Phase 1) + real save UX + delete blocks/cards. -Daily notes: timezone bug (UTC midnight stored, local day shown), add DELETE /api/daily-notes/:id, autosave cleanup, mood/energy before content creates the note. -Graph: domain picker (shared with topbar), node detail → navigable entity links. -Analytics: real charts (tasks completed over time, created vs completed, habit consistency, project progress, time per domain, productivity heatmap) + honest empty states + working CSV. -Domains: active-domain selection store + picker; API already supports multi-domain. -Tags: assign/remove tags on tasks/habits/notes from the UI (currently create-only); fix in-memory tag filter after pagination. -Import/Export: scope export to domain (currently exports every user's data), implement advertised CSV format, validate import. -Custom fields: decide and wire to entities, or hide if inert. -Phase 4 — Advanced features (selected; largest phase) -Webhooks pipeline: enqueue webhook_delivery jobs on entity events; fix POST /webhooks/:id/test; worker already knows how to deliver — just wire the queue. -Recurring tasks: create scheduledJobs from recurrenceRule; worker recurring_spawn already exists — wire creation. -Agents: real CRUD UI, permission editing, activity filters (from/to/action honored server-side). -Notifications: real notification count/feed instead of hardcoded 0. -Reports & Milestones: minimal working versions, or remove from nav until built (recommend: build minimal). -MCP polish: validation, correct error codes, domain scoping, drop the dead ?? sql`` `` `` `` leftover. -Worker: wire ai_dispatch or mark it disabled. -Phase 5 — E2E rewrite + docs -Rewrite the Playwright suite (e2e/*.spec.ts) for the new SPA: auth cookie flow, tasks/habits/projects/notes/calendar/canvas/daily/graph/search/analytics/settings/agents-activity; new auth helper; drop specs for removed features (reports/mcp UI). -CI: dedicated ephemeral Postgres + dev servers for E2E, run chromium only in CI (full 5-browser matrix locally/on demand); npx playwright install --with-deps chromium on the runner. -Update docs/API.md to match reality. -Risks & mitigations -Huge scope → sequenced phases; each phase lands on main and deploys independently, so value ships incrementally. -DB migrations on live prod data → Phase 0 makes migrations idempotent + verifiable before any deploy; backup volume (project-e-pg-data) noted in DEPLOY.md. -E2E against prod → E2E runs in CI against an ephemeral test DB, never prod. -Secrets → host .env (gitignored), never committed; Gitea Actions secrets only if needed later. -DnD persistence + Tiptap are the two most invasive frontend changes → done early (Phase 1) so regressions surface in CI before polish. -Verification -Per user: CI + manual testing — quality job gates every push (typecheck/build/docker build); deploy job gates main; you verify on the live site (10.0.0.204:3000) after each deploy. E2E suite (Phase 5) becomes the automated gate once rewritten. diff --git a/docs/internal/TEST-REPORT.md b/docs/internal/TEST-REPORT.md deleted file mode 100644 index 49b6bab..0000000 --- a/docs/internal/TEST-REPORT.md +++ /dev/null @@ -1,252 +0,0 @@ -# Project E — Phase 8 Interactive Test Report - -**Verdict: NEEDS FIXES — 2 CRITICAL + 3 HIGH + 3 MEDIUM bugs must be fixed before production use** - -**Test date:** 2026-08-01 (UTC) -**App URL:** http://10.0.0.204:3000 -**Server:** `hermes@10.0.0.204` (commit `54d788e`, branch `redesign/ui-v2`) -**Tested by:** coding-coder (T10) -**Tester login:** `user@example.com` / `87AwFGzfMuNcitie0g1C` -**Containers up:** project-e-db (healthy), project-e-api, project-e-spa, project-e-worker -**All 15 routes return HTTP 200 at the SPA shell level** (SPA serves index.html for everything; authentication is enforced by the client). - ---- - -## Summary - -| Metric | Value | -|---|---| -| Pages visited | 15 / 15 | -| Interactive elements cataloged | ~140 | -| Operations exercised | ~45 | -| **CRITICAL bugs** | **2** | -| **HIGH bugs** | **3** | -| **MEDIUM bugs** | **3** | -| **LOW bugs** | **2** | -| Bug cards filed | 5 | -| Screenshots saved | 0 (Camofox backend does not write PNGs; see "Limitations" below) | - ---- - -## Pass/Fail per page - -| # | Page | Status | Notes | -|---|---|---|---| -| 1 | `/login` | **FAIL (CRITICAL)** | Form submits, but no auth cookie/token is persisted. Every login attempt loops back to /login. | -| 2 | `/` Dashboard | PASS (with caveat) | Renders, but shows "Unknown widget: stats" for a pre-existing widget of an unsupported type. | -| 3 | `/tasks` | PASS | Board view, list toggle, New task dialog, search, combobox, edit dialog, delete confirm all work. CRUD verified (created + deleted "T10 test task from card"). | -| 4 | `/habits` | PASS | List renders, "Mark complete" increments streak (Debug habit 0→1). | -| 5 | `/projects` | PASS | Card list, progressbar, "New Project" dialog (Name/Description/Status/Color/Target Date). | -| 6 | `/projects/:id` | **FAIL (HIGH)** | Route file missing — every project detail URL renders "Not Found". | -| 7 | `/notes` | PASS | List + split editor; clicking a note loads it in the right pane. Backlinks, Version history, Delete buttons present. | -| 8 | `/calendar` | PASS | Agenda (default), Month, Week, Day, Prev/Today/Next, New Event dialog. Event "v-test" visible across days. | -| 9 | `/graph` | **FAIL (CRITICAL)** | Page loads but graph is empty. SPA hard-codes `?domain=placeholder`, which 500s on the API. | -| 10 | `/search` | **FAIL (HIGH)** | Input + type filters work, but returns 0 results for any query. `search_vector` column is never populated. | -| 11 | `/analytics` | PASS | 6 charts render (Tasks Completed, Created vs Completed, Habit Completion, Project Progress, Time per Domain, Productivity Heatmap). Combobox for date range. | -| 12 | `/agents/activity` | **FAIL (HIGH)** | "All agents" filter calls `/api/agents/_all/activity` which 500s. Specific agent works. | -| 13 | `/canvas` | PASS | "New Canvas" button, list ("v-canvas", 0 blocks freeform). | -| 14 | `/daily` | PASS | Calendar sidebar (Jul 2026), Mood/Energy buttons (1–10), "Start Writing" CTA. | -| 15 | `/settings` | PASS (with caveats) | All 9 tabs render and switch. Appearance tab buttons are decorative — see Bug #6. | - ---- - -## Bugs found - -### BUG #1 — CRITICAL: Login is completely broken (no auth persistence) - -- **Severity:** CRITICAL (blocks use) -- **Page:** `/login` + global -- **What happened:** Filling email + password and clicking "Sign In" does nothing. The page does not transition; refreshing brings back the login form. (Verified manually in browser + curl.) -- **Steps to reproduce:** - 1. `browser_navigate http://10.0.0.204:3000/login` - 2. `browser_type` user@example.com into email textbox - 3. `browser_type` 87AwFGzfMuNcitie0g1C into password textbox - 4. `browser_click` "Sign In" button - 5. Observed: URL stays at `/login`, no error message displayed, dashboard never appears. -- **Expected behavior:** Successful login navigates to `/` (dashboard). -- **Actual behavior:** Login API returns 200 + `{user, token}`, but the response is **not** converted to a `session` cookie or localStorage entry. The browser is then unauthenticated for every subsequent call. -- **Root cause:** - - `apps/api/src/routes/auth.ts` line 44: `authRoutes.post("/credentials", ...)` returns JSON but **does not call `setCookie()`**. The auth middleware in `apps/api/src/middleware/auth.ts:46` reads the `session` cookie via `c.req.header("Cookie")` — but the cookie is never set. - - `apps/web/src/routes/login.tsx:18–30` reads the JSON, only checks `res.ok`, and navigates to `/` without writing the token anywhere. - - `apps/web/src/lib/api.ts:28` uses `credentials: "include"` (so the SPA **expects** the server to set the cookie) but the SPA never falls back to setting `Authorization: Bearer ` from a stored value. -- **Console errors:** Browser console capture unavailable (Camofox backend), but verified by curl that the response has no `Set-Cookie` header and the SPA has no `localStorage.setItem("token", ...)` or `document.cookie = ...` in `login.tsx`. -- **Workaround for the test:** I dropped a static HTML helper at `/usr/share/caddy/auth-helper.html` that sets the cookie from a hard-coded token, then redirects. With that, the rest of the app is fully usable. - -### BUG #2 — CRITICAL: Graph page is empty (hard-coded "placeholder" domain) - -- **Severity:** CRITICAL (core feature completely broken) -- **Page:** `/graph` -- **What happened:** The page loads the controls (search, Filters, Zoom in/out, Reset view) but the canvas is empty. No nodes, no edges. -- **Steps to reproduce:** - 1. Log in - 2. Navigate to `/graph` - 3. Wait — the canvas stays blank indefinitely. -- **Expected behavior:** At least 20 nodes should render (the dataset has 50+ entities). -- **Actual behavior:** `useApiQuery(["graph","nodes"], "/graph/nodes?domain=placeholder")` and `.../edges?domain=placeholder` are sent. The API validates the `domain` parameter and returns `{"error":{"code":"VALIDATION_ERROR","message":"domain parameter is required"}}` (status 400 / 500 depending on code path). -- **Root cause:** `apps/web/src/routes/_app/graph.tsx` line ~30 hard-codes the string `"placeholder"` instead of resolving the active domain from the `useDomainStore` / domain list. The route never received a real domain id. -- **Console errors:** API call returns 500 with "Failed to get graph nodes". UI silently shows an empty canvas. - -### BUG #3 — HIGH: Search returns 0 results for every query - -- **Severity:** HIGH (core feature completely broken) -- **Page:** `/search` (and any UI that uses full-text search) -- **What happened:** Type "test" → "No results found for 'test'". Type "tasks" → same. The dataset has 20 tasks, 8 habits, 6 projects, 7 notes with the word "test" in the title. -- **Steps to reproduce:** - 1. `browser_navigate /search` - 2. `browser_type` "test" into the search textbox - 3. Observed: "No results found for 'test'" -- **Expected behavior:** Returns tasks/notes/etc. that match. -- **Actual behavior:** `/api/search?q=test` returns `{"results":[],"totalCount":0,"query":"test"}`. -- **Root cause:** The Postgres `search_vector tsvector` column is **never populated**. `docker exec project-e-db psql -U project_e -d project_e -c "SELECT count(*), count(search_vector) AS with_vec FROM tasks"` shows `0 of 20` tasks have a vector. There is no Drizzle migration that creates a trigger or GENERATED column to populate the vector on INSERT/UPDATE. -- **Fix:** Add a Postgres trigger (BEFORE INSERT OR UPDATE) that sets `NEW.search_vector = to_tsvector('english', coalesce(NEW.title,'') || ' ' || coalesce(NEW.description,''))` for each searchable table, then backfill existing rows. - -### BUG #4 — HIGH: Project detail page does not exist - -- **Severity:** HIGH (one of the spec'd 7 core entity detail pages is missing) -- **Page:** `/projects/:id` -- **What happened:** Any project detail URL renders a 404 "Not Found" page. -- **Steps to reproduce:** - 1. From the projects list, click any project card - 2. Observed: page shows "Not Found" -- **Expected behavior:** Project detail page with Overview, Tasks, Sections, Members, Notes, Activity tabs (per the Phase 8 test plan). -- **Actual behavior:** The `apps/web/src/routes/_app/projects/` directory exists but is empty — no `[id].tsx` route file. T6's PR claim "7 core entity pages built" was inaccurate for the detail view. -- **Fix:** Add `apps/web/src/routes/_app/projects/$id.tsx` (TanStack Router file-based or programmatic). Same likely true for `/tasks/:id`, `/habits/:id`, `/notes/:id`, `/canvas/:id` — only the list pages exist. - -### BUG #5 — HIGH: "All agents" filter on Agent Activity page returns 500 - -- **Severity:** HIGH (default filter on the page is broken) -- **Page:** `/agents/activity` -- **What happened:** The "All agents" filter (which is the default) calls `/api/agents/_all/activity` and gets a 500, leaving the page stuck on "Loading activity...". -- **Steps to reproduce:** - 1. Log in - 2. `browser_navigate /agents/activity` - 3. Observed: spinner "Loading activity..." stays forever; API call returned 500. -- **Expected behavior:** Should show all agents' activity. -- **Actual behavior:** The SPA passes the literal string `_all` as the agent id. The API route at `apps/api/src/routes/agents.ts:180` does `db.select().from(agentActivity).where(eq(agentActivity.agentId, id))` — there's no `_all` sentinel handling; passing `_all` to Postgres fails on the UUID cast. -- **Fix:** Either (a) make the SPA filter out the `_all` case and call a new endpoint `/api/agents/_all/activity` (or a `?agentId=` query param), or (b) add a server-side branch that skips the `WHERE` when `id === "_all"`. - -### BUG #6 — MEDIUM: Two parallel theme systems fight each other in Settings - -- **Severity:** MEDIUM (theme button is decorative, doesn't sync across tabs) -- **Page:** `/settings` (Appearance tab) -- **What happened:** The Light/Dark/System buttons in settings appear to work (the active border updates), but they're driven by a local `useState` in `settings.tsx` that is never wired to the rest of the app. A `useThemeStore` (Zustand) in `lib/stores/use-theme-store.ts` is what `theme-provider.tsx` actually reads from, but no UI writes to it. -- **Steps to reproduce:** - 1. Log in, open `/settings`, click "Light" - 2. Open a new tab → page still renders as dark - 3. Open the command palette (Cmd+K) and click "Switch to light mode" → that does work (it writes to Zustand) - 4. Go back to Settings → "Dark" is highlighted again (because settings re-reads from `localStorage.getItem("theme")` on mount) -- **Expected behavior:** One theme system, consistent across all entry points. -- **Actual behavior:** Two parallel state stores. Settings' buttons write to `localStorage` + `document.documentElement.className`; the command palette writes to Zustand. They don't sync. -- **Fix:** Make the Appearance tab use the Zustand store directly, or remove the Zustand store and migrate everything to the `localStorage` + `classList` approach. - -### BUG #7 — MEDIUM: Dashboard has a stale "Unknown widget: stats" - -- **Severity:** MEDIUM (cosmetic, easy to fix) -- **Page:** `/` (Dashboard) -- **What happened:** The dashboard shows a card titled "v" with body "Unknown widget: stats". It's a widget that exists in the DB but the SPA's `WIDGET_TYPES` array (in `_app/index.tsx`) doesn't include a renderer for `type: "stats"`. -- **Steps to reproduce:** - 1. Log in, land on `/` - 2. Observed: widget card "v" with "Unknown widget: stats" message -- **Expected behavior:** Either the widget renders something meaningful, or it's removed from the default seed. -- **Actual behavior:** Pre-existing widget from earlier phases that doesn't have a renderer. User can hit the "Remove widget" button (e16) to delete it. -- **Fix:** Add a "stats" widget renderer, or remove the widget from the seed/migration. - -### BUG #8 — MEDIUM: `/api/agents/activity` (no id) returns 500 - -- **Severity:** MEDIUM -- **Page:** API (called by various agent-activity UIs) -- **What happened:** `GET /api/agents/activity` returns `{"error":{"code":"INTERNAL_ERROR","message":"Failed to get agent"}}` (500). The route was renamed to `:id/activity` but the bare `/activity` path was never added. -- **Steps to reproduce:** - - `curl -H "Authorization: Bearer …" http://10.0.0.204:3000/api/agents/activity` → 500 -- **Expected behavior:** 404 with a helpful message, or 200 with all agents' activity. -- **Actual behavior:** 500 "Failed to get agent" — the bare path falls into the `/:id/activity` handler with `id === "activity"`, which fails the UUID lookup. - -### BUG #9 — LOW: Page-level console errors not capturable - -- **Severity:** LOW (test infrastructure, not app) -- **Note:** The Camofox browser backend used by the test agent does not persist console.log / JS errors (`browser_console` always returns 0 messages). The agent had to rely on direct API curl + DOM snapshots to characterize behavior. This is not a bug in the app under test. - -### BUG #10 — LOW: 7 entity detail routes missing - -- **Severity:** LOW (consistency) -- **Note:** In addition to the missing `/projects/:id` (Bug #4), the route files for `/tasks/:id`, `/habits/:id`, `/notes/:id`, `/canvas/:id` are also absent. The current UX uses in-page side panels for editing (which works) so detail pages are a polish item rather than a blocker — except for `/projects/:id`, which the Phase 8 spec lists as a distinct page. - ---- - -## Performance observations - -- **Initial page loads:** All 15 pages return HTTP 200 (HTML) in <300ms via Caddy. The SPA bundle is loaded once and routes are client-side; navigation between pages is instant after the initial load. -- **API response times:** Health endpoint 1–2ms; list endpoints 20–100ms with 20–50 rows. -- **No jank observed** during interactions (New Task dialog, New Project dialog, sidebar drawer, command palette). -- **Analytics page** renders 6 charts simultaneously without lag. -- **The auth-helper workaround** is the only thing slowing down the test; with a working login flow, an end-to-end test would feel snappy. - ---- - -## Accessibility observations - -- **Keyboard nav:** Tab order is reasonable on every tested page. Dialogs trap focus correctly (New Task, New Project, New Event, New Canvas, etc.). -- **Shortcuts help:** `?` opens a well-structured dialog with all shortcuts listed (works). -- **Command palette:** `Cmd+K` works (via clicking the topbar button); cmdk supports arrow keys, Enter to select, Esc to close. -- **Concerns:** - - Theme buttons in Settings are `