All Docs
FeaturesAgentOS WorkUpdated March 13, 2026

Workforce Capacity Planning

Workforce Capacity Planning

The Workforce Capacity Planning module allows HR and operations teams to define headcount targets by department and planning period, model hiring plans against budget constraints, and monitor skill gaps and staffing risks in real time.

Accessing the Dashboard

Navigate to Dashboard → Capacity Planning in the main sidebar (listed after Recruitment). The URL is /dashboard/capacity.

Authentication is required. The page uses a server-side auth guard before rendering.


Dashboard Overview

The capacity planning dashboard is divided into four sections:

1. Summary KPI Cards

Four cards at the top of the page provide an org-wide snapshot:

CardDescription
Planned HeadcountTotal headcount targeted across all active plans
Current HeadcountLive headcount pulled from the employees table
Net GapDifference between planned and current (negative = understaffed)
Risk DistributionCount of plans by risk level (critical / understaffed / healthy / overstaffed)

2. Headcount Heatmap

A colour-coded grid showing headcount risk at the intersection of planning period and department.

ColourRisk LevelGap % Threshold
🔴 RedCriticalGap > 25% understaffed
🟡 AmberUnderstaffedGap 10–25% understaffed
🟢 GreenHealthyGap within −10% to +20%
🔵 BlueOverstaffedGap > 20% overstaffed

3. Plans Table

A paginated table of all capacity plans. Each row is expandable and shows:

  • Planned vs. current headcount with gap badge
  • Planning period (formatted as Mon YYYY – Mon YYYY)
  • Budget allocated and budget per head
  • Skill requirements with priority levels

4. High-Priority Skill Gap Alert Panel

Surfaces skill gaps flagged as high priority across all active plans, grouped by skill and level. Use this panel to identify critical hiring needs that cut across multiple departments.


Creating a Capacity Plan

Click the + New Plan button on the dashboard to open the create plan modal. Fill in the following fields:

FieldDescription
DepartmentSelect from the organisation's department list
Period Start / EndPlanning window (e.g. a quarter)
Planned HeadcountTarget number of employees for this period
Budget AllocatedBudget in your base currency
StatusOne of: draft, active, closed, archived

On creation, the plan's current_headcount is automatically populated from the live employees table.


Risk Classification

Gap percentage is calculated as:

gap     = planned_headcount − current_headcount
gap_pct = (gap / planned_headcount) × 100

Risk level is then assigned:

Gap %Risk LevelMeaning
> +25%CriticalSeverely understaffed
+10% to +25%UnderstaffedModerate shortfall
−10% to +10%HealthyWithin acceptable range
< −10% (surplus)OverstaffedHeadcount exceeds plan by more than 20%

The gap sign convention is: a positive gap means you need more people; a negative gap means you have more people than planned.


tRPC API Reference

All capacity planning operations are exposed under the capacityPlanning router.

capacityPlanning.list

Returns a paginated list of capacity plans with department names resolved.

Input:

{
  orgId: string;
  page?: number;      // default 1
  pageSize?: number;  // default 20
}

capacityPlanning.get

Returns full details for a single plan.

Input:

{ id: string; orgId: string }

capacityPlanning.create

Creates a new plan. current_headcount is auto-populated from the employees table at creation time.

Input:

{
  orgId: string;
  departmentId?: string;
  periodStart: string;        // ISO date, e.g. "2025-01-01"
  periodEnd: string;          // ISO date, e.g. "2025-03-31"
  plannedHeadcount: number;
  skillRequirements?: Array<{
    skill: string;
    level: string;
    count: number;
    priority: string;         // e.g. "high", "medium", "low"
  }>;
  budgetAllocated?: number;
  status?: "draft" | "active" | "closed" | "archived";
}

All mutations are audit-logged via logAudit().


capacityPlanning.update

Updates an existing plan's headcount, skills, budget, or status.

Input:

{
  id: string;
  orgId: string;
  plannedHeadcount?: number;
  currentHeadcount?: number;
  skillRequirements?: SkillRequirement[];
  budgetAllocated?: number;
  status?: "draft" | "active" | "closed" | "archived";
}

capacityPlanning.delete

Soft-deletes a capacity plan.

Input:

{ id: string; orgId: string }

capacityPlanning.gapAnalysis

Returns computed gap metrics for all active plans within an org.

Response per plan:

{
  id: string;
  departmentName: string;
  periodStart: string;
  periodEnd: string;
  plannedHeadcount: number;
  currentHeadcount: number;
  gap: number;
  gapPct: number;
  riskLevel: "critical" | "warning" | "healthy" | "overstaffed";
  budgetAllocated: number;
  budgetPerHead: number;
  skillRequirements: SkillRequirement[];
  highPrioritySkillGaps: SkillRequirement[];
  status: string;
}

capacityPlanning.summary

Org-level aggregates for dashboard KPI cards.

Response:

{
  totalPlanned: number;
  totalCurrent: number;
  netGap: number;
  riskDistribution: {
    critical: number;
    warning: number;
    healthy: number;
    overstaffed: number;
  };
}

capacityPlanning.departments

Returns the list of departments for use in plan-creation dropdowns.

Input:

{ orgId: string }

Automated Weekly Refresh (Cron)

The capacity-gap-analysis Inngest function runs automatically every Monday at 06:00 UTC.

What it does:

  1. Fetches all active capacity plans for each organisation
  2. Queries the live employees table to get the latest headcount per department
  3. Updates current_headcount on each plan with the refreshed value
  4. Tracks execution via registerBatch / trackBatchRun

Concurrency: The function is scoped to a concurrency limit of 1 — it will not execute twice simultaneously for the same organisation.

You do not need to manually trigger this job; it runs on schedule. To force a manual refresh of the dashboard data, use the refresh button on the capacity planning page.


Database Schema

Capacity plans are stored in the capacity_plans table (defined in src/db/schema-planning.ts).

ColumnTypeDescription
iduuidPrimary key
org_iduuidTenant identifier
department_iduuid | nullLinks to the departments table
period_startdateStart of the planning window
period_enddateEnd of the planning window
planned_headcountintegerTarget number of employees
current_headcountintegerLive employee count (auto-refreshed)
skill_requirementsjsonbArray of skill requirement objects
budget_allocatednumericBudget for this plan
statusenumdraft | active | closed | archived

The skill_requirements JSONB field holds an array of objects with the shape:

[
  {
    "skill": "TypeScript",
    "level": "senior",
    "count": 3,
    "priority": "high"
  }
]

Audit Logging

All mutating operations (create, update, delete) on capacity plans are recorded via the platform's logAudit() function. Audit records include the acting user, the org, the operation type, and a snapshot of the changed data.