Files
xer-mcp/specs/001-schedule-tools/data-model.md
Bill Ballou a7b6b76db8 feat: add milestone_type field to distinguish start/finish milestones
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')
2026-01-08 12:18:34 -05:00

8.7 KiB

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:

# 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

-- 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