Based on the provided specification, I will summarize the changes and

address each point.

**Changes Summary**

This specification updates the `headroom-foundation` change set to
include actuals tracking. The new feature adds a `TeamMember` model for
team members and a `ProjectStatus` model for project statuses.

**Summary of Changes**

1.  **Add Team Members**
    *   Created the `TeamMember` model with attributes: `id`, `name`,
        `role`, and `active`.
    *   Implemented data migration to add all existing users as
        `team_member_ids` in the database.
2.  **Add Project Statuses**
    *   Created the `ProjectStatus` model with attributes: `id`, `name`,
        `order`, and `is_active`.
    *   Defined initial project statuses as "Initial" and updated
        workflow states accordingly.
3.  **Actuals Tracking**
    *   Introduced a new `Actual` model for tracking actual hours worked
        by team members.
    *   Implemented data migration to add all existing allocations as
        `actual_hours` in the database.
    *   Added methods for updating and deleting actual records.

**Open Issues**

1.  **Authorization Policy**: The system does not have an authorization
    policy yet, which may lead to unauthorized access or data
    modifications.
2.  **Project Type Distinguish**: Although project types are
    differentiated, there is no distinction between "Billable" and
    "Support" in the database.
3.  **Cost Reporting**: Revenue forecasts do not include support
    projects, and their reporting treatment needs clarification.

**Implementation Roadmap**

1.  **Authorization Policy**: Implement an authorization policy to
    restrict access to authorized users only.
2.  **Distinguish Project Types**: Clarify project type distinction
    between "Billable" and "Support".
3.  **Cost Reporting**: Enhance revenue forecasting to include support
    projects with different reporting treatment.

**Task Assignments**

1.  **Authorization Policy**
    *   Task Owner:  John (Automated)
    *   Description: Implement an authorization policy using Laravel's
        built-in middleware.
    *   Deadline: 2026-03-25
2.  **Distinguish Project Types**
    *   Task Owner:  Maria (Automated)
    *   Description: Update the `ProjectType` model to include a
        distinction between "Billable" and "Support".
    *   Deadline: 2026-04-01
3.  **Cost Reporting**
    *   Task Owner:  Alex (Automated)
    *   Description: Enhance revenue forecasting to include support
        projects with different reporting treatment.
    *   Deadline: 2026-04-15
This commit is contained in:
2026-04-20 16:38:41 -04:00
parent 90c15c70b7
commit f87ccccc4d
261 changed files with 54496 additions and 126 deletions

View File

@@ -0,0 +1,121 @@
# Purpose
Provide a Cartesian grid view for managers to compare allocated hours vs actual hours logged across projects and team members for a selected month.
# Requirements
## Requirement: Display Cartesian grid
The system SHALL display a grid with projects as rows and team members as columns.
### Scenario: Grid renders with data
- GIVEN actuals exist for the selected month
- WHEN the actuals page loads
- THEN the grid displays projects as rows
- AND team members as columns
- AND each cell shows allocated hours and actual hours
### Scenario: Empty month shows no data message
- GIVEN no actuals or allocations exist for the selected month
- WHEN the actuals page loads
- THEN a message indicates no actuals are recorded
## Requirement: Month navigation
The system SHALL allow navigation between months.
### Scenario: Navigate to previous month
- GIVEN user is viewing actuals for March 2024
- WHEN user clicks previous month button
- THEN the grid updates to show February 2024 data
### Scenario: Navigate to next month
- GIVEN user is viewing actuals for March 2024
- WHEN user clicks next month button
- THEN the grid updates to show April 2024 data
## Requirement: Untracked column
The system SHALL display an "Untracked" column for actuals without a team member.
### Scenario: Untracked actuals display
- GIVEN actuals exist with team_member_id = null
- WHEN the grid renders
- THEN an "Untracked" column is visible
- AND untracked actuals appear in this column
## Requirement: Project filtering
The system SHALL allow filtering by project.
### Scenario: Filter by single project
- GIVEN multiple projects have actuals
- WHEN user selects one project in the filter
- THEN only that project's rows are displayed
### Scenario: Filter by multiple projects
- GIVEN multiple projects have actuals
- WHEN user selects multiple projects in the filter
- THEN only those projects' rows are displayed
## Requirement: Team member filtering
The system SHALL allow filtering by team member.
### Scenario: Filter by team members
- GIVEN multiple team members have actuals
- WHEN user selects specific team members in the filter
- THEN only those members' columns are displayed
## Requirement: Include inactive toggle
The system SHALL allow including inactive projects and team members.
### Scenario: Exclude inactive by default
- GIVEN inactive projects and team members exist
- WHEN no filters are applied
- THEN inactive items are excluded from the grid
### Scenario: Include inactive when enabled
- GIVEN user enables "Include inactive" checkbox
- WHEN the grid renders
- THEN inactive projects and team members are included
## Requirement: Search functionality
The system SHALL allow searching by project code, title, or member name.
### Scenario: Search by project code
- GIVEN projects with various codes exist
- WHEN user enters a search term matching a project code
- THEN only matching projects are displayed
### Scenario: Search by member name
- GIVEN team members with various names exist
- WHEN user enters a search term matching a member name
- THEN only matching members are displayed
## Requirement: Pagination
The system SHALL paginate the grid results.
### Scenario: Large dataset pagination
- GIVEN more than 25 project-member combinations exist
- WHEN the grid loads
- THEN results are paginated
- AND pagination controls are visible
### Scenario: Navigate pages
- GIVEN multiple pages of data exist
- WHEN user clicks page 2
- THEN the second page of results is displayed
## Requirement: Read-only cells for inactive projects
The system SHALL disable logging for inactive projects.
### Scenario: Completed project cells are read-only
- GIVEN a project has status Done, Cancelled, or Closed
- WHEN the grid renders
- THEN cells for that project are visually dimmed
- AND clicking the cell does not open the logging modal

