Project Management Module
Implementation Plan — Module Breakdown
Ziel Log System
Table of Contents
Click Ctrl+Click on any heading in the Navigation Pane to jump to a section.
M1: Workflow Engine (Foundation)
M2: Task & Log Enhancement
M3: Blockers & Comments
M4: Dependencies & Critical Path
M5: Health, Burndown & Reporting
M6: Sprints & Velocity
M7: Client Portal
Rollout Strategy
Goals Removal
Module Dependency Graph
M1: Workflow Engine (Foundation)
Purpose
Every downstream feature — burndown, health status, blockers, dependencies, sprints —
resolves a task's state through workflow_statuses.category, never through the status label. This
module builds that foundation so every subsequent module can rely on a structured status
system. Configurable workflows come first because critical path and velocity calculations both
need a reliable, structured answer to whether a task is actually done, independent of whatever
label a given project's workflow uses for that state.
Prerequisites
None. This is the foundation module.
Schema Changes
New Tables
workflow_templates — reusable template object (name, description, created_by,
created_at)
workflow_statuses — each status within a template: name, category (todo | in_progress |
done), sort_order, is_initial
workflow_transitions — allowed from→to transitions. from_status_id nullable = allowed
from any status
task_status_history — audit trail of every status change (task_id, from_status_id,
to_status_id, changed_by_type, changed_by_id, changed_at)
Modified Tables
tasks — add status_id (FK → workflow_statuses.id) to replace the free-text status column
projects — add workflow_template_id (FK → workflow_templates.id)
Database Triggers
A database trigger on tasks.status_id writes a row to task_status_history on every INSERT
and UPDATE of status_id. This ensures no code path can change a status without leaving a
record.
Business Logic
Data Migration — Step 1: Default Template
Create a built-in template named "Standard" with statuses matching current free-text values:
unlinked (todo, is_initial), linked (in_progress), in_progress (in_progress), complete (done),
returned (todo).
Data Migration — Step 2: Backfill
Run: UPDATE tasks SET status_id = (SELECT id FROM workflow_statuses WHERE name =
[Link] LIMIT 1).
Data Migration — Step 3: Dual-Write Period
Keep the old status column as a fallback during M1 so all existing pages continue to work
unmodified. Migrate pages one-by-one to read from status_id, then drop the old column at the
end of M1.
Category-Driven Calculations
Every calculation in this document resolves a task's state through workflow_statuses.category,
never through the status name. This lets a project use any labels it wants without breaking
downstream logic.
UI Changes
Workflow Templates management page (Admin) — create/edit templates, add/edit
statuses, define transitions
Project Settings — pick a workflow template for the project
Task edit dialog — status picker reads from the project's workflow template, shows only
allowed transitions
Status badge on all task lists reads from workflow_statuses.name and colors by category
Testing Checklist
Create a workflow template with 3+ statuses and transitions
Assign template to a project
Create a task — verify default status is the is_initial status
Change task status — verify only allowed transitions are offered
Attempt a disallowed transition — verify it is rejected
Verify task_status_history is written on every status change
Verify old status column still works for existing pages (dual-read period)
Backfill migration — verify all existing tasks get a correct status_id
Multiple projects with different templates — verify no cross-contamination
────────────────────────────────────────────────────────────
M2: Task & Log Enhancement
Purpose
Adds new columns to the tasks and daily_logs tables that downstream modules (burndown,
reporting, client portal, sprints) depend on. Pure schema-and-UI extension — no new business
logic tables yet.
Prerequisites
M1 must be complete (status_id replaces free-text status).
Schema Changes
Modified Tables
tasks — add due_date (date, nullable), client_visible (boolean, default true), story_points
(numeric, nullable)
daily_logs — add task_id (uuid, FK→[Link], nullable)
Note: estimated_hours already exists on tasks. daily_logs.task_id is nullable — historical logs
remain project-level only.
UI Changes
Task create/edit form — due_date picker, client_visible toggle, story_points input
Task lists — show due_date column, color overdue tasks red, show client_visible indicator
Log Submit form — add task selector dropdown (filtered to user's assigned projects)
Log detail/view — show which task the log was submitted against
Testing Checklist
Create a task with due_date — verify it displays on the task list
Toggle client_visible on/off — verify it saves and reflects in UI
Set story_points on a task — verify it persists
Submit a daily log with a task selected — verify task_id is saved
Submit a daily log without selecting a task — verify task_id is null (backward compatible)
Overdue detection — verify tasks past due_date are highlighted
────────────────────────────────────────────────────────────
M3: Blockers & Comments
Purpose
Adds collaboration capabilities: blockers for tracking what holds up progress, and task
comments for internal team discussion. Blockers also feed into the health status calculation
(M5).
Prerequisites
M1 (workflow engine) and M2 (task_id on logs) must be complete.
Schema Changes
New Tables
blockers — id, project_id, task_id (nullable), description, status (open|resolved),
client_visible (default true), raised_by, raised_at, resolved_by, resolved_at
task_comments — id, task_id, author_type (human|ai), author_id (nullable), body,
created_at
Business Logic
Blockers can be raised against a specific task or at the project level. Open blockers feed into
health status. A blocker on a critical-path task (is_critical=true) is treated as more urgent.
The author_type field on task_comments (human|ai) is included from the start so this table can
support AI-generated comments once the scrum master automation begins, without a schema
change.
UI Changes
Task detail page — Blockers section (list of open/resolved blockers for this task or project)
Project overview — Blockers summary widget showing count by status
Raise blocker form — description, client_visible toggle, optional task association
Resolve blocker button (only for open blockers)
Task Comments thread — inline comment input, list view with author and timestamp
Testing Checklist
Raise a task-level blocker — verify it shows on the task and project views
Raise a project-level blocker (no task) — verify it shows on project view only
Toggle client_visible on a blocker — verify visibility changes
Resolve a blocker — verify resolved_at and resolved_by are set
Post a comment on a task — verify it appears immediately
Multiple comments — verify chronological ordering
author_type — verify it defaults to "human" for user-created comments
────────────────────────────────────────────────────────────
M4: Dependencies & Critical Path
Purpose
Allows tasks to depend on other tasks and computes the critical path — the chain of tasks with
zero scheduling slack. A blocker on a critical-path task is treated as more urgent than one with
schedule slack.
Prerequisites
M1 must be complete (critical path calculation needs workflow_statuses.category to determine
whether a task is done, because a completed task has zero remaining duration). M2 is needed
for due_date on tasks.
Schema Changes
New Tables
task_dependencies — id, task_id (dependent), depends_on_task_id (dependency),
dependency_type (finish_to_start|start_to_start|finish_to_finish|start_to_finish),
created_by, created_at
task_schedule_snapshots — id, task_id, snapshot_date, earliest_start, earliest_finish,
latest_start, latest_finish, slack_days, is_critical
Edge Functions
compute-critical-path — nightly cron job (pg_cron) that recalculates
task_schedule_snapshots for every active project
Business Logic
Cycle Prevention
On dependency insert, check that the graph does not loop: a task cannot depend, directly or
transitively, on a task that depends on it. This is a recursive check at insert time.
Critical Path Calculation
Forward pass: earliest_start = max of dependencies' earliest_finish (or project start);
earliest_finish = earliest_start + duration. Backward pass: latest_finish = min of dependents'
latest_start (or project end); latest_start = latest_finish − duration. slack_days = latest_start −
earliest_start. is_critical = true when slack_days = 0.
Enforcement Policy
Warning, not hard block. When a user tries to move a task to "done" or any in_progress status
while an unfinished finish_to_start predecessor exists, show a warning banner naming the
predecessor — but do not prevent the change. Hard blocking is deferred until dependency data
quality is proven.
UI Changes
Task edit dialog — Dependency picker to add/remove predecessor tasks by type
Task detail — Dependency view showing predecessors, successors, and dependency type
Task lists — Critical path indicator (icon or badge) on critical-path tasks
Warning banner on status change when blocking predecessors are unfinished
Schedule view — table view with slack_days and critical path highlighted
Testing Checklist
Create a dependency A→B (finish_to_start) — verify it is saved
Create a cycle A→B→A — verify it is rejected at insert time
Run compute-critical-path — verify slack_days and is_critical are calculated correctly
Verify a task with no dependencies shows earliest_start = project start
Verify warning banner appears when trying to advance a task with blocking predecessors
Verify the warning does NOT block the actual status change (soft enforcement)
Verify different dependency types affect dates correctly
────────────────────────────────────────────────────────────
M5: Health, Burndown & Reporting
Purpose
Provides project health status (on_track / at_risk / delayed), burndown charts against estimated
hours, and a reporting dashboard for utilization, overdue items, blocker frequency, and health
trends. This is the integration module.
Prerequisites
Needs M1 (workflow engine — knows which tasks are done via category), M2 (task_id on logs —
hours tracked per task), M3 (blockers — feeds into health), and M4 (critical path — feeds into
health) to be fully operational.
Schema Changes
New Tables
project_health_snapshots — id, project_id, snapshot_date, health_status (on_track|at_risk|
delayed), planned_hours, logged_hours, tasks_total, tasks_complete, tasks_overdue,
open_blockers
Edge Functions
compute-project-health — daily cron job that calculates burndown variance, overdue tasks,
and open blockers per active project and writes a project_health_snapshots row
Business Logic
Burndown Calculation
Remaining = SUM over tasks of max(0, task.estimated_hours − hours logged against [Link]).
Ideal line runs linearly from total estimated hours to 0 between earliest task creation date and
project/phase due date. Tasks without an estimate are excluded and shown as a separate
"unestimated" count.
Health Status Thresholds
On track: no overdue tasks, no open blockers, no open blocker on a critical-path task.
At risk: burndown variance 10–25%, at least one overdue task, any open blocker, or any open
blocker on a critical-path task.
Delayed: variance >25%, phase past due with completion below expected, blocker open >5
working days, or blocker on critical-path task open >2 working days.
UI Changes
Project overview — Health badge (green/amber/red) with tooltip
Burndown chart — line chart with ideal vs actual, scoped to project or single phase, with
unestimated task count
Reporting dashboard — utilization (hours by team member), hours by phase/category, task
velocity (completed/week), overdue/at-risk lists, blocker frequency & avg resolution time,
health trend chart
Testing Checklist
Create tasks with estimates, log hours — verify burndown line moves correctly
Verify the ideal line matches: total_hours at start, zero at due_date
Unestimated tasks — verify they are excluded from burndown but shown in count
Create an overdue task — verify health becomes at_risk or delayed
Create a blocker — verify health status changes
Run compute-project-health — verify snapshot is written with correct values
Verify health trend chart renders multiple snapshots over time
Reporting dashboard — verify utilization, hours by phase, velocity numbers
────────────────────────────────────────────────────────────
M6: Sprints & Velocity
Purpose
Adds sprint-based planning as an alternative or complement to phase-based organization. Tasks
can be pulled into time-boxed sprints, assigned story points, and tracked for velocity.
Prerequisites
Needs M1 (workflow engine — sprint completion reads from category=done) and M2
(story_points and sprint_id on tasks). Can be built in parallel with M3 and M4.
Schema Changes
New Tables
sprints — id, project_id, name, start_date, end_date, status (planned|active|completed),
created_at
sprint_snapshots — id, sprint_id, snapshot_date, committed_points, completed_points,
points_added_mid_sprint, points_removed_mid_sprint
Modified Tables
tasks — sprint_id (added in M2, reused here), story_points (added in M2)
Edge Functions
compute-sprint-snapshot — daily cron job (while sprint is active) that captures committed,
completed, added, and removed points. Also triggered at sprint close for the final snapshot.
Business Logic
Sprint Lifecycle
planned → active (sprint starts) → completed (sprint ends). Only active sprints have daily
snapshots captured.
Scope Change Tracking
When story points are added or removed after a sprint becomes active, they are tracked in
points_added_mid_sprint and points_removed_mid_sprint separately from completed_points.
Velocity
Velocity for a completed sprint = completed_points. Rolling velocity = simple average across
recent sprints. No forecasting logic in this version (deferred to AI scrum master stages).
UI Changes
Sprint management page — create sprint, set dates, activate/complete
Task list — sprint filter, sprint assignment dropdown
Backlog view — tasks without a sprint assignment
Sprint detail — progress bar (completed_points / committed_points), scope change log
Velocity chart — bar chart of completed_points per sprint, rolling average line
Testing Checklist
Create a sprint (planned) — verify it appears in sprint list
Activate a sprint — verify status changes to active
Assign tasks with story points — verify snapshot shows committed_points
Complete a task — verify completed_points increases
Add/remove story points mid-sprint — verify tracked separately
Close a sprint — verify final snapshot captured
Backlog tasks (no sprint) — verify filterable separately
Velocity calculation — verify completed sprint shows correct velocity
────────────────────────────────────────────────────────────
M7: Client Portal
Purpose
Provides a client-facing portal accessed via a tokenized share link (no login required). Shows
phase status, client-visible tasks and assignees, open blockers, action items, status updates, and
portal messaging.
Prerequisites
Requires M2 (client_visible flag), M3 (client-visible blockers), M5 (health badge). Built last
because it depends on data from all other modules.
Schema Changes
New Tables
project_share_links — id, project_id, token (unique), passcode_hash (nullable), expires_at
(nullable), revoked (default false), created_by, created_at, last_viewed_at
project_share_views — id, share_link_id, viewed_at (lightweight view log)
client_action_items — id, project_id, title, description, status (pending|completed|waived),
requested_by, due_date (nullable), completed_at (nullable)
client_portal_messages — id, project_id, title, body, cta_label (nullable), cta_url (nullable),
active (default true), created_by, created_at
project_status_updates — id, project_id, author_type (human|ai), author_id (nullable),
summary, visible_to_client (default false), created_at
Edge Functions
send-share-link — HTTP request (admin action); emails the client-facing link via existing
Resend integration
validate-share-token — HTTP request (client view load); validates token, checks
expiry/revocation/passcode
client-complete-action — HTTP request (client portal action); marks client_action_item
complete using service role (no RLS since client has no login)
Business Logic
Security Model
Token is a high-entropy random string (not sequential). Optional passcode, hashed at rest.
Configurable expiry — links default to expiring. Revocable at any time. Revoked/expired token
returns a generic "link no longer available" page — not an error that reveals project existence.
Client action items can only be marked complete by a valid, unexpired, unrevoked share token
scoped to that project. The write happens through a dedicated edge function using a service
role.
UI Changes
Admin: Share link generation/revocation UI on project page
Admin: Client action item management (create, assign due date, mark waived)
Admin: Portal message composer (title, body, optional CTA button)
Admin: Status update composer with visible_to_client toggle
Client Portal page (tokenized link): phase + health badge, client-visible task list with
assignees, open client-visible blockers, action items (with mark complete button), status
updates, portal message
Client Portal: view log (last_viewed_at, total views from project_share_views)
Testing Checklist
Generate a share link — verify token is created and can be copied
Set a passcode — verify portal requires it before rendering
Set expiry — verify link stops working after expiry date
Revoke a link — verify revoked link shows "not available" page
Portal view — verify it shows only client_visible tasks, blockers, and status updates
Mark a client action item complete from portal — verify it updates in DB
Verify internal-only data is NOT shown in portal
Verify health badge shows but schedule/velocity detail does not
Portal messaging — verify team-authored message shows in portal
Share link views — verify last_viewed_at updates on portal access
Expired token URL — verify it does not reveal whether the project exists
────────────────────────────────────────────────────────────
Rollout Strategy
Recommended Build Order
The modules are ordered by dependency. Each module can be built, tested, and deployed
incrementally:
M1 (Workflow Engine) → M2 (Task & Log Enhancement) → M3 (Blockers & Comments)
M4 (Dependencies) and M6 (Sprints) can be built in parallel after M1+M2 are complete
M5 (Health/Burndown/Reporting) depends on M1, M2, M3, M4
M7 (Client Portal) must wait for all other modules
Testing Approach
Unit: Test individual functions, triggers, and edge functions in isolation
Integration: Test module-specific UI flows end-to-end
Regression: Run all previous module tests when a new module is added
Dual-write period (M1): Keep old status column until all pages are migrated
Staged rollout per module: Schema → Edge Functions → UI → Test → Next module
Deployment Per Module
1. Apply schema migration (new tables + columns)
2. Deploy edge functions / cron jobs / triggers
3. Build and deploy UI changes
4. Run module-specific test checklist
5. Run regression tests for previous modules
6. Proceed to next module
Goals Removal
The existing goals feature is not part of the new Project Management Module specification and
should be removed completely.
What to Remove
Schema: goals table + all RLS policies + indexes
Schema: tasks.goal_id column (FK → [Link])
Pages: src/pages/[Link], src/pages/[Link]
Routes: /goals, /goals/new, /goals/:id
Navigation: all links to goals pages (sidebar, dashboard, etc.)
PhaseEditPage: goal-related branching logic (unassignWithGoal vs unassignNoGoal) —
simplify to unconditional phase_id=null update
Any goal references in reporting, queries, or utility functions
When to Remove
Before M1 development begins. Goals have no dependencies on any new module. Removing
them first cleans up the codebase. The old code remains in git history if ever needed.
Why Workflow Engine Comes First
Configurable workflows (M1) come before dependencies (M4) and sprints (M6) because critical
path and velocity calculations both need a reliable, structured answer to whether a task is
actually done — independent of whatever label a given project's workflow uses for that state.
Dependencies and sprints have no ordering requirement relative to each other, and can be built
in either order, or in parallel, once workflows are in place.
Module Dependency Graph
M1 ──▸ M2 ──▸ M3 ──▸ M5 ◄── M4
│ ▲ │
│ │ │
└─────▸ M6 ──▸│ │
│
M7 ◄───────┘
M1 — Workflow Engine (no dependencies)
M2 — Task & Log Enhancement (depends on M1)
M3 — Blockers & Comments (depends on M1, M2)
M4 — Dependencies & Critical Path (depends on M1, M2; parallel with M3, M6)
M5 — Health, Burndown & Reporting (depends on M1, M2, M3, M4)
M6 — Sprints & Velocity (depends on M1, M2; parallel with M3, M4)
M7 — Client Portal (depends on all other modules)
────────────────────────────────────────────────────────────
End of document