Skip to main content

dpm_engine/
readiness.rs

1use crate::{EngineError, Transition};
2use chrono::{DateTime, Utc};
3pub use dpm_model::completion;
4use dpm_model::{Plan, Timeline, WorkItem, WorkItemId, WorkStatus};
5
6/// Whether a planned task can be claimed at an adapter-supplied time.
7///
8/// Claim eligibility is the start gate: FS and SS predecessor events plus positive lag, and every
9/// decision on the task or its containers. FF and SF constrain submission and verification instead.
10pub fn is_ready(plan: &Plan, work: &WorkItem, now: DateTime<Utc>) -> bool {
11    plan.work_items.get(&work.id) == Some(work)
12        && crate::gate_report(plan, work.id, Transition::Claim, now)
13            .is_ok_and(|report| report.ready)
14}
15
16/// Return a work projection with aggregate completion reflected in its lifecycle.
17///
18/// The returned value is a view, not an authoritative snapshot to persist.
19pub fn show_work(
20    plan: &Plan,
21    work: WorkItemId,
22    now: DateTime<Utc>,
23) -> Result<WorkItem, EngineError> {
24    plan.validate()?;
25    let timeline = Timeline::at(plan, now);
26    show_with(plan, work, &timeline)
27}
28
29pub(crate) fn show_with(
30    plan: &Plan,
31    work: WorkItemId,
32    timeline: &Timeline,
33) -> Result<WorkItem, EngineError> {
34    let mut item = plan
35        .work_items
36        .get(&work)
37        .ok_or(EngineError::MissingWorkItem(work))?
38        .clone();
39    if !item.is_executable() && timeline.completed_at(work).is_some() {
40        item.execution.status = WorkStatus::Verified;
41    }
42    Ok(item)
43}
44
45#[cfg(test)]
46mod tests;