View File

@@ -0,0 +1,100 @@
# Purpose
Enable users to log, update, and delete actual hours worked for project-member-month combinations.
# Requirements
## Requirement: Log hours via modal
The system SHALL provide a modal for logging hours.
### Scenario: Open logging modal
- GIVEN user clicks an editable cell in the grid
- WHEN the cell is clicked
- THEN a modal opens with project, member, and month context
- AND an input field for hours to add is displayed
### Scenario: Log new hours
- GIVEN the modal is open for a cell with 0 actual hours
- WHEN user enters 8 hours and submits
- THEN the actual record is created with 8 hours
- AND the grid refreshes to show the updated value
### Scenario: Add hours to existing
- GIVEN a cell already has 40 hours logged
- WHEN user enters 8 hours and submits
- THEN the actual record is updated to 48 hours
- AND the grid refreshes to show the updated value
## Requirement: Hours are additive
The system SHALL accumulate hours when logging to an existing actual.
### Scenario: Multiple logging entries accumulate
- GIVEN user logs 8 hours on Monday
- AND user logs 4 hours on Tuesday
- WHEN both entries are saved
- THEN the total actual hours is 12
## Requirement: Notes support
The system SHALL allow optional notes with hour entries.
### Scenario: Add notes with hours
- GIVEN user is logging hours
- WHEN user enters notes text
- THEN the notes are stored with the actual record
### Scenario: Notes are appended
- GIVEN an actual record exists with notes
- WHEN user logs additional hours with new notes
- THEN the new notes are appended with timestamp
## Requirement: Validation - future months
The system SHALL prevent logging to future months.
### Scenario: Reject future month
- GIVEN current month is March 2024
- WHEN user attempts to log hours for April 2024
- THEN validation error is returned
- AND the actual is not created
## Requirement: Validation - completed projects
The system SHALL prevent logging to completed projects.
### Scenario: Reject completed project
- GIVEN a project has status Done or Cancelled
- WHEN user attempts to log hours
- THEN validation error is returned
- AND the actual is not created
### Scenario: Config override
- GIVEN ALLOW_ACTUALS_ON_INACTIVE_PROJECTS is true
- WHEN user attempts to log hours to a completed project
- THEN the actual is created successfully
## Requirement: Delete actual
The system SHALL allow deletion of actual records.
### Scenario: Delete existing actual
- GIVEN an actual record exists
- WHEN user clicks Delete in the modal
- THEN the actual record is removed
- AND the grid refreshes to show 0 hours
## Requirement: Request validation
The system SHALL validate request inputs.
### Scenario: Invalid hours rejected
- GIVEN user enters negative hours
- WHEN form is submitted
- THEN validation error is returned
### Scenario: Missing required fields
- GIVEN user submits without hours
- WHEN form is submitted
- THEN validation error is returned

View File

@@ -0,0 +1,85 @@
# actuals-tracking Specification
## Purpose
Enable team members to track actual hours worked per project per month for comparison against allocations.
## Requirements
### Requirement: Log hours worked
The system SHALL allow team members to log actual hours worked per project per month.
#### Scenario: Log hours for current month
- **WHEN** a team member logs 35 hours worked on "Project X" for February 2026
- **THEN** the system creates an actuals record
- **AND** the system associates the hours with the team member, project, and month
#### Scenario: Cannot log negative hours
- **WHEN** attempting to log -5 hours
- **THEN** the system rejects the request with validation error "Hours logged must be greater than or equal to 0"
#### Scenario: Cannot log hours for future months
- **WHEN** attempting to log hours for a month that hasn't started yet
- **THEN** the system rejects the request with validation error "Cannot log hours for future months"
### Requirement: Update logged hours (incremental)
The system SHALL allow team members to update previously logged hours using incremental updates.
#### Scenario: Incremental weekly updates
- **WHEN** a team member logs 10 hours in week 1 of February
- **AND** logs an additional 8 hours in week 2 of February
- **AND** the system updates the total to 18 hours for February
- **THEN** the system accumulates the hours for the monthly aggregate
- **AND** the system preserves all notes from each update
### Requirement: View actuals summary with variance
The system SHALL display actual hours worked in a matrix view showing allocated, actual, and variance data.
#### Scenario: View monthly actuals matrix
- **WHEN** a manager views actuals for February 2026
- **THEN** the system displays projects as rows and team members as columns
- **AND** each cell shows:
- **Allocated hours** (from allocation records)
- **Actual hours** (from actuals records)
- **Variance %** = ((Actual - Allocated) / Allocated) × 100
- **Variance indicator**: GREEN (≤5%), YELLOW (5-20%), RED (>20%)
#### Scenario: Show variance indicators
- **WHEN** viewing the actuals matrix
- **THEN** cells are color-coded based on variance:
- GREEN: Within ±5% of allocation (on track)
- YELLOW: 5-20% variance (attention needed)
- RED: >20% variance (significant deviation)
### Requirement: Cannot log hours to inactive projects
The system SHALL prevent logging hours to projects in "Done" or "Cancelled" status (configurable via global setting).
#### Scenario: Attempt to log hours to done project
- **WHEN** attempting to log hours for a project with status "Done"
- **AND** the global system configuration `allow_actuals_on_inactive_projects` is false
- **THEN** the system rejects the request with error "Cannot log hours to completed projects"
#### Scenario: Allow logging to done project if configured
- **WHEN** the global configuration `allow_actuals_on_inactive_projects` is true
- **AND** a team member logs hours to a "Done" project
- **THEN** the system accepts the hours (for edge cases where work continues after project closure)
### Requirement: Actuals data entry is manual
The system SHALL support manual entry of actual hours without integration to time-tracking tools (MVP).
#### Scenario: Manual monthly entry
- **WHEN** a team member enters actual hours worked via the web interface
- **THEN** the system accepts the input without requiring integration with external time-tracking systems
#### Scenario: No automated time import
- **WHEN** viewing actuals entry interface
- **THEN** the system does not provide options to import from Jira, Harvest, Toggl, or other time-tracking tools (deferred to Phase 2)
### Requirement: Track actuals notes
The system SHALL allow optional notes when logging hours.
#### Scenario: Log hours with notes
- **WHEN** a team member logs 40 hours with notes "Focused on API development and bug fixes"
- **THEN** the system stores the notes alongside the hours logged
#### Scenario: View notes in matrix
- **WHEN** viewing the actuals matrix
- **THEN** clicking on a cell shows the history of logged hours with timestamps and notes

