Expand description
cpm-planner: a Critical Path Method (CPM) planner for agent work, exposed as a standalone MCP server and usable as a plain Rust library.
The CPM kernel does the forward pass (earliest start/finish), backward pass (latest start/finish), slack computation, critical-path identification, parallel batch grouping, and bottleneck (ROI) analysis.
On top of that kernel, BasicCpmPlanner implements the lock-aware
Planner trait: callers submit a PlanGraph,
then acquire / heartbeat / release locks on disjoint cohorts of deliverables
so that multiple workers can run in parallel without stepping on each other.
The same planner keeps named plan lines and variants (plan-as-code files
under .cpm-planner/plans/), frozen earned-value baselines and snapshots,
and durable state in SQLite (SqlitePlanStore). PlanServer surfaces
all of it as MCP tools (plan.submit, plan.acquire_cohort, plan.ev, …)
over stdio, so any MCP-speaking client — Claude Code, Cursor, a custom
orchestrator, or a praxec workflow connection — can drive it.
§Example
Schedule a graph, then lease and complete its first ready cohort:
use cpm_planner::plan::{AcquireRequest, CallerId, DeliverableStatus, PlanGraph};
use cpm_planner::ports::Planner;
use cpm_planner::{BasicCpmPlanner, MarkStatusRequest};
let graph: PlanGraph = serde_json::from_value(serde_json::json!({
"deliverables": [
{"id": "design", "owned_files": ["docs/design.md"], "prerequisites": [],
"estimated_effort_hours": 4.0},
{"id": "build", "owned_files": ["src/app.rs"], "prerequisites": ["design"],
"estimated_effort_hours": 8.0},
{"id": "docs", "owned_files": ["README.md"], "prerequisites": ["design"],
"estimated_effort_hours": 2.0}
]
}))?;
// Pure CPM: `__start__` and `__finish__` are synthetic zero-length endpoints.
let cpm = cpm_planner::schedule::compute_cpm(&graph)?;
assert_eq!(cpm.critical_path, ["__start__", "design", "build", "__finish__"]);
assert_eq!(cpm.critical_path_duration, 12.0);
// Lock-aware execution on a private in-memory store.
let runtime = tokio::runtime::Runtime::new()?;
runtime.block_on(async {
let planner = BasicCpmPlanner::new();
let plan_id = planner.submit_plan(graph).await?;
let caller = CallerId("worker-1".into());
let cohort = planner
.acquire_cohort(AcquireRequest::new(plan_id.clone(), caller.clone(), 4))
.await?;
let leased: Vec<&str> = cohort.rows.iter().map(|r| r.deliverable.id.as_str()).collect();
assert_eq!(leased, ["design"]); // `build` and `docs` wait on it
planner
.mark_status(MarkStatusRequest::new(
plan_id.clone(),
"design",
caller.clone(),
DeliverableStatus::Complete,
))
.await?;
// Completing `design` releases its lock and readies both dependents.
let next = planner
.acquire_cohort(AcquireRequest::new(plan_id, caller, 4))
.await?;
let mut leased: Vec<&str> = next.rows.iter().map(|r| r.deliverable.id.as_str()).collect();
leased.sort();
assert_eq!(leased, ["build", "docs"]);
Ok::<_, anyhow::Error>(())
})?;§Layout
plan— the wire/domain model (deliverables, cohorts, locks, errors).ports— thePlannertrait.algorithm/task— the pure CPM kernel and its internal model (theTasktypes carry ES/EF/LS/LF/slack/batching state the wire model doesn’t need to expose).schedule—schedule::compute_cpm, the CPM run shared by submit, status and the analysis tools.planner—BasicCpmPlanner, the lock-aware implementation (including the earned-value operations).plan_store— durable SQLite persistence (plans, statuses, cohort locks, submit dedup, portfolio lines/variants/revisions, earned value) and the cross-process atomicity mechanism.project— project-root discovery and confined plan-file I/O.edits,revise— structured graph edits and progress-preserving revision.lint,simulate,resource_schedule,monte_carlo,compare,metrics,drag,risk,network_health— the read-only analysis tools and the plan scorecard.earned_value— the pure PV / EV / AC engine.review,llm— the optionalplan.reviewand its OpenRouter / Jev judge.server— the MCP tool façade.skills— the embedded agent skills andcpm-planner skills install.audit— the lock-lifecycle audit surface.
See docs/architecture.md in the repository for the request flow, the
store schema and the concurrency model.
This crate has no dependency on praxec; it is consumed purely over the MCP protocol.
Re-exports§
pub use algorithm::CpmAlgorithm;pub use drag::DragResult;pub use drag::diameter;pub use drag::drag;pub use estimator::EffortEstimator;pub use estimator::EstimationConfig;pub use plan::AcceptRequest;pub use plan::AcquireRequest;pub use plan::Estimate;pub use plan::ForceReleaseRequest;pub use plan::HeartbeatRequest;pub use plan::MarkStatusRequest;pub use plan_store::DB_PATH_ENV;pub use plan_store::SqlitePlanStore;pub use planner::BasicCpmPlanner;pub use planner::ClockFn;pub use planner::DEFAULT_EFFORT_HOURS;pub use planner::DEFAULT_MAX_TTL;pub use planner::DEFAULT_TTL;pub use planner::MAX_ATTEMPTS;pub use planner::MAX_LAPSES;pub use server::PLAN_TOOL_NAMES;pub use server::PlanServer;pub use server::TOOL_ACCEPT;pub use server::TOOL_ACQUIRE_COHORT;pub use server::TOOL_FORCE_RELEASE;pub use server::TOOL_HEARTBEAT;pub use server::TOOL_MARK_STATUS;pub use server::TOOL_REVIEW;pub use server::TOOL_STATUS;pub use server::TOOL_SUBMIT;pub use server::plan_tool_definitions;pub use task::Bottleneck;pub use task::CriticalPathResult;pub use task::Task;pub use task::TaskBatch;pub use task::TaskKind;pub use task::TaskStatus;
Modules§
- algorithm
- CPM Algorithm Implementation
- audit
- Minimal audit surface for the planner’s lock-lifecycle events.
- compare
- Compare plan variants on the scorecard: Pareto front and weighted rank.
- drag
- Devaux DRAG and graph diameter for CPM schedules.
- earned_
value - Pure earned-value engine: PV / EV / AC and the derived SPI / CPI / EAC
family, computed from a frozen baseline, the current graph, statuses and
actuals. No I/O, no clocks: the caller supplies
as_of. - edits
- Pure structured edits over a
PlanGraph. - estimator
- Effort Estimation for CPM Tasks
- lint
plan.lint: static checks over aPlanGraphthat never submit it.- llm
- OpenRouter / Jev configuration and the
JudgmentModelseam (P6, #26). - metrics
- Plan scorecard: one struct of headline numbers that wires the DRAG, risk and network-health metrics together with the schedule and lint results.
- monte_
carlo - Seeded Monte Carlo schedule risk over three-point estimates.
- network_
health - iDesign network-health metrics.
- plan
- SPEC §33 PA1 —
Plannerdata model. - plan_
store - SQLite persistence for
crate::planner::BasicCpmPlanner. - planner
BasicCpmPlanner— open-sourcePlannerimplementation.- ports
- The lock-aware
Plannertrait — the seam between an MCP/host caller and a scheduling implementation.crate::planner::BasicCpmPlanneris the textbook Critical Path Method implementation shipped by this crate. - project
- Project-root discovery and confined plan-file paths.
- resource_
schedule - Resource-constrained scheduling: levels a plan against per-resource capacities with event-driven list scheduling, then reports the driving chain (what each link waited on) and project / feeding buffers.
- review
plan.review: deterministic lint, one batched Jev judgment call, and mechanically verified proposals (P6, #26).- revise
- Pure plan revision: derive the runtime state of a revised graph from the state of the plan it replaces, carrying progress over.
- risk
- iDesign risk engine for CPM Planner.
- schedule
- Shared CPM computation: turns a validated
PlanGraphinto aCriticalPathResult. Used when a plan is submitted, when the store recomputes results cached by an older kernel version, and by the read-only analysis tools. - server
- MCP tool surface for the open-source CPM planner.
- simulate
- Read-only plan simulation: lint, CPM, optional resource leveling and Monte Carlo, and the scorecard, for a graph that is never persisted.
- skills
cpm-planner skills install|uninstall|list: copies the agent skills underskills/(embedded at compile time) into the directories each agent tool reads, perdocs/agents/tool-matrix.md.- task
- CPM Task data structures