- j/k keyboard navigation in task list view with visual selection ring
- Enter to open task, e for inline title edit, x to toggle complete
- Shift+J/K to reorder tasks in list view
- Bulk selection via Shift+Click/Cmd+Click with checkbox column
- BulkActionBar: complete, set priority, set state, add tag, delete
- Calendar arrow key navigation, t=today, n=new event
- kbd hint legend on first load
- Convert _app/index.tsx from dashboard page to app layout shell
- Render Sidebar, Topbar, CommandPalette, ShortcutsHelp, and Outlet
- Extract DashboardPage into _app/dashboard.tsx as child route
- Update routeTree import to point to new dashboard file
Commit 2175c24 deleted apps/web/src/routes/_app.tsx alongside __root.tsx.
Only __root was restored (28b2cbd). With _app.tsx missing, routeTree.ts's
import './routes/_app' resolved to the _app/ directory index (dashboard
route), making the route its own child — TanStack buildRouteTree throws
'Invariant failed' at module init and React never mounts (blank page, no
console error). Restores the exact file: _app pathless layout with
Sidebar/Topbar/Outlet.
PL-7 — Task Board Columns from States:
- Add State, Module, Cycle, Link TypeScript types to types/index.ts
- Add stateId, moduleId, cycleId, trackedMinutes to Task type
- Rewrite tasks.tsx: fetch states from API, render 5 state-group columns
(backlog/unstarted/started/completed/cancelled), drag-and-drop updates
stateId via PATCH /tasks/:id, colored state badges, state filter dropdown,
project filter
- Fix deprecated POST /tasks/:id/status → PATCH /tasks/:id with stateId
- Update tasks/.tsx: state selector dropdown replaces hardcoded status enum,
toggle complete uses state-based approach, dependencies replaced with
link-based UI using /api/links
PL-8 — Module + Cycle Views:
- Create apps/api/src/routes/cycles.ts: full CRUD + task assignment/removal
- Create apps/api/src/routes/links.ts: list/create/delete links between entities
- Register cycleRoutes and linkRoutes in API index
- Add Modules tab to project detail: list modules, expand to show tasks,
add/remove tasks from modules, create/edit/delete module dialogs
- Add Cycles tab to project detail: sprint board grid, backlog lane,
manual task transfer between cycles and backlog, create/edit cycle dialogs
- Fix ProjectTasks toggle to use PATCH with stateId instead of deprecated endpoint
PL-9 — Link Panels + Graph:
- Update graph API to read links bidirectionally (source OR target)
- Add link type color map (LINK_TYPE_COLORS) for edge rendering
- Graph edges now colored by linkType (blocks=red, relates=gray, etc.)
- Filter panel shows link types with color indicators
- Task detail Dependencies tab now uses /api/links for add/remove links
- Added link type selector (blocks/relates/parent-child/created-from)
- tasks.list: replace status filter with state_group (backlog/unstarted/started/completed/cancelled)
- tasks.create: remove obsolete status param (tasks now use state_id FK)
- tasks.update: remove obsolete status param
- Implement state_group filtering via EXISTS subquery on states table
- Update API.md example to use state_group instead of status
The dashboard route imported Route from '../_app', which resolves to itself
(_app/index.tsx IS the _app route), creating a self-referencing circular
type inference (TS7022/TS7023).
Fix by importing the root route from '__root' as the parent, which is the
correct ancestor in the route tree hierarchy.
- Add states.ts with GET/POST/PATCH/DELETE endpoints
- States scoped to project via projectId FK; workspace access resolved through project
- stateGroupEnum enforced (backlog|unstarted|started|completed|cancelled)
- List ordered by sortOrder, auto-increment on create
- Soft-delete via deletedAt column (added to schema)
- 3-step contract on every write: DB write, activity feed, pg_notify
- Register /api/states route in main index.ts
- Default state seeding already present in projects.ts
Replace flat status enum with stateId/stateGroup references:
- Import states table; add state_group filter via EXISTS subquery
- Add module_id and cycle_id filter params to task list endpoint
- Validate stateId on create/update (404 if state not found)
- Auto-set completedAt when state group is 'completed', clear otherwise
- Zero references to old taskStatusEnum remain
- Custom workflow statuses: per-project configurable status definitions
replacing the fixed task_status enum. Each project defines its own
workflow with drag-to-reorder, color coding, and category mapping.
- Gantt/timeline view: full project roadmap with task bars, dependency
arrows, milestone diamonds, zoom levels (day/week/month), and drag
to reschedule.
- Natural-language quick-add: NLP parser extracts dates, priorities,
projects, labels, and recurrence from free text. Floating bar with
'n' shortcut and live parsed preview.
- Automation rules: no-code trigger-action system per project. Triggers
on status change, task creation, due date approaching. Actions set
status/priority, add labels, create notifications.
- Notification center: in-app bell icon with unread badge, slide-out
panel, real-time SSE updates, mark read/all read. Replaces raw
activity feed dropdown.
Schema: adds status_definitions, automation_rules, notifications tables.
Migrations: 0007, 0008, 0009. 41 NLP parser tests pass.
Resolved conflicts in web-legacy pages and report schema by taking v2 side.
v2 is the deployed, current architecture; v1 paths preserved under apps/web-legacy.
- Add rose accent to ACCENT_PALETTE
- Settings AppearanceTab derives ACCENT_COLORS from ACCENT_PALETTE
- Wire accent state through useThemeStore instead of localStorage
The generateWeeklySummary/generateProjectHealth/generateHabitAnalysis/
generateTimeAudit helpers all had "const pb = token ? createAdminClient()
: createAdminClient();" — both branches return the admin client, so a
passed user token was silently discarded and every caller got admin-level
access. Use createPocketBaseClient(token) when a token is provided,
matching the sibling project-service.ts / note-service.ts pattern.
projectIds query omitted isNull(projects.deletedAt), unlike the sibling
projectRows query, so sections of soft-deleted projects were loaded as
orphan nodes via inArray(sections.projectId, projectIds).
GET/PATCH/DELETE for single task and habit used createPocketBaseClient(),
which is a dead stub, and DELETE did a hard delete violating the soft-delete
rule in AGENTS.md. Rewrite with Drizzle ORM: soft-delete (deleted_at),
activity feed insert via recordActivity(), Zod validation, parent-task
existence check, workspace access check, and 404 NOT_FOUND handling.
The dashboard widget was filtering with a broken filter=status!=done query
param that the list API never interpreted, so completed tasks could appear.
Add a status= todo,in_progress filter to GET /api/tasks and update the widget
to use it (also fix domain badge to use domainId).
Added reports table to PostgreSQL and Drizzle schema. Rewrote reports API routes to use Drizzle ORM instead of PocketBase stub. Fixed shared schema to match frontend field names. Fixed TipTap v3 type errors in note-editor.tsx and report-editor.tsx (StarterKit cast+chain as any).
Root cause: inline arrow functions for onDelete and onSave props created new references every render, breaking React.memo and causing editor re-renders that steal focus.