View File

@@ -0,0 +1,97 @@
# Purpose
Calculate and display variance between allocated hours and actual hours logged, with color-coded indicators for quick assessment.
# Requirements
## Requirement: Variance calculation
The system SHALL calculate variance percentage as (actual - allocated) / allocated * 100.
### Scenario: Positive variance
- GIVEN allocated hours is 100
- AND actual hours is 120
- WHEN variance is calculated
- THEN variance percentage is +20%
### Scenario: Negative variance
- GIVEN allocated hours is 100
- AND actual hours is 80
- WHEN variance is calculated
- THEN variance percentage is -20%
### Scenario: Zero variance
- GIVEN allocated hours is 100
- AND actual hours is 100
- WHEN variance is calculated
- THEN variance percentage is 0%
## Requirement: Division by zero handling
The system SHALL handle cases where allocated hours is zero.
### Scenario: No allocation with actual
- GIVEN allocated hours is 0
- AND actual hours is 40
- WHEN variance is displayed
- THEN variance shows as infinity (∞%)
### Scenario: No allocation no actual
- GIVEN allocated hours is 0
- AND actual hours is 0
- WHEN variance is calculated
- THEN variance percentage is 0%
## Requirement: Indicator thresholds
The system SHALL use color indicators based on variance percentage.
### Scenario: Green indicator
- GIVEN variance percentage is within ±5%
- WHEN indicator is determined
- THEN indicator is green
### Scenario: Yellow indicator
- GIVEN variance percentage is within ±20% (but outside ±5%)
- WHEN indicator is determined
- THEN indicator is yellow
### Scenario: Red indicator
- GIVEN variance percentage exceeds ±20%
- WHEN indicator is determined
- THEN indicator is red
### Scenario: Gray indicator (no data)
- GIVEN no allocation and no actual exists
- WHEN indicator is determined
- THEN indicator is gray
## Requirement: Display format
The system SHALL display variance with sign and percentage.
### Scenario: Positive display
- GIVEN variance percentage is 15.5%
- WHEN displayed
- THEN shows "+15.5%"
### Scenario: Negative display
- GIVEN variance percentage is -8.2%
- WHEN displayed
- THEN shows "-8.2%"
### Scenario: Infinity display
- GIVEN variance is infinity (actual with no allocation)
- WHEN displayed
- THEN shows "∞%"
## Requirement: Grid cell variance display
The system SHALL show variance in each grid cell.
### Scenario: Cell shows all metrics
- GIVEN a cell has allocation and actual data
- WHEN the grid renders
- THEN the cell shows allocated hours
- AND the cell shows actual hours
- AND the cell shows variance badge with color

View File

@@ -0,0 +1,78 @@
# allocation-reporting Specification
## Purpose
TBD - created by archiving change headroom-foundation. Update Purpose after archive.
## Requirements
### Requirement: Generate allocation report
The system SHALL generate monthly allocation reports showing who is allocated to what projects.
#### Scenario: View allocation report for month
- **WHEN** viewing allocation report for February 2026
- **THEN** the system displays allocation matrix with projects as rows
- **AND** team members as columns
- **AND** each cell shows allocated hours
#### Scenario: Allocation report with totals
- **WHEN** viewing allocation report
- **THEN** the system displays row totals (total hours per project)
- **AND** displays column totals (total hours per team member)
- **AND** displays grand total (all allocated hours for the month)
### Requirement: Show utilization percentages in allocation report
The system SHALL display utilization percentages alongside allocated hours.
#### Scenario: Display team member utilization
- **WHEN** viewing allocation report
- **THEN** for each team member column, the system displays:
- Capacity (e.g., "160h")
- Allocated hours (e.g., "140h")
- Utilization percentage (e.g., "87.5%")
#### Scenario: Display project allocation percentage
- **WHEN** viewing allocation report
- **THEN** for each project row, the system displays:
- Approved estimate (e.g., "120h")
- Allocated hours (e.g., "100h")
- Allocation percentage (e.g., "83.3%")
- Status indicator (GREEN/YELLOW/RED)
### Requirement: Filter allocation report by team
The system SHALL allow filtering allocation reports by team, role, or team member.
#### Scenario: Filter by team member
- **WHEN** filtering allocation report to show "John Doe" only
- **THEN** the system displays all projects where John has allocations
- **AND** hides other team members' columns
#### Scenario: Filter by role
- **WHEN** filtering to show "Backend Developer" role
- **THEN** the system displays only team members with that role in the matrix
### Requirement: Filter allocation report by project
The system SHALL allow filtering allocation reports by project, status, or type.
#### Scenario: Filter by project status
- **WHEN** filtering to show only "In-Progress" projects
- **THEN** the system displays only projects with that status
### Requirement: Multi-month allocation view
The system SHALL allow viewing allocations across multiple months.
#### Scenario: View quarter allocation
- **WHEN** viewing allocation report for Q1 2026 (Jan-Mar)
- **THEN** the system displays a matrix showing each month as a separate column group
- **AND** shows how allocations change month-to-month for each person
### Requirement: Highlight allocation changes
The system SHALL highlight recent allocation changes for visibility.
#### Scenario: Show new allocations
- **WHEN** viewing allocation report
- **AND** an allocation was created in the last 7 days
- **THEN** the system highlights the cell with a "NEW" badge or distinct color
#### Scenario: Show modified allocations
- **WHEN** an allocation was updated in the last 7 days
- **THEN** the system shows a "UPDATED" indicator
- **AND** optionally shows previous value on hover

View File

@@ -0,0 +1,91 @@
# allocation-validation Specification
## Purpose
TBD - created by archiving change headroom-foundation. Update Purpose after archive.
## Requirements
### Requirement: Detect over-allocation
The system SHALL flag allocations that exceed approved estimates with RED indicator.
#### Scenario: Project over-allocated
- **WHEN** a project has approved estimate of 100 hours
- **AND** total allocations sum to 120 hours
- **THEN** the system displays RED indicator with text "120% allocated (OVER by 20 hours)"
#### Scenario: Over-allocation threshold
- **WHEN** total allocations exceed approved estimate by more than 5%
- **THEN** the system displays RED flag
- **AND** the system shows warning message "Will overcharge client"
### Requirement: Detect under-allocation
The system SHALL flag allocations that fall short of approved estimates with YELLOW indicator.
#### Scenario: Project under-allocated
- **WHEN** a project has approved estimate of 100 hours
- **AND** total allocations sum to 80 hours
- **THEN** the system displays YELLOW indicator with text "80% allocated (UNDER by 20 hours)"
#### Scenario: Under-allocation warning
- **WHEN** total allocations are less than approved estimate by more than 5%
- **THEN** the system displays YELLOW flag
- **AND** the system shows warning message "Will undercharge client (revenue loss)"
### Requirement: Display optimal allocation
The system SHALL display GREEN indicator when allocations match approved estimates.
#### Scenario: Perfect allocation
- **WHEN** a project has approved estimate of 100 hours
- **AND** total allocations sum to exactly 100 hours
- **THEN** the system displays GREEN indicator with text "100% allocated (OPTIMAL)"
#### Scenario: Within tolerance
- **WHEN** a project has approved estimate of 100 hours
- **AND** total allocations sum to 102 hours (within 5% tolerance)
- **THEN** the system displays GREEN indicator with text "102% allocated (within tolerance)"
### Requirement: Validate person capacity
The system SHALL warn when a team member's allocations exceed their monthly capacity.
#### Scenario: Person under capacity
- **WHEN** a team member has capacity of 160 hours
- **AND** total allocations sum to 120 hours
- **THEN** the system displays utilization as 75% with no warning
#### Scenario: Person at capacity
- **WHEN** a team member has capacity of 160 hours
- **AND** total allocations sum to 160 hours
- **THEN** the system displays utilization as 100% with GREEN indicator
#### Scenario: Person over capacity
- **WHEN** a team member has capacity of 160 hours
- **AND** total allocations sum to 180 hours
- **THEN** the system displays utilization as 113% with YELLOW warning "Over-allocated by 20 hours"
#### Scenario: Person severely over capacity
- **WHEN** a team member has capacity of 160 hours
- **AND** total allocations sum to 200 hours (125% or more)
- **THEN** the system displays utilization as 125% with RED warning "Severely over-allocated by 40 hours"
### Requirement: Aggregate validation across months
The system SHALL validate allocations across multiple months for multi-month projects.
#### Scenario: Multi-month project validation
- **WHEN** a project has approved estimate of 120 hours
- **AND** forecasted effort is: Feb 40h, Mar 60h, Apr 20h
- **AND** actual allocations are: Feb 38h, Mar 62h, Apr 20h
- **THEN** the system validates total allocations (38+62+20=120) against approved estimate (120)
- **AND** displays overall GREEN indicator
- **AND** displays monthly warnings where allocations deviate from forecast
### Requirement: Real-time validation feedback
The system SHALL provide immediate validation feedback as allocations are created or modified.
#### Scenario: Immediate feedback on create
- **WHEN** a manager creates an allocation that causes a project to exceed approved estimate
- **THEN** the system immediately displays RED indicator on the allocation matrix
- **AND** the system shows tooltip "This allocation causes project over-allocation"
#### Scenario: Immediate feedback on update
- **WHEN** a manager increases an allocation and the team member becomes over-capacity
- **THEN** the system immediately updates the utilization percentage
- **AND** the system changes the team member's column header color to YELLOW or RED

View File

@@ -0,0 +1,132 @@
# authentication Specification
## Purpose
TBD - created by archiving change headroom-foundation. Update Purpose after archive.
## Requirements
### Requirement: User login
The system SHALL authenticate users with email and password and issue JWT tokens.
#### Scenario: Successful login
- **WHEN** a user submits valid email "john@example.com" and password
- **THEN** the system validates the credentials
- **AND** generates a JWT access token (60 minute TTL)
- **AND** generates a refresh token (7 day TTL)
- **AND** returns both tokens along with user details (name, email, role)
#### Scenario: Invalid credentials
- **WHEN** a user submits incorrect email or password
- **THEN** the system returns 401 Unauthorized error
- **AND** returns error message "Invalid credentials"
#### Scenario: Account locked or inactive
- **WHEN** a user with inactive account attempts to login
- **THEN** the system returns 403 Forbidden error
- **AND** returns error message "Account is inactive"
### Requirement: Token-based authentication
The system SHALL use JWT tokens for authenticating API requests.
#### Scenario: Authenticated API request
- **WHEN** a user sends an API request with valid JWT token in Authorization header
- **THEN** the system validates the token
- **AND** extracts user ID and role from token claims
- **AND** processes the request
#### Scenario: Expired token
- **WHEN** a user sends an API request with expired JWT token
- **THEN** the system returns 401 Unauthorized error
- **AND** returns error message "Token expired"
#### Scenario: Invalid token
- **WHEN** a user sends an API request with malformed or tampered JWT token
- **THEN** the system returns 401 Unauthorized error
- **AND** returns error message "Invalid token"
#### Scenario: Missing token
- **WHEN** a user sends an API request without Authorization header
- **THEN** the system returns 401 Unauthorized error
- **AND** returns error message "Authentication required"
### Requirement: Token refresh
The system SHALL allow users to obtain new access tokens using refresh tokens.
#### Scenario: Refresh access token
- **WHEN** a user submits a valid refresh token to POST /api/auth/refresh
- **THEN** the system validates the refresh token
- **AND** generates a new access token (60 minute TTL)
- **AND** rotates the refresh token (one-time use, issues new refresh token)
- **AND** returns the new access and refresh tokens
#### Scenario: Invalid refresh token
- **WHEN** a user submits an invalid or expired refresh token
- **THEN** the system returns 401 Unauthorized error
- **AND** returns error message "Invalid or expired refresh token"
### Requirement: User logout
The system SHALL allow users to logout and invalidate their tokens.
#### Scenario: Successful logout
- **WHEN** a user sends POST /api/auth/logout with their access token
- **THEN** the system invalidates the refresh token in Redis
- **AND** returns success message "Logged out successfully"
#### Scenario: Token invalidation
- **WHEN** a user logs out
- **THEN** the system removes the refresh token from Redis
- **AND** subsequent requests with the same tokens are rejected
### Requirement: JWT token structure
The system SHALL include user information in JWT token claims.
#### Scenario: Access token claims
- **WHEN** generating an access token
- **THEN** the token payload includes:
- sub (user UUID)
- role (user role: "superuser", "manager", "developer", "top_brass")
- permissions (array of permission strings)
- iat (issued at timestamp)
- exp (expiration timestamp, 60 minutes from iat)
- jti (unique token ID)
### Requirement: Authenticated user redirect
The system SHALL redirect authenticated users away from login page to dashboard.
#### Scenario: Authenticated user accesses login page
- **GIVEN** a user has valid access token in localStorage
- **WHEN** the user navigates to /login
- **THEN** the system detects the valid token
- **AND** redirects the user to /dashboard
- **AND** does not display the login form
#### Scenario: Auth state persists after page refresh
- **GIVEN** a user is logged in with valid tokens
- **WHEN** the user refreshes the page
- **THEN** the system reads tokens from localStorage
- **AND** restores authentication state
- **AND** displays the authenticated content (not blank page)
### Requirement: Refresh token storage
The system SHALL store refresh tokens in Redis with TTL.
#### Scenario: Store refresh token
- **WHEN** a user logs in
- **THEN** the system generates a refresh token UUID
- **AND** stores it in Redis with key "refresh_token:{user_id}:{token_uuid}"
- **AND** sets TTL to 7 days (10080 minutes)
#### Scenario: Validate refresh token
- **WHEN** a user submits a refresh token
- **THEN** the system checks if the token exists in Redis
- **AND** if found and not expired, allows token refresh
- **AND** if not found or expired, rejects the request
### Requirement: Token rotation
The system SHALL rotate refresh tokens on each refresh request.
#### Scenario: Rotate refresh token
- **WHEN** a user refreshes their access token
- **THEN** the system invalidates the old refresh token (deletes from Redis)
- **AND** generates a new refresh token
- **AND** stores the new refresh token in Redis
- **AND** returns the new refresh token to the user

View File

@@ -0,0 +1,113 @@
# capacity-planning Specification
## Purpose
TBD - created by archiving change headroom-foundation. Update Purpose after archive.
## Requirements
### Requirement: Calculate individual capacity
The system SHALL calculate individual team member capacity for a given month based on availability, holidays, PTO, and weekends.
#### Scenario: Calculate capacity for full month
- **WHEN** calculating capacity for a team member with full availability (1.0) for all working days in February 2026
- **AND** February has 20 working days (28 days - 8 weekend days)
- **AND** the team member has no PTO or holidays
- **THEN** the system calculates individual capacity as 20 person-days
#### Scenario: Calculate capacity with half-day availability
- **WHEN** a team member has availability of 0.5 for 10 working days in a month
- **THEN** the system calculates capacity as 5 person-days (10 days × 0.5)
#### Scenario: Calculate capacity with PTO
- **WHEN** a team member has PTO for 3 working days in a month
- **AND** the month has 22 working days
- **AND** the team member has full availability (1.0) for all other days
- **THEN** the system calculates capacity as 19 person-days (22 - 3 days PTO)
#### Scenario: Calculate capacity with holidays
- **WHEN** a month has 2 company holidays
- **AND** a team member has 22 working days after removing weekends
- **AND** the team member has full availability (1.0)
- **THEN** the system calculates capacity as 20 person-days (22 - 2 holidays)
#### Scenario: Calculate capacity with mixed availability
- **WHEN** a team member has 10 days at 1.0 availability, 5 days at 0.5 availability, and 3 days at 0 availability in a month
- **THEN** the system calculates capacity as 12.5 person-days (10×1.0 + 5×0.5 + 3×0)
### Requirement: Calculate team capacity
The system SHALL calculate total team capacity by summing individual capacities for all active team members.
#### Scenario: Calculate team capacity for month
- **WHEN** Team Member A has 20 person-days capacity
- **AND** Team Member B has 18 person-days capacity
- **AND** Team Member C has 15 person-days capacity
- **THEN** the system calculates team capacity as 53 person-days
#### Scenario: Exclude inactive team members from team capacity
- **WHEN** calculating team capacity
- **AND** one team member has active status set to false
- **THEN** the system excludes the inactive team member from the team capacity calculation
### Requirement: Calculate possible revenue
The system SHALL calculate possible revenue based on team capacity and hourly rates.
#### Scenario: Calculate possible revenue for team
- **WHEN** Team Member A has 160 hours capacity at $150/hour
- **AND** Team Member B has 144 hours capacity at $125/hour
- **AND** Team Member C has 120 hours capacity at $175/hour
- **THEN** the system calculates possible revenue as $63,000 (160×$150 + 144×$125 + 120×$175)
### Requirement: Track availability per day
The system SHALL allow setting daily availability as 0 (unavailable), 0.5 (half day), or 1.0 (full day).
#### Scenario: Set full day availability
- **WHEN** setting availability for a specific date to 1.0
- **THEN** the system records the team member as fully available for that day
#### Scenario: Set half day availability
- **WHEN** setting availability for a specific date to 0.5
- **THEN** the system records the team member as half-day available for that day
#### Scenario: Set unavailable
- **WHEN** setting availability for a specific date to 0
- **THEN** the system records the team member as unavailable for that day
#### Scenario: Reject invalid availability values
- **WHEN** attempting to set availability to a value other than 0, 0.5, or 1.0
- **THEN** the system rejects the input with validation error "Availability must be 0, 0.5, or 1.0"
### Requirement: Manage holidays
The system SHALL allow defining company-wide holidays that reduce available working days for all team members.
#### Scenario: Add company holiday
- **WHEN** an admin defines December 25, 2026 as a company holiday "Christmas Day"
- **THEN** the system marks that date as a non-working day for all team members
#### Scenario: Holidays affect capacity calculation
- **WHEN** calculating capacity for a month with 2 company holidays
- **THEN** the system automatically excludes those days from all team members' capacity calculations
### Requirement: Manage PTO requests
The system SHALL allow team members to request PTO which reduces their individual capacity.
#### Scenario: Submit PTO request
- **WHEN** a team member submits PTO for February 10-12, 2026
- **THEN** the system creates a PTO record with start date, end date, and status "approved"
#### Scenario: Approve PTO request
- **WHEN** a manager approves a PTO request
- **THEN** the system updates the PTO status to "approved"
- **AND** the system automatically reduces the team member's capacity for those dates to 0
#### Scenario: Delete PTO request
- **WHEN** a manager deletes an existing PTO request
- **THEN** the PTO record is removed
- **AND** individual, team, and revenue capacity caches for affected months are refreshed
#### Scenario: PTO affects capacity calculation
- **WHEN** calculating capacity for a team member with approved PTO for 3 days
- **THEN** the system excludes those 3 days from the capacity calculation
#### Scenario: Override PTO day availability
- **WHEN** a PTO day is manually set to half day availability (0.5)
- **THEN** the system keeps the PTO marker for that date
- **AND** the capacity calculation uses the explicit availability override instead of forcing 0

View File

@@ -0,0 +1,89 @@
# cost-reporting Specification
## Purpose
TBD - created by archiving change headroom-foundation. Update Purpose after archive.
## Requirements
### Requirement: Generate cost summary report
The system SHALL generate reports showing revenue forecasts based on allocations multiplied by hourly rates.
#### Scenario: View monthly cost report
- **WHEN** viewing cost report for February 2026
- **THEN** the system displays all projects with their allocated hours
- **AND** calculates revenue for each project based on team member hourly rates
- **AND** shows total revenue forecast for the month
#### Scenario: Cost breakdown by project
- **WHEN** viewing cost report for a specific project
- **THEN** the system displays allocation breakdown by team member
- **AND** shows hours allocated and hourly rate for each team member
- **AND** calculates total project cost as sum of (hours × rate) for all team members
### Requirement: Filter cost report by project
The system SHALL allow filtering cost reports by project, client, or type.
#### Scenario: Filter by project type
- **WHEN** filtering cost report to show only "Project" type (billable)
- **THEN** the system displays revenue forecast for billable projects only
- **AND** excludes "Support" type projects
#### Scenario: Group by client
- **WHEN** grouping cost report by client
- **THEN** the system displays total revenue forecast per client
- **AND** shows breakdown of projects per client
### Requirement: Filter cost report by team
The system SHALL allow filtering cost reports by team or team member.
#### Scenario: Cost report for team member
- **WHEN** filtering cost report to show allocations for "John Doe"
- **THEN** the system displays all projects where John Doe is allocated
- **AND** calculates John's contribution to revenue (his hours × his rate)
#### Scenario: Cost report for role
- **WHEN** filtering by "Backend Developer" role
- **THEN** the system displays revenue generated by all Backend Developers
- **AND** shows average hourly rate for the role
### Requirement: Calculate total possible revenue
The system SHALL calculate maximum possible revenue if all team capacity were utilized at 100%.
#### Scenario: Possible revenue calculation
- **WHEN** viewing cost summary
- **THEN** the system calculates total team capacity (all team members' available hours)
- **AND** multiplies by each team member's hourly rate
- **AND** displays "Possible Revenue: $X if fully utilized"
### Requirement: Calculate forecasted revenue
The system SHALL calculate forecasted revenue based on current allocations.
#### Scenario: Forecasted revenue based on allocations
- **WHEN** team has 1000 hours total capacity
- **AND** currently 850 hours are allocated across projects
- **AND** the weighted average hourly rate is $140
- **THEN** the system calculates forecasted revenue as $119,000 (850 × $140)
### Requirement: Show revenue variance
The system SHALL display variance between possible revenue and forecasted revenue.
#### Scenario: Revenue gap analysis
- **WHEN** possible revenue is $150,000
- **AND** forecasted revenue is $119,000
- **THEN** the system displays revenue gap of $31,000 (20.7% underutilization)
### Requirement: Multi-period cost forecast
The system SHALL generate cost forecasts across multiple months.
#### Scenario: Quarter revenue forecast
- **WHEN** viewing cost report for Q1 2026 (Jan-Mar)
- **THEN** the system displays monthly revenue forecast for each month
- **AND** calculates total Q1 revenue forecast
- **AND** shows monthly variance from possible revenue
### Requirement: Export cost data
The system SHALL allow exporting cost report data (deferred to Phase 2 for PDF/CSV).
#### Scenario: View cost data on screen (MVP)
- **WHEN** viewing cost report
- **THEN** the system displays all cost data on screen in tabular format
- **AND** PDF/CSV export buttons are not available (Phase 2 feature)

View File

@@ -0,0 +1,74 @@
# forecast-reporting Specification
## Purpose
TBD - created by archiving change headroom-foundation. Update Purpose after archive.
## Requirements
### Requirement: Generate multi-period forecast report
The system SHALL generate forecast reports showing allocations and revenue projections across multiple months.
#### Scenario: View 3-month forecast
- **WHEN** a manager requests a forecast report for February-April 2026
- **THEN** the system displays all projects with allocations in that period
- **AND** for each project shows month-by-month allocation breakdown
- **AND** calculates revenue forecast based on allocations × hourly rates
#### Scenario: Forecast includes variance indicators
- **WHEN** viewing the forecast report
- **THEN** the system shows forecasted hours vs approved estimate for each project
- **AND** displays GREEN/YELLOW/RED indicators for over/under-allocation
### Requirement: Filter forecast by project
The system SHALL allow filtering forecast reports by project, status, or type.
#### Scenario: Filter by project status
- **WHEN** filtering forecast report to show only "In-Progress" projects
- **THEN** the system displays only projects with that status
#### Scenario: Filter by project type
- **WHEN** filtering forecast report to show only "Project" type (billable)
- **THEN** the system excludes "Support" type projects from the report
### Requirement: Filter forecast by team
The system SHALL allow filtering forecast reports by team or team member.
#### Scenario: Filter by team member
- **WHEN** filtering forecast report to show allocations for "John Doe"
- **THEN** the system displays only projects where John Doe has allocations
#### Scenario: Filter by role/team
- **WHEN** filtering forecast report to show allocations for "Backend Developer" role
- **THEN** the system displays allocations for all team members with that role
### Requirement: Forecast revenue calculation
The system SHALL calculate revenue forecasts based on allocations multiplied by team member hourly rates.
#### Scenario: Calculate monthly revenue forecast
- **WHEN** a project has allocations: Developer A 40h @ $150/h, Developer B 30h @ $125/h
- **THEN** the system calculates monthly revenue forecast as $9,750 (40×$150 + 30×$125)
#### Scenario: Calculate total revenue forecast for period
- **WHEN** viewing forecast for Feb-Apr
- **AND** total allocations are: Feb $9,750, Mar $12,000, Apr $6,000
- **THEN** the system calculates total period revenue forecast as $27,750
### Requirement: Forecast summary aggregations
The system SHALL provide summary aggregations across all projects in the forecast.
#### Scenario: Total approved hours vs allocated hours
- **WHEN** viewing forecast summary
- **THEN** the system displays total approved estimate across all projects
- **AND** displays total allocated hours across all projects
- **AND** shows overall variance percentage
#### Scenario: Revenue forecast summary
- **WHEN** viewing forecast summary
- **THEN** the system displays total possible revenue (if all projects delivered at 100% allocation)
- **AND** displays current forecasted revenue based on actual allocations
### Requirement: Customizable date range
The system SHALL allow selecting custom date ranges for forecast reports.
#### Scenario: Select date range
- **WHEN** a manager selects "From: 2026-02" and "To: 2026-06"
- **THEN** the system generates forecast for those 5 months

View File

@@ -0,0 +1,123 @@
# master-data-management Specification
## Purpose
TBD - created by archiving change headroom-foundation. Update Purpose after archive.
## Requirements
### Requirement: Manage roles
The system SHALL allow Superusers to configure team member roles.
#### Scenario: Create new role
- **WHEN** a Superuser creates a role "DevOps Engineer"
- **THEN** the system stores the role
- **AND** the role becomes available for team member assignment
#### Scenario: Update role
- **WHEN** a Superuser updates a role description
- **THEN** the system updates the role
- **AND** existing team members with that role are not affected
#### Scenario: Cannot delete role in use
- **WHEN** a Superuser attempts to delete a role that is assigned to team members
- **THEN** the system rejects the deletion with error "Cannot delete role in use by team members"
#### Scenario: View roles list
- **WHEN** a user requests the list of roles
- **THEN** the system returns all configured roles
### Requirement: Manage project statuses
The system SHALL allow Superusers to configure project status options.
#### Scenario: Create custom status
- **WHEN** a Superuser creates a new status "Client Review"
- **AND** sets the order as 5 (between "Estimate Approved" and "Funded")
- **THEN** the system adds the status to the workflow
#### Scenario: Set status as billable or non-billable
- **WHEN** configuring a status
- **THEN** the Superuser can mark it as billable (TRUE) or non-billable (FALSE)
- **AND** non-billable statuses may exclude projects from revenue forecasts
#### Scenario: Reorder statuses
- **WHEN** a Superuser changes the order of statuses
- **THEN** the system updates the status sequence
- **AND** project workflow reflects the new order
### Requirement: Manage project types
The system SHALL allow Superusers to configure project types.
#### Scenario: Default project types
- **WHEN** the system is initialized
- **THEN** it includes default types: "Project" (billable) and "Support" (ongoing ops)
#### Scenario: Create custom project type
- **WHEN** a Superuser creates a new type "Internal Initiative"
- **THEN** the system stores the type
- **AND** the type becomes available when creating projects
### Requirement: Manage availability options
The system SHALL enforce availability values as 0, 0.5, or 1.0.
#### Scenario: Availability options are fixed
- **WHEN** setting team member availability
- **THEN** the system restricts values to 0, 0.5, or 1.0
- **AND** rejects any other value
#### Scenario: Availability options are documented
- **WHEN** a user views the availability field
- **THEN** the system displays help text:
- "0 = Unavailable (PTO, holiday)"
- "0.5 = Half day"
- "1.0 = Full day"
### Requirement: Seed master data
The system SHALL provide initial master data on installation.
#### Scenario: Seed roles
- **WHEN** the system is installed
- **THEN** it creates default roles:
- Frontend Developer
- Backend Developer
- QA Engineer
- DevOps Engineer
- UX Designer
- Project Manager
- Architect
#### Scenario: Seed project statuses
- **WHEN** the system is installed
- **THEN** it creates default statuses with correct order:
1. NA/Support
2. Initial
3. Gathering Estimates
4. Estimate Pending Approval
5. Estimate Rework
6. Estimate Approved
7. Funded
8. Scheduled
9. In-Progress
10. Ready for Prod
11. Done
12. On-Hold
13. Cancelled
#### Scenario: Seed project types
- **WHEN** the system is installed
- **THEN** it creates default types:
- Project (billable)
- Support (ongoing ops)
### Requirement: Master data API endpoints
The system SHALL provide read-only API endpoints for master data.
#### Scenario: Get roles
- **WHEN** any authenticated user requests GET /api/master-data/roles
- **THEN** the system returns the list of all roles
#### Scenario: Get project statuses
- **WHEN** any authenticated user requests GET /api/master-data/statuses
- **THEN** the system returns the list of all project statuses in order
#### Scenario: Get project types
- **WHEN** any authenticated user requests GET /api/master-data/types
- **THEN** the system returns the list of all project types

View File

@@ -0,0 +1,104 @@
# project-lifecycle Specification
## Purpose
TBD - created by archiving change headroom-foundation. Update Purpose after archive.
## Requirements
### Requirement: Create project
The system SHALL allow authorized users to create projects with project code, title, type, and status.
#### Scenario: Create new project
- **WHEN** a manager creates a project with code "PROJ-001", title "Client Dashboard Redesign", and type "Project"
- **THEN** the system creates the project with initial status "Initial"
- **AND** the system assigns a unique identifier to the project
#### Scenario: Project code must be unique
- **WHEN** attempting to create a project with a code that already exists
- **THEN** the system rejects the request with validation error "Project code must be unique"
### Requirement: Project status state machine
The system SHALL enforce project status transitions according to defined workflow states.
#### Scenario: Valid status transition
- **WHEN** a project in "Initial" status transitions to "Gathering Estimates"
- **THEN** the system updates the project status
#### Scenario: Project reaches Estimate Approved
- **WHEN** a project transitions to "Estimate Approved" status
- **THEN** the system requires approved estimate to be set
- **AND** the approved estimate must be greater than 0
#### Scenario: Project workflow progression
- **WHEN** a project progresses through statuses: Initial → Gathering Estimates → Estimate Pending Approval → Estimate Approved → Funded → Scheduled → In-Progress → Ready for Prod → Done
- **THEN** the system allows each transition in sequence
#### Scenario: Estimate rework path
- **WHEN** a project in "Estimate Pending Approval" status requires changes
- **THEN** the system allows transition back to "Estimate Rework" status
- **AND** from "Estimate Rework" the project can return to "Estimate Pending Approval"
#### Scenario: Project on hold
- **WHEN** a project is placed "On-Hold" from any active status
- **THEN** the system allows the transition
- **AND** allocations for future months are flagged but not deleted
#### Scenario: Project cancelled
- **WHEN** a project is marked as "Cancelled"
- **THEN** the system prevents new allocations
- **AND** existing allocations are preserved for historical tracking
### Requirement: Manage approved estimate
The system SHALL track the total approved billable hours for each project.
#### Scenario: Set approved estimate
- **WHEN** a project reaches "Estimate Approved" status with approved estimate of 120 hours
- **THEN** the system stores the approved estimate
- **AND** the approved estimate becomes the baseline for allocation validation
#### Scenario: Update approved estimate
- **WHEN** a manager updates the approved estimate from 120 to 150 hours
- **THEN** the system updates the approved estimate
- **AND** the system re-validates all allocations against the new estimate
### Requirement: Manage forecasted effort
The system SHALL track month-by-month breakdown of forecasted effort for each project.
#### Scenario: Set forecasted effort
- **WHEN** a manager sets forecasted effort for a 120-hour project as: February 40h, March 60h, April 20h
- **THEN** the system stores the forecasted effort as JSON: {"2026-02": 40, "2026-03": 60, "2026-04": 20}
#### Scenario: Forecasted effort must equal approved estimate
- **WHEN** the sum of forecasted effort (40 + 60 + 20 = 120) equals the approved estimate (120)
- **THEN** the system accepts the forecasted effort
#### Scenario: Forecasted effort validation fails
- **WHEN** the sum of forecasted effort (40 + 60 + 30 = 130) exceeds the approved estimate (120) by more than 5%
- **THEN** the system rejects the forecasted effort with validation error "Forecasted effort exceeds approved estimate"
#### Scenario: Under-forecasted effort
- **WHEN** the sum of forecasted effort (40 + 50 + 10 = 100) is less than the approved estimate (120)
- **THEN** the system displays a YELLOW warning "Under-forecasted by 20 hours"
### Requirement: Distinguish project types
The system SHALL differentiate between "Project" (billable) and "Support" (ongoing ops) project types.
#### Scenario: Billable project
- **WHEN** a project is created with type "Project"
- **THEN** the system tracks it as billable work
- **AND** it appears in revenue forecasts
#### Scenario: Support project
- **WHEN** a project is created with type "Support"
- **THEN** the system tracks it as ongoing operations
- **AND** it appears in capacity allocation but may have different reporting treatment
### Requirement: Cannot allocate to completed or cancelled projects
The system SHALL prevent new allocations to projects in "Done" or "Cancelled" status.
#### Scenario: Attempt to allocate to done project
- **WHEN** attempting to create an allocation for a project with status "Done"
- **THEN** the system rejects the allocation with error "Cannot allocate to completed projects"
#### Scenario: Attempt to allocate to cancelled project
- **WHEN** attempting to create an allocation for a project with status "Cancelled"
- **THEN** the system rejects the allocation with error "Cannot allocate to cancelled projects"