feat: add driving flag to relationship query responses
Add computed driving flag to all relationship queries (list_relationships, get_predecessors, get_successors). A relationship is marked as driving when the predecessor's early end date plus lag determines the successor's early start date. Changes: - Add early_start_date and early_end_date columns to activities schema - Parse early dates from TASK table in XER files - Implement is_driving_relationship() helper with 24hr tolerance for calendar gaps - Update all relationship queries to compute and return driving flag - Add contract and unit tests for driving flag functionality - Update spec, contracts, and documentation
This commit is contained in:
@@ -1,44 +1,47 @@
|
||||
# Implementation Plan: Project Schedule Tools
|
||||
# Implementation Plan: Add Driving Flag to Relationships
|
||||
|
||||
**Branch**: `001-schedule-tools` | **Date**: 2026-01-06 | **Spec**: [spec.md](./spec.md)
|
||||
**Input**: Feature specification from `/specs/001-schedule-tools/spec.md`
|
||||
|
||||
## Summary
|
||||
|
||||
Build an MCP server that parses Primavera P6 XER files and exposes schedule data (activities, relationships, project summaries) through MCP tools. The server uses stdio transport, loads XER data into SQLite for efficient querying, and implements pagination to prevent context overflow for AI assistants.
|
||||
Extend the existing XER MCP Server implementation to include the "driving" flag in all relationship query responses. This requires updating the database schema, XER parser, and query functions to extract and return the driving relationship indicator from Primavera P6 schedule data.
|
||||
|
||||
## Technical Context
|
||||
|
||||
**Language/Version**: Python 3.14
|
||||
**Package Manager**: uv
|
||||
**Primary Dependencies**: mcp (MCP SDK), sqlite3 (stdlib)
|
||||
**Storage**: SQLite (in-memory or file-based per session)
|
||||
**Testing**: pytest with pytest-asyncio for async MCP handlers
|
||||
**Target Platform**: Linux/macOS/Windows (cross-platform CLI)
|
||||
**Transport**: stdio (standard input/output)
|
||||
**Project Type**: Single project
|
||||
**Performance Goals**: Load 10,000 activities in <5 seconds; query response <1 second
|
||||
**Constraints**: Default 100-item pagination limit; single-user operation
|
||||
**Storage**: SQLite in-memory database
|
||||
**Testing**: pytest, pytest-asyncio
|
||||
**Target Platform**: Linux server (local MCP server)
|
||||
**Project Type**: single
|
||||
**Performance Goals**: Query response < 1 second, load < 5 seconds for 10k activities
|
||||
**Constraints**: Single-user operation, in-memory storage for loaded XER data
|
||||
**Scale/Scope**: Files up to 50,000 activities
|
||||
|
||||
## Constitution Check
|
||||
|
||||
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
|
||||
|
||||
| Principle | Requirement | Status | Notes |
|
||||
|-----------|-------------|--------|-------|
|
||||
| I. Test-First Development | TDD mandatory; tests fail before implementation | ✅ PASS | Plan includes contract tests for MCP tools, integration tests for XER parsing |
|
||||
| II. Extensibility Architecture | Pluggable handlers; separated concerns | ✅ PASS | XER parser separated from MCP layer; table handlers pluggable |
|
||||
| III. MCP Protocol Compliance | Complete JSON schemas; proper error format | ✅ PASS | All tools will have full schemas; errors follow MCP format |
|
||||
| IV. XER Format Fidelity | No data loss; preserve precision | ✅ PASS | SQLite preserves all parsed data; unknown tables stored |
|
||||
| V. Semantic Versioning | SemVer for releases | ✅ PASS | Will use 0.1.0 for initial release |
|
||||
| Principle | Status | Evidence/Notes |
|
||||
|-----------|--------|----------------|
|
||||
| I. Test-First Development | ✅ PASS | TDD required - write failing tests first for driving flag in relationship responses |
|
||||
| II. Extensibility Architecture | ✅ PASS | Current design already separates parser → db → query → tool layers |
|
||||
| III. MCP Protocol Compliance | ✅ PASS | Tool schemas already JSON Schema compliant; no schema changes needed |
|
||||
| IV. XER Format Fidelity | ✅ PASS | Must parse `driving_flag` from TASKPRED table in XER files |
|
||||
| V. Semantic Versioning | ✅ PASS | Adding new field to response is backward-compatible (MINOR) |
|
||||
|
||||
**Technical Standards Compliance**:
|
||||
- Python 3.14 ✅
|
||||
- Type hints throughout ✅
|
||||
- Formatting via ruff ✅
|
||||
- Dependencies pinned in pyproject.toml ✅
|
||||
- Console logging ✅
|
||||
**Pre-design Status**: All gates PASS
|
||||
|
||||
**Post-design Status** (re-evaluated after Phase 1):
|
||||
|
||||
| Principle | Status | Evidence/Notes |
|
||||
|-----------|--------|----------------|
|
||||
| I. Test-First Development | ✅ PASS | Tests will verify: (1) early dates parsed from TASK, (2) driving computed correctly, (3) driving included in all relationship responses |
|
||||
| II. Extensibility Architecture | ✅ PASS | Driving computation is isolated in query layer; no changes to parser or model structure |
|
||||
| III. MCP Protocol Compliance | ✅ PASS | Response schema updated in contracts/mcp-tools.json |
|
||||
| IV. XER Format Fidelity | ✅ PASS | Research confirmed: driving is computed from schedule dates (matching P6 behavior), not stored in XER |
|
||||
| V. Semantic Versioning | ✅ PASS | Adding `driving` field is backward-compatible (MINOR version bump)
|
||||
|
||||
## Project Structure
|
||||
|
||||
@@ -50,8 +53,8 @@ specs/001-schedule-tools/
|
||||
├── research.md # Phase 0 output
|
||||
├── data-model.md # Phase 1 output
|
||||
├── quickstart.md # Phase 1 output
|
||||
├── contracts/ # Phase 1 output (MCP tool schemas)
|
||||
└── tasks.md # Phase 2 output (/speckit.tasks command)
|
||||
├── contracts/ # Phase 1 output
|
||||
└── tasks.md # Phase 2 output (/speckit.tasks)
|
||||
```
|
||||
|
||||
### Source Code (repository root)
|
||||
@@ -59,69 +62,40 @@ specs/001-schedule-tools/
|
||||
```text
|
||||
src/
|
||||
├── xer_mcp/
|
||||
│ ├── __init__.py
|
||||
│ ├── server.py # MCP server entry point (stdio)
|
||||
│ ├── tools/ # MCP tool implementations
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── load_xer.py
|
||||
│ │ ├── list_activities.py
|
||||
│ │ ├── get_activity.py
|
||||
│ │ ├── list_relationships.py
|
||||
│ │ ├── get_predecessors.py
|
||||
│ │ ├── get_successors.py
|
||||
│ │ ├── get_project_summary.py
|
||||
│ │ ├── list_milestones.py
|
||||
│ │ └── get_critical_path.py
|
||||
│ ├── parser/ # XER file parsing
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── xer_parser.py # Main parser
|
||||
│ │ └── table_handlers/ # Pluggable table handlers
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── base.py # Abstract base class
|
||||
│ │ ├── project.py
|
||||
│ │ ├── task.py
|
||||
│ │ ├── taskpred.py
|
||||
│ │ ├── projwbs.py
|
||||
│ │ └── calendar.py
|
||||
│ ├── db/ # SQLite database layer
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── schema.py # Table definitions
|
||||
│ │ ├── loader.py # Load parsed data into SQLite
|
||||
│ │ └── queries.py # Query functions with pagination
|
||||
│ └── models/ # Data models (dataclasses)
|
||||
│ ├── __init__.py
|
||||
│ ├── project.py
|
||||
│ ├── activity.py
|
||||
│ ├── relationship.py
|
||||
│ └── pagination.py
|
||||
│ ├── models/
|
||||
│ │ └── relationship.py # Add driving field to dataclass
|
||||
│ ├── parser/
|
||||
│ │ └── table_handlers/
|
||||
│ │ └── taskpred.py # Parse driving_flag from XER
|
||||
│ ├── db/
|
||||
│ │ ├── schema.py # Add driving column to relationships table
|
||||
│ │ ├── loader.py # Store driving flag when loading
|
||||
│ │ └── queries.py # Return driving in relationship queries
|
||||
│ └── tools/
|
||||
│ ├── list_relationships.py # Response already uses query result
|
||||
│ ├── get_predecessors.py # Response already uses query result
|
||||
│ └── get_successors.py # Response already uses query result
|
||||
|
||||
tests/
|
||||
├── conftest.py # Shared fixtures (sample XER files)
|
||||
├── contract/ # MCP tool contract tests
|
||||
│ ├── test_load_xer.py
|
||||
│ ├── test_list_activities.py
|
||||
│ ├── test_get_activity.py
|
||||
│ ├── test_list_relationships.py
|
||||
│ ├── test_get_predecessors.py
|
||||
│ ├── test_get_successors.py
|
||||
│ ├── test_get_project_summary.py
|
||||
│ ├── test_list_milestones.py
|
||||
│ └── test_get_critical_path.py
|
||||
├── integration/ # End-to-end XER parsing tests
|
||||
│ ├── test_xer_parsing.py
|
||||
│ ├── test_multi_project.py
|
||||
│ └── test_edge_cases.py
|
||||
└── unit/ # Unit tests for parser, db, models
|
||||
├── test_parser.py
|
||||
├── test_table_handlers.py
|
||||
├── test_db_queries.py
|
||||
└── test_models.py
|
||||
|
||||
pyproject.toml # Project config with uv
|
||||
├── contract/
|
||||
│ ├── test_list_relationships.py # Verify driving flag in response
|
||||
│ ├── test_get_predecessors.py # Verify driving flag in response
|
||||
│ └── test_get_successors.py # Verify driving flag in response
|
||||
├── integration/
|
||||
│ └── test_xer_parsing.py # Verify driving flag parsed from XER
|
||||
└── unit/
|
||||
├── test_table_handlers.py # Test TASKPRED handler parses driving
|
||||
└── test_db_queries.py # Test queries return driving flag
|
||||
```
|
||||
|
||||
**Structure Decision**: Single project structure with clear separation between MCP tools, XER parsing, and SQLite database layers. The parser/table_handlers/ directory enables extensibility for future XER table types.
|
||||
**Structure Decision**: Single project structure (existing). No new directories needed.
|
||||
|
||||
## Complexity Tracking
|
||||
|
||||
No constitution violations requiring justification. Design follows all principles.
|
||||
> No constitution violations. Implementation is a focused enhancement to existing architecture.
|
||||
|
||||
| Item | Assessment |
|
||||
|------|------------|
|
||||
| Scope | Small - single field addition across 4 layers |
|
||||
| Risk | Low - additive change, no behavior modification |
|
||||
| Testing | Contract tests + unit tests for each layer |
|
||||
|
||||
Reference in New Issue
Block a user