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)
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
The worktree session created node_modules symlinks that were staged with
git add -A. These broke the deploy script which uses cp -r and cannot
overwrite directories with symlinks.
- 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.
The CI workflow (projecte-runner) was never registered on the Gitea
instance, leaving quality/e2e/deploy/smoke jobs stuck at 'Waiting to run'.
Document the act_runner install/register/start flow so the pipeline can be
unblocked from the deploy host.
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).
- Capture deleted item before DELETE call
- Show sonner toast with Undo action button
- On Undo, POST to recreate the item with original data
- Applied to tasks-list-view, task-detail-panel, and notes page
- Add offset/limit pagination with 50 items per page
- Load More button appends tasks to existing list
- Show "Showing X of Y" count in the filter bar area
- Add time tracking section in task detail panel with progress bar
- Start/Stop timer with elapsed time display
- On stop, PATCH trackedMinutes with delta
- Add clock icon on kanban cards when estimatedMinutes is set
- Note: timer is per-tab (no persistence across refresh)
- Dashboard: fetch project progress from /api/projects/[id]/progress
- Tasks List: add DropdownMenuTrigger to More options button
- Projects: add New project button with CreateItemDialog integration
- Settings: use color picker value when creating domains
- Calendar: use dynamic domain options instead of hardcoded list
- Dashboard: make View all button navigate to /tasks
- Dashboard: domain names already resolved via domainMap (verified working)
- CreateItemDialog: shared Zustand store for dialog state
- TopBar: use store instead of router.push navigation
- TaskDetailPanel: dynamic domain fetch from /api/domains
- TodayTasksWidget: domain name resolution from UUIDs
- ProjectProgressWidget: fetch real progress, default to 0
- Calendar page: domain filter fetches from API dynamically
- Habits page: edit/delete dropdown with AlertDialog
- Projects page: domain name display + delete button
- Notes page: domain picker on creation, names in list
- Settings domains: add color picker input
- Tasks list view: MoreHorizontal wired to edit/delete
- HabitCard: domain resolution + edit/delete dropdown
- PocketBase compat: add JSDoc migration comment