Add milestone_type field to milestone queries that indicates whether
a milestone is a start milestone ('start') or finish milestone ('finish').
Changes:
- Add milestone_type column to activities table schema
- Parse milestone_type from XER TASK table (MS_Start/MS_Finish)
- Include milestone_type in list_milestones response
- Update contract tests for milestone_type field
- Update specs, contracts, and documentation
The milestone_type is determined by:
1. Explicit milestone_type field in XER (MS_Start -> 'start', MS_Finish -> 'finish')
2. Derived from task_type (TT_Mile -> 'start', TT_FinMile -> 'finish')
254 lines
8.7 KiB
Markdown
254 lines
8.7 KiB
Markdown
# Data Model: Project Schedule Tools
|
|
|
|
**Date**: 2026-01-06
|
|
**Branch**: `001-schedule-tools`
|
|
|
|
## Entity Overview
|
|
|
|
```
|
|
┌─────────────┐ ┌─────────────┐
|
|
│ Project │───────│ WBS │
|
|
└─────────────┘ └─────────────┘
|
|
│ │
|
|
│ │
|
|
▼ ▼
|
|
┌─────────────┐ ┌─────────────┐
|
|
│ Activity │◄──────│ Relationship│
|
|
└─────────────┘ └─────────────┘
|
|
│
|
|
▼
|
|
┌─────────────┐
|
|
│ Calendar │ (internal only)
|
|
└─────────────┘
|
|
```
|
|
|
|
## Entities
|
|
|
|
### Project
|
|
|
|
Top-level container representing a P6 project.
|
|
|
|
| Field | Type | Required | Description |
|
|
|-------|------|----------|-------------|
|
|
| proj_id | string | Yes | Unique project identifier from P6 |
|
|
| proj_short_name | string | Yes | Short display name |
|
|
| plan_start_date | datetime | No | Planned start date |
|
|
| plan_end_date | datetime | No | Planned end date |
|
|
| loaded_at | datetime | Yes | When file was loaded into server |
|
|
|
|
**XER Source**: `PROJECT` table
|
|
|
|
**Validation Rules**:
|
|
- proj_id must be unique within loaded data
|
|
- proj_short_name must not be empty
|
|
|
|
### Activity
|
|
|
|
A unit of work in the schedule.
|
|
|
|
| Field | Type | Required | Description |
|
|
|-------|------|----------|-------------|
|
|
| task_id | string | Yes | Unique activity identifier |
|
|
| proj_id | string | Yes | Parent project reference |
|
|
| wbs_id | string | No | WBS element reference |
|
|
| task_code | string | Yes | User-visible activity code |
|
|
| task_name | string | Yes | Activity description |
|
|
| task_type | enum | Yes | TT_Task, TT_Mile, TT_LOE, TT_WBS, TT_Rsrc |
|
|
| milestone_type | enum | No | 'start' for start milestones, 'finish' for finish milestones, null for non-milestones |
|
|
| target_start_date | datetime | No | Planned start |
|
|
| target_end_date | datetime | No | Planned finish |
|
|
| early_start_date | datetime | No | Calculated early start (for driving computation) |
|
|
| early_end_date | datetime | No | Calculated early finish (for driving computation) |
|
|
| act_start_date | datetime | No | Actual start |
|
|
| act_end_date | datetime | No | Actual finish |
|
|
| total_float_hr_cnt | float | No | Total float in hours |
|
|
| driving_path_flag | boolean | No | True if on critical path |
|
|
| status_code | enum | No | TK_NotStart, TK_Active, TK_Complete |
|
|
|
|
**XER Source**: `TASK` table
|
|
|
|
**Validation Rules**:
|
|
- task_id must be unique
|
|
- task_code must not be empty
|
|
- task_name must not be empty
|
|
- If act_start_date exists, target_start_date should exist
|
|
- If act_end_date exists, act_start_date must exist
|
|
|
|
**State Transitions**:
|
|
```
|
|
TK_NotStart ──[actual start recorded]──► TK_Active
|
|
TK_Active ──[actual finish recorded]──► TK_Complete
|
|
```
|
|
|
|
### Relationship
|
|
|
|
A dependency link between two activities.
|
|
|
|
| Field | Type | Required | Description |
|
|
|-------|------|----------|-------------|
|
|
| task_pred_id | string | Yes | Unique relationship identifier |
|
|
| task_id | string | Yes | Successor activity (the one being constrained) |
|
|
| pred_task_id | string | Yes | Predecessor activity (the one constraining) |
|
|
| pred_type | enum | Yes | PR_FS, PR_SS, PR_FF, PR_SF |
|
|
| lag_hr_cnt | float | No | Lag time in hours (can be negative) |
|
|
| driving | boolean | No | **Computed** - True if this relationship determines successor's early dates |
|
|
|
|
**XER Source**: `TASKPRED` table
|
|
|
|
**Driving Flag Computation** (not stored, computed at query time):
|
|
|
|
The `driving` flag indicates whether this predecessor relationship constrains the successor activity's early start date. It is computed by comparing dates:
|
|
|
|
```python
|
|
# For FS (Finish-to-Start) relationships:
|
|
driving = (predecessor.early_end_date + lag_hr_cnt ≈ successor.early_start_date)
|
|
|
|
# With 1-hour tolerance for floating point arithmetic
|
|
```
|
|
|
|
This requires JOIN on activity dates when querying relationships.
|
|
|
|
**Relationship Types**:
|
|
| Code | Name | Meaning |
|
|
|------|------|---------|
|
|
| PR_FS | Finish-to-Start | Successor starts after predecessor finishes |
|
|
| PR_SS | Start-to-Start | Successor starts after predecessor starts |
|
|
| PR_FF | Finish-to-Finish | Successor finishes after predecessor finishes |
|
|
| PR_SF | Start-to-Finish | Successor finishes after predecessor starts |
|
|
|
|
**Validation Rules**:
|
|
- task_id and pred_task_id must reference existing activities
|
|
- task_id must not equal pred_task_id (no self-reference)
|
|
- pred_type must be one of the four valid types
|
|
|
|
### WBS (Work Breakdown Structure)
|
|
|
|
Hierarchical organization of activities.
|
|
|
|
| Field | Type | Required | Description |
|
|
|-------|------|----------|-------------|
|
|
| wbs_id | string | Yes | Unique WBS identifier |
|
|
| proj_id | string | Yes | Parent project reference |
|
|
| parent_wbs_id | string | No | Parent WBS element (null for root) |
|
|
| wbs_short_name | string | Yes | Short code |
|
|
| wbs_name | string | No | Full description |
|
|
| wbs_level | integer | No | Hierarchy depth (0 = root) |
|
|
|
|
**XER Source**: `PROJWBS` table
|
|
|
|
**Validation Rules**:
|
|
- wbs_id must be unique
|
|
- parent_wbs_id must reference existing WBS or be null
|
|
- No circular parent references
|
|
|
|
### Calendar (Internal)
|
|
|
|
Work schedule definition. Not exposed via MCP tools.
|
|
|
|
| Field | Type | Required | Description |
|
|
|-------|------|----------|-------------|
|
|
| clndr_id | string | Yes | Unique calendar identifier |
|
|
| clndr_name | string | Yes | Calendar name |
|
|
| day_hr_cnt | float | No | Hours per work day |
|
|
| week_hr_cnt | float | No | Hours per work week |
|
|
|
|
**XER Source**: `CALENDAR` table
|
|
|
|
**Note**: Parsed and stored but not exposed as queryable. Used internally if duration calculations are needed in future.
|
|
|
|
### PaginationMetadata
|
|
|
|
Response wrapper for paginated queries.
|
|
|
|
| Field | Type | Required | Description |
|
|
|-------|------|----------|-------------|
|
|
| total_count | integer | Yes | Total items matching query |
|
|
| offset | integer | Yes | Current offset (0-based) |
|
|
| limit | integer | Yes | Items per page |
|
|
| has_more | boolean | Yes | True if more items exist |
|
|
|
|
## SQLite Schema
|
|
|
|
```sql
|
|
-- Projects
|
|
CREATE TABLE projects (
|
|
proj_id TEXT PRIMARY KEY,
|
|
proj_short_name TEXT NOT NULL,
|
|
plan_start_date TEXT,
|
|
plan_end_date TEXT,
|
|
loaded_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
);
|
|
|
|
-- Activities
|
|
CREATE TABLE activities (
|
|
task_id TEXT PRIMARY KEY,
|
|
proj_id TEXT NOT NULL,
|
|
wbs_id TEXT,
|
|
task_code TEXT NOT NULL,
|
|
task_name TEXT NOT NULL,
|
|
task_type TEXT NOT NULL,
|
|
target_start_date TEXT,
|
|
target_end_date TEXT,
|
|
early_start_date TEXT, -- Used for driving relationship computation
|
|
early_end_date TEXT, -- Used for driving relationship computation
|
|
act_start_date TEXT,
|
|
act_end_date TEXT,
|
|
total_float_hr_cnt REAL,
|
|
driving_path_flag INTEGER DEFAULT 0,
|
|
status_code TEXT,
|
|
FOREIGN KEY (proj_id) REFERENCES projects(proj_id),
|
|
FOREIGN KEY (wbs_id) REFERENCES wbs(wbs_id)
|
|
);
|
|
|
|
-- Relationships
|
|
CREATE TABLE relationships (
|
|
task_pred_id TEXT PRIMARY KEY,
|
|
task_id TEXT NOT NULL,
|
|
pred_task_id TEXT NOT NULL,
|
|
pred_type TEXT NOT NULL,
|
|
lag_hr_cnt REAL DEFAULT 0,
|
|
FOREIGN KEY (task_id) REFERENCES activities(task_id),
|
|
FOREIGN KEY (pred_task_id) REFERENCES activities(task_id)
|
|
);
|
|
|
|
-- WBS
|
|
CREATE TABLE wbs (
|
|
wbs_id TEXT PRIMARY KEY,
|
|
proj_id TEXT NOT NULL,
|
|
parent_wbs_id TEXT,
|
|
wbs_short_name TEXT NOT NULL,
|
|
wbs_name TEXT,
|
|
FOREIGN KEY (proj_id) REFERENCES projects(proj_id),
|
|
FOREIGN KEY (parent_wbs_id) REFERENCES wbs(wbs_id)
|
|
);
|
|
|
|
-- Calendars (internal)
|
|
CREATE TABLE calendars (
|
|
clndr_id TEXT PRIMARY KEY,
|
|
clndr_name TEXT NOT NULL,
|
|
day_hr_cnt REAL,
|
|
week_hr_cnt REAL
|
|
);
|
|
|
|
-- Indexes
|
|
CREATE INDEX idx_activities_proj ON activities(proj_id);
|
|
CREATE INDEX idx_activities_wbs ON activities(wbs_id);
|
|
CREATE INDEX idx_activities_type ON activities(task_type);
|
|
CREATE INDEX idx_activities_critical ON activities(driving_path_flag) WHERE driving_path_flag = 1;
|
|
CREATE INDEX idx_activities_dates ON activities(target_start_date, target_end_date);
|
|
CREATE INDEX idx_relationships_task ON relationships(task_id);
|
|
CREATE INDEX idx_relationships_pred ON relationships(pred_task_id);
|
|
CREATE INDEX idx_wbs_parent ON wbs(parent_wbs_id);
|
|
CREATE INDEX idx_wbs_proj ON wbs(proj_id);
|
|
```
|
|
|
|
## Date Handling
|
|
|
|
All dates stored as ISO8601 strings in SQLite for portability:
|
|
- Format: `YYYY-MM-DDTHH:MM:SS`
|
|
- Timezone: Preserved from XER if present, otherwise naive
|
|
- Comparisons: String comparison works correctly with ISO8601
|
|
|
|
XER date format: `YYYY-MM-DD HH:MM` (space-separated, no seconds)
|
|
Conversion on import: Add `:00` for seconds, replace space with `T`
|