Schema Changes
Phases 2-4
Three phases of feature work. Zero new custom objects. One new field. One new Custom Metadata Type. One new Apex service.
Last Updated: March 27, 2026
Phase 2: Polish -- No New Schema
Phase 2 added zero new objects or fields. Everything was reports, tabs, and UI.
Zero new objects or fields
Everything was reports, tabs, and UI
Activity_Logs, Work_Logs (for existing ActivityLog__c and WorkLog__c)
DeliveryHubClient, DeliveryHubVendor, DeliveryHubPipeline
Active Deliveries, Attention Items, Blocked, Budget Health, Monthly Hours, Velocity by Week, and more
Delivery_Activity_Dashboard, Delivery_Workspace
DeliveryDocumentRecordPage, DeliveryHubAdminHome
Phase 2 focused entirely on surfacing existing data through reports and workspace -- no schema bloat.
Phase 3: Self-Service -- 1 New Field
Phase 3 added one field to an existing object.
PortalAccess__c.AccessTokenTxt__c
128-bit hex token, auto-generated during client onboarding
Why on PortalAccess__c?
This object already existed for tracking who has portal access. The token is the auth credential -- it belongs with the access record.
No new objects. The template manager and workflow picker are pure LWC/Apex -- they read existing CMT records (WorkflowType__mdt, WorkflowStage__mdt).
Phase 4: Intelligence -- 1 CMT + 1 Service
Phase 4 added one new Custom Metadata Type (DeveloperCapacity__mdt) and one new Apex service (DeliveryVelocityService.cls), plus enhanced the existing WorkItemETAService.
DeveloperCapacity__mdt
Custom Metadata Type with 6 fields
| Field | Type | Purpose |
|---|---|---|
| DeveloperUserIdTxt__c | Text | The Salesforce User ID of the developer |
| WeeklyCapacityHoursNumber__c | Number | How many hours/week this developer is available (default assumption: 40) |
| AllocationPercentNumber__c | Number | What percentage of their time is allocated to this workflow type (e.g., 80% means 32 of 40 hours) |
| WorkflowTypeNameTxt__c | Text | Which workflow type this capacity record applies to (e.g., "Software_Delivery") |
| EffectiveDateDt__c | Date | When this capacity allocation takes effect. Allows scheduling future capacity changes. |
| IsActiveBool__c | Checkbox | Whether this capacity record is currently active. Inactive records are ignored by the velocity service. |
How it works together
Entry point
DeliveryVelocityService.getVelocityDashboard("Software_Delivery")
Velocity (no CMT needed)
Queries WorkItem__c for items in terminal stages over the last 8 weeks, groups by week → bar chart
Projection (no CMT needed)
Counts remaining non-terminal items, divides by average velocity → estimated weeks + projected completion date
Capacity (uses DeveloperCapacity__mdt)
Loads all capacity records for the workflow type, calculates allocated hours per developer, counts active items → utilization %
What-if (client-side only)
User enters "what if we add N items?" → JS recalculates projected weeks instantly without an Apex call
Why CMT instead of a custom object?
Configuration, not transactional data
"Glen works 40hrs/week at 80% allocation" doesn't change per work item.
Deploys with the package
Survives installs and upgrades.
Admin-editable in Setup
No code required to change capacity values.
Consistent with existing pattern
WorkflowType__mdt, WorkflowStage__mdt, WorkflowPersonaView__mdt are all CMT.
Queryable without DML or CRUD checks
Simpler Apex, no permission concerns.
Why not just use User fields?
Can't easily add custom fields to the standard User object in a managed package.
Allocation varies per workflow type -- same person might be 80% on Software_Delivery and 20% on Loan_Approval.
CMT records are version-controlled in the repo.
DeliveryVelocityService.cls
New Apex service -- all Phase 4 server-side logic
| Method | Purpose |
|---|---|
| getTeamVelocity(workflowType, weeks) | Rolling velocity calculation -- items completed per week over the specified window (default 8 weeks) |
| getProjectedETAs(entityId) | Queue-aware ETA projection for all items under an entity, factoring in position and team throughput |
| getDeveloperUtilization() | Reads DeveloperCapacity__mdt records, calculates allocated vs. available hours per developer |
| getWhatIfProjection(params) | Scenario modeling -- "what if we add N items?" or "what if we lose a developer?" |
| getVelocityDashboard(workflowType) | Composite method that returns velocity, projection, and capacity data in a single call for the LWC dashboard |
WorkItemETAService Enhancement
The existing ETA service is parameterized to accept velocity data instead of relying solely on static estimates. Falls back to estimate-based ETAs when velocity data is insufficient.
What happens without any DeveloperCapacity__mdt records: Velocity chart still works (queries WorkItem__c directly). Projected completion still works (based on velocity + remaining items). Capacity section shows "No developer capacity configured" -- graceful degradation. The CMT is purely additive.
The Big Picture
Combined schema impact across three phases of development.
Total schema impact across Phases 2, 3, and 4
Velocity Data Flow
Keep the schema small.
Surface existing data through code, not more fields.
Related Pages
Dive deeper into each phase or see the full roadmap.