Project Management
This document defines operations for managing projects within a Morphir workspace.
Overview
A project is a single Morphir package with its own morphir.toml configuration. Projects within a workspace can depend on each other and share resolved dependencies.
Project States
┌─────────────────┐
│ unloaded │◄──────────────┐
└────────┬────────┘ │
│ load │ unload
▼ │
┌─────────────────┐ │
│ loading │ │
└────────┬────────┘ │
│ │
┌──────────────┴──────────────┐ │
│ success │ failure │
▼ ▼ │
┌─────────────────┐ ┌─────────────────┐
│ ready │ │ error │
└────────┬────────┘ └─────────────────┘
│ source changed
▼
┌─────────────────┐
│ stale │
└────────┬────────┘
│ reload
└─────────────► loading
Types
ProjectState
/// Project lifecycle state within a workspace
pub type ProjectState {
/// Project metadata loaded, IR not compiled
Unloaded
/// Project is being compiled
Loading
/// Project IR is loaded and valid
Ready
/// Source files changed, needs recompilation
Stale
/// Project has compilation errors
Error
}
ProjectInfo
/// Project information and status
pub type ProjectInfo {
ProjectInfo(
/// Package name (e.g., "my-org/domain")
name: PackagePath,
/// Semantic version
version: SemVer,
/// Path relative to workspace root
path: String,
/// Current state
state: ProjectState,
/// Source directory within project
source_dir: String,
/// Project dependencies
dependencies: List(DependencyInfo),
)
}
Operations
Add Project
Adds an existing project directory to the workspace.
Behavior
- Verify project path exists and contains
morphir.toml - Parse project configuration
- Register project in workspace
- Update
morphir.toml - Return project info (state:
Unloaded)
WIT Interface
/// Add a project to the workspace
add-project: func(
/// Project name (package path)
name: package-path,
/// Project path (relative to workspace root)
path: string,
/// Initial version
version: semver,
/// Source directory
source-dir: string,
) -> result<project-info, workspace-error>;
JSON-RPC
Request:
{
"method": "workspace/addProject",
"params": {
"name": "my-org/new-service",
"path": "packages/new-service",
"version": "0.1.0",
"sourceDir": "src"
}
}
Response:
{
"result": {
"name": "my-org/new-service",
"version": "0.1.0",
"path": "packages/new-service",
"state": "unloaded",
"sourceDir": "src",
"dependencies": []
}
}
CLI
morphir workspace add packages/new-service
morphir workspace add --name my-org/new-service --path packages/new-service
Remove Project
Removes a project from the workspace (does not delete files).
Behavior
- Verify project exists in workspace
- Check for dependents (warn if other projects depend on it)
- Unload project if loaded
- Remove from workspace registry
- Update
morphir.toml
WIT Interface
/// Remove a project from the workspace
remove-project: func(
name: package-path,
) -> result<_, workspace-error>;
JSON-RPC
Request:
{
"method": "workspace/removeProject",
"params": {
"name": "my-org/old-service"
}
}
CLI
morphir workspace remove my-org/old-service
Get Project Info
Returns detailed information about a specific project.
WIT Interface
/// Get project info
get-project-info: func(
name: package-path,
) -> result<project-info, workspace-error>;
JSON-RPC
Request:
{
"method": "workspace/projectInfo",
"params": {
"name": "my-org/domain"
}
}
List Projects
Lists all projects in the workspace.
WIT Interface
/// List all projects
list-projects: func() -> result<list<project-info>, workspace-error>;
JSON-RPC
Request:
{
"method": "workspace/listProjects",
"params": {}
}
Response:
{
"result": [
{
"name": "my-org/core",
"version": "1.0.0",
"path": "packages/core",
"state": "ready",
"sourceDir": "src",
"dependencies": []
},
{
"name": "my-org/domain",
"version": "2.0.0",
"path": "packages/domain",
"state": "stale",
"sourceDir": "src",
"dependencies": [
{ "name": "my-org/core", "version": "1.0.0", "resolved": true }
]
}
]
}
Load Project
Compiles a project and loads its IR into memory.
Behavior
- Transition to
Loadingstate - Resolve project dependencies
- Compile source files to IR
- Validate IR
- Transition to
ReadyorErrorstate - Return distribution
WIT Interface
/// Load a project (parse and compile)
load-project: func(
name: package-path,
) -> result<distribution, workspace-error>;
JSON-RPC
Request:
{
"method": "workspace/loadProject",
"params": {
"name": "my-org/domain"
}
}
Response:
{
"result": {
"distribution": {
"Library": {
"package": { "name": "my-org/domain", "version": "2.0.0" },
"definition": { "..." }
}
},
"diagnostics": []
}
}
Unload Project
Releases a project's IR from memory.
Behavior
- Free IR memory
- Transition to
Unloadedstate - Retain project metadata
WIT Interface
/// Unload a project (free resources)
unload-project: func(
name: package-path,
) -> result<_, workspace-error>;
JSON-RPC
Request:
{
"method": "workspace/unloadProject",
"params": {
"name": "my-org/domain"
}
}
Reload Project
Recompiles a project (typically after source changes).
Behavior
- Same as
Load, but preserves previous IR until new compilation succeeds - Supports incremental compilation if available
WIT Interface
/// Reload a project (recompile)
reload-project: func(
name: package-path,
) -> result<distribution, workspace-error>;
JSON-RPC
Request:
{
"method": "workspace/reloadProject",
"params": {
"name": "my-org/domain"
}
}
Project Discovery
When a workspace is opened, projects are discovered by:
-
Explicit listing in
morphir.toml:[[projects]]
name = "my-org/core"
path = "packages/core" -
Glob patterns for automatic discovery:
[workspace]
project-patterns = ["packages/*", "libs/*"] -
Walking the directory tree for
morphir.tomlfiles
Dependency Order
Projects are loaded in dependency order:
my-org/core (no deps) → load first
my-org/domain (→ core) → load second
my-org/api (→ domain) → load third
Circular dependencies are detected and reported as errors.
Error Handling
| Error | Cause | Recovery |
|---|---|---|
ProjectNotFound | Project not in workspace | Check project name |
ProjectAlreadyExists | Duplicate project name | Use different name |
InvalidConfig | Bad morphir.toml | Fix configuration |
CompilationError | Source code errors | Fix source code |
DependencyError | Missing dependency | Add dependency |
Best Practices
- Lazy Loading: Only load projects when needed for operations
- Dependency Caching: Cache resolved dependencies to speed up loads
- Partial Builds: Allow building subset of projects
- State Recovery: Persist project states for daemon restart