Milestone Lifecyclemilestone
Lifecycle state machine for Milestone nodes. Milestones are time-bound events (Scope v2 §0.1) tied to a due_date. Health states surface through the AtRisk -> Slipped -> Achieved branch driven by the dependency scheduler (Scope v2 §1B.1.4). Recovery from AtRisk uses an explicit MilestoneRecovered event rather than reusing MilestoneStarted (see x-decisions-surfaced).
- Node type
Milestone- Initial state
Planned- Terminal states
Achieved
Full machine
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://gcc.bootminds.com/ontology/state-machines/milestone.json",
"title": "Milestone Lifecycle",
"description": "Lifecycle state machine for Milestone nodes. Milestones are time-bound events (Scope v2 §0.1) tied to a due_date. Health states surface through the AtRisk -> Slipped -> Achieved branch driven by the dependency scheduler (Scope v2 §1B.1.4). Recovery from AtRisk uses an explicit MilestoneRecovered event rather than reusing MilestoneStarted (see x-decisions-surfaced).",
"node_type": "Milestone",
"initial_state": "Planned",
"terminal_states": [
"Achieved"
],
"states": {
"Planned": {
"description": "Milestone has been authored with owner, due_date, exit criteria, and dependency links. Not yet started."
},
"InFlight": {
"description": "Owner has marked the milestone as in flight (work toward its exit criteria is in progress). Tracked actively by the dependency scheduler."
},
"AtRisk": {
"description": "Milestone is flagged as at risk of missing its due_date. May be set manually by a contributor (e.g., the work owner) or automatically by the dependency scheduler when an upstream slip propagates. Distinct from Slipped: AtRisk is a forward-looking warning; Slipped is a past-due fact."
},
"Slipped": {
"description": "Milestone has missed its due_date without Achievement. Set automatically by the scheduler at due_date + 0 if not already Achieved. Recoverable via re-baselining and eventual MilestoneAchieved."
},
"Achieved": {
"description": "Milestone's exit criteria are satisfied and recorded by the decision_maker. Terminal: no further transitions in normal flow. Reachable from InFlight (on-time), AtRisk (recovered late but still by due_date), or Slipped (achieved after slipping)."
}
},
"transitions": [
{
"from": "Planned",
"to": "InFlight",
"trigger_event": "MilestoneStarted",
"requires_role": "decision_maker",
"requires_events": [],
"guard_description": "The decision_maker (milestone owner) declares work has begun. Captures the actual start vs the planned start; the dependency scheduler starts tracking commitment health from this point."
},
{
"from": "InFlight",
"to": "AtRisk",
"trigger_event": "MilestoneAtRisk",
"requires_role": "contributor",
"requires_events": [],
"guard_description": "Manual flag by a contributor OR automatic flag by the dependency scheduler. When emitted by the scheduler, the actor_id on the event is the system actor; when emitted by a human, requires_role enforces 'contributor' (an inclusive role: the owner, a Bootminds advisor, or any RACI-attached stakeholder can raise the flag). The event payload SHOULD record source = 'manual' | 'scheduler' and the reason (upstream slip, owner-reported, etc.)."
},
{
"from": "AtRisk",
"to": "InFlight",
"trigger_event": "MilestoneRecovered",
"requires_role": "decision_maker",
"requires_events": [],
"guard_description": "Recovery path: conditions that triggered AtRisk no longer hold (upstream slip resolved, scope adjusted, owner reports back on track). The decision_maker (milestone owner) re-asserts that the milestone is in flight. Distinct event type rather than re-using MilestoneStarted, so audit and timeline reconstruction can clearly distinguish initial start from recovery (see x-decisions-surfaced)."
},
{
"from": "AtRisk",
"to": "Slipped",
"trigger_event": "MilestoneSlipped",
"requires_role": null,
"requires_events": [],
"guard_description": "Automatic transition emitted by the dependency scheduler / time-clock job when the milestone's due_date passes without Achievement. No human role required (system actor on the event). May also be emitted from InFlight directly if the milestone was never explicitly flagged AtRisk before due_date passed; (omitted as an explicit transition here because the canonical happy-but-degrading path runs InFlight -> AtRisk -> Slipped, and the scheduler should flag AtRisk before allowing a hard Slipped to fire. Tenants may differ; if the InFlight -> Slipped direct path is needed, surface it as a registry decision)."
},
{
"from": "InFlight",
"to": "Achieved",
"trigger_event": "MilestoneAchieved",
"requires_role": "decision_maker",
"requires_events": [],
"guard_description": "On-time achievement. The decision_maker records satisfaction of the exit criteria. Gate workflows that depend on this milestone unblock automatically as their exit-criteria checklists tick over (Build Plan §7.1.5)."
},
{
"from": "Slipped",
"to": "Achieved",
"trigger_event": "MilestoneAchieved",
"requires_role": "decision_maker",
"requires_events": [],
"guard_description": "Late achievement after a recorded slip. Same trigger event as the on-time achievement; the slip is preserved in the event history for Programme Memory and Programme Confidence (Scope v3 #4 Schedule Realism). The decision_maker records exit-criteria satisfaction."
}
],
"x-decisions-surfaced": [
"Recovery uses a new event type MilestoneRecovered rather than reusing MilestoneStarted. Rationale: replaying the event log to reconstruct programme history must distinguish 'first time the milestone started' from 'milestone recovered from at-risk', and the dependency scheduler / Programme Confidence Schedule Realism component (Scope v3 #4) needs the signal for slip-pattern learning. The event-types.json registry MUST add MilestoneRecovered (surfaced for the agent authoring event-types.json in parallel).",
"The direct path InFlight -> Slipped (skipping AtRisk) is intentionally omitted from the canonical state graph. The scheduler is expected to raise MilestoneAtRisk before the due_date passes; missing that flag is itself a scheduler bug worth surfacing. If real engagements show this is impractical, the registry can add InFlight -> Slipped later — additive change."
]
}