Remove internal planning/deploy docs from public repo
This commit is contained in:
@@ -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)
|
||||
@@ -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 <Toaster/>; 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 <mark>), 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.
|
||||
@@ -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 <token>` 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 `<button>` elements with no `aria-pressed` / `aria-checked` — a screen reader can't tell which theme is active.
|
||||
- The "Notifications" topbar button shows `"0"` as plain text; no `aria-label="0 notifications"`.
|
||||
- Mood/Energy buttons in Daily Notes are a 1–10 grid with no group label visible (the heading "Mood" / "Energy" is there but no `role="radiogroup"`).
|
||||
|
||||
---
|
||||
|
||||
## Verdict
|
||||
|
||||
**NOT production-ready as a single-user primary tool** — the login flow is fundamentally broken, which means a real user (who can't deploy an `auth-helper.html` to bypass it) cannot get past the login screen. The agent activity "all" filter, the graph, and full-text search are also broken, but those are addressable on a per-page basis.
|
||||
|
||||
### Top 3 must-fix items
|
||||
1. **Fix login persistence** (Bug #1). Server must `setCookie('session', token, {httpOnly, secure, sameSite, path, maxAge})` on `POST /api/auth/credentials`, OR the login page must write the token to `localStorage` and the `api.ts` must read it and set `Authorization: Bearer <token>` on every call. Without this, the app is unusable.
|
||||
2. **Populate `search_vector`** (Bug #3). Add a Postgres trigger (or Drizzle-generated column) on `tasks`/`notes`/`projects`/`habits` that calls `to_tsvector('english', title || ' ' || description)` on INSERT/UPDATE, then backfill existing rows.
|
||||
3. **Resolve the graph's domain id** (Bug #2). Replace the hard-coded `?domain=placeholder` with `useDomainStore.getState().activeDomainId` (or fetch the first domain from `/api/domains`).
|
||||
|
||||
### What works well
|
||||
- 13/15 pages render correctly.
|
||||
- Tasks CRUD end-to-end is solid (create / edit / delete via dialogs).
|
||||
- Habits completion + streak display works.
|
||||
- Calendar (agenda/month/week/day) renders events.
|
||||
- Analytics renders 6 distinct charts.
|
||||
- Settings has 9 working tabs.
|
||||
- Command palette is well-built.
|
||||
- Keyboard shortcuts (g+t, ?, Cmd+K) all work.
|
||||
- The shell (sidebar, topbar) is consistent and well-organized.
|
||||
|
||||
---
|
||||
|
||||
## Limitations of this test
|
||||
|
||||
1. **No screenshots saved.** The Camofox browser backend does not persist PNGs (only the agent's own session-cache screenshots are kept, not committed). The test relied on browser_snapshot accessibility trees and direct DOM observation. The user can re-run any of these flows in a real browser to see what the agent saw.
|
||||
2. **No JS console capture.** Browser console is unavailable in this environment; the agent had to infer errors from API responses and DOM state. The user should open DevTools and re-run a couple of flows to confirm there are no client-side exceptions.
|
||||
3. **Realtime SSE** was not exhaustively tested (no two-tab verification of the same session). The `/api/realtime` endpoint and `use-realtime` hook are wired into several pages, but a side-by-side test was out of scope.
|
||||
4. **The auth-helper workaround** was needed to bypass Bug #1. The agent did NOT modify production code; the helper was placed in a one-time static file inside the running `project-e-spa` container and will be removed by the next container restart.
|
||||
|
||||
---
|
||||
|
||||
## Repro: how to verify the verdict yourself
|
||||
|
||||
```bash
|
||||
# 1. Confirm the app is up
|
||||
curl -s http://10.0.0.204:3000/api/health
|
||||
# → {"status":"ok","database":{"connected":true,"ping_ms":2},...}
|
||||
|
||||
# 2. Verify the login response is missing Set-Cookie
|
||||
curl -i -X POST http://10.0.0.204:3000/api/auth/credentials \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"email":"user@example.com","password":"87AwFGzfMuNcitie0g1C"}'
|
||||
# → no Set-Cookie header; only {"user":{...},"token":"..."}
|
||||
|
||||
# 3. Verify search is broken
|
||||
TOKEN=$(curl -s -X POST http://10.0.0.204:3000/api/auth/credentials \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"email":"user@example.com","password":"87AwFGzfMuNcitie0g1C"}' | python3 -c "import sys,json; print(json.load(sys.stdin)['token'])")
|
||||
curl -s -H "Authorization: Bearer $TOKEN" "http://10.0.0.204:3000/api/search?q=test"
|
||||
# → {"results":[],"totalCount":0,"query":"test"}
|
||||
|
||||
# 4. Verify graph is broken (with auth)
|
||||
curl -s -H "Authorization: Bearer $TOKEN" "http://10.0.0.204:3000/api/graph/nodes?domain=placeholder"
|
||||
# → {"error":{"code":"INTERNAL_ERROR","message":"Failed to get graph nodes"}}
|
||||
```
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user