pub enum PlannerError {
Show 21 variants
LockHeld {
plan_id: String,
deliverable_id: String,
holder: String,
},
LockNotHeld {
caller_id: String,
deliverable_id: String,
},
LockExpired {
deliverable_id: String,
expired_at: DateTime<Utc>,
},
OverlapDetected {
deliverable_id: String,
files: Vec<PathBuf>,
},
MissingPrerequisite {
deliverable_id: String,
prereq: String,
},
PlanNotFound {
plan_id: String,
},
DeliverableNotFound {
plan_id: String,
deliverable_id: String,
},
LapseLimit {
deliverable_id: String,
lapse_count: u32,
max_lapses: u32,
},
PrerequisitesIncomplete {
plan_id: String,
deliverable_id: String,
missing: Vec<String>,
},
InvalidGraph {
reason: String,
},
InvalidPath {
reason: String,
},
InvalidCapacities {
missing: Vec<String>,
},
VariantNotSelected {
plan_id: String,
name: String,
variant: String,
selected: String,
},
ArchiveRefused {
reason: String,
},
NoActivePlan {
project: String,
},
AmbiguousActivePlan {
project: String,
lines: Vec<String>,
},
InvalidActuals {
reason: String,
},
InvalidValue {
reason: String,
},
NotBaselined {
plan_id: String,
},
BaselineRequired {
plan_id: String,
},
BackendError(Error),
}Expand description
Errors returned by crate::ports::Planner methods.
Every variant carries enough context to log without further lookup. The
stable string token at the start of the #[error(..)] message doubles
as the wire-level error code surfaced by PA4’s MCP server, so the
variant prefixes (LOCK_HELD, LOCK_NOT_HELD, etc.) MUST NOT change
without bumping the MCP server schema.
Variants§
LockHeld
The requested deliverable is already locked by another caller. The
holder field surfaces the conflicting caller for human triage.
LockNotHeld
The caller invoked an operation that requires holding a lock
(mark_status, heartbeat) but the lock is held by someone else,
or no lock exists at all.
LockExpired
The lock the caller is referencing has passed its TTL. The Planner is free to reclaim the deliverable for another caller.
OverlapDetected
acquire_cohort discovered that the candidate deliverable’s
owned_files overlap with files held by an existing lock. Surfaced
as a distinct variant (rather than LOCK_HELD) because the
conflict is at the file level, not the deliverable level.
MissingPrerequisite
acquire_cohort cannot include the candidate because at least one
of its prerequisites is not yet DeliverableStatus::Complete.
This is not a hard error for the whole call (other cohort members
may still be returned); it surfaces when a caller explicitly
requests an ineligible deliverable.
PlanNotFound
The plan id supplied to a lookup or mutation does not correspond to any submitted plan.
DeliverableNotFound
The deliverable id supplied to a lookup or mutation does not correspond to any deliverable in the named plan.
LapseLimit
A deliverable’s lease has lapsed via TTL (no terminal mark — the
driving process was killed or timed out) more times than the
runaway bound allows. These are ENVIRONMENTAL losses, not
implementation failures, so the deliverable is NOT auto-failed;
instead acquire_cohort skips it and reports it in
Cohort::blocked until an operator intervenes (fix the
environment, then force_release with reset_counters). No longer
returned by acquire_cohort; kept for wire compatibility.
PrerequisitesIncomplete
A deliverable cannot be completed without a lease (or accepted)
while some of its prerequisites are not yet Complete.
InvalidGraph
The submitted graph fails a structural invariant: duplicate ids,
unknown prerequisite reference, cycle, empty owned_files, etc.
The reason is the precise failure message.
InvalidPath
A project/plan-file path failed validation: not a valid slug, not of the
form .cpm-planner/plans/<name>/<variant>.json, absolute, contains ..,
or resolves (e.g. via a symlink) outside the plans directory.
InvalidCapacities
plan.schedule was given no usable capacity (missing or zero) for
one or more resources that scheduled work needs. missing is sorted.
VariantNotSelected
An execution operation (acquire_cohort, heartbeat, mark_status,
accept, force_release) targeted a named plan variant that is not
its line’s selected variant. Read and analysis tools are never gated.
ArchiveRefused
An operation was refused because of archiving: archiving the selected variant alone, syncing into / selecting an archived variant or line, unarchiving a variant of an archived line, or an execution operation on any variant of an archived line.
NoActivePlan
crate::ports::Planner::resolve_plan with
PlanRef::Active found no active line: every line
of project is archived, complete, or has no live selected variant.
AmbiguousActivePlan
crate::ports::Planner::resolve_plan with
PlanRef::Active found more than one active line in
project; lines holds every name, sorted.
InvalidActuals
mark_status progress fields failed validation: earned_pct above
100 or given with a status other than in_progress/complete,
actual_effort_hours not finite or outside 0..=1000000, or
evidence longer than MAX_EVIDENCE_CHARS characters or beyond
MAX_EVIDENCE_ENTRIES entries for the deliverable.
InvalidValue
A request carried a value that is well-typed but out of range for
the plan it targets, e.g. a plan.baseline backfill_from in the
future or before the plan’s creation time.
NotBaselined
An earned-value read or snapshot (plan.ev, plan.snapshot)
targeted a plan that has no baseline yet; take one with
plan.baseline first.
BaselineRequired
The server runs in refuse baseline mode and an execution-starting
call targeted a plan with no baseline yet; take one with
plan.baseline first.
BackendError(Error)
Catch-all for backend failures (DB unavailable, serialization
errors against the persistence layer, etc.). Wraps the underlying
anyhow::Error so the caller can introspect via source().
Trait Implementations§
Source§impl Debug for PlannerError
impl Debug for PlannerError
Source§impl Display for PlannerError
impl Display for PlannerError
Source§impl Error for PlannerError
impl Error for PlannerError
Source§fn source(&self) -> Option<&(dyn Error + 'static)>
fn source(&self) -> Option<&(dyn Error + 'static)>
1.0.0 · Source§fn description(&self) -> &str
fn description(&self) -> &str
use the Display impl or to_string()