///|
pub(all) enum MissionClearanceKind {
TerrainGradeClearance
IlluminationConfidenceClearance
EnergyMarginClearance
OperatorReviewClearance
} derive(Debug, Eq, ToJson, FromJson)
///|
pub(all) enum MissionClearanceStatus {
AcceptedEvidence
RejectedEvidence
NeedsEvidence
ActionRequired
} derive(Debug, Eq, ToJson, FromJson)
///|
pub(all) enum MissionClearanceReviewDecision {
ClearanceAccept
ClearanceReject
ClearanceRequestEvidence
} derive(Debug, Eq, ToJson, FromJson)
///|
pub(all) struct MissionClearanceTransition {
transition_id : String
clearance_id : String
decision : MissionClearanceReviewDecision
reviewer_id : String
recorded_at_utc : String
evidence_ref : String
rationale : String
} derive(Debug, Eq, ToJson, FromJson)
///|
pub(all) struct MissionClearanceItem {
clearance_id : String
kind : MissionClearanceKind
label : String
decision : TraverseDecision
status : MissionClearanceStatus
source_check_id : String
evidence_path : String
observed : String
required : String
clearance_action : String
accepted_evidence_id : String
} derive(Debug, Eq, ToJson, FromJson)
///|
pub(all) struct MissionClearancePlan {
plan_id : String
route_id : String
decision : TraverseDecision
items : Array[MissionClearanceItem]
blocking_items : Array[String]
review_items : Array[String]
accepted_items : Array[String]
rejected_items : Array[String]
next_action : String
} derive(Debug, Eq, ToJson, FromJson)
///|
pub fn mission_clearance_kind_label(kind : MissionClearanceKind) -> String {
match kind {
TerrainGradeClearance => "terrain-grade"
IlluminationConfidenceClearance => "illumination-confidence"
EnergyMarginClearance => "energy-margin"
OperatorReviewClearance => "operator-review"
}
}
///|
pub fn mission_clearance_status_label(
status : MissionClearanceStatus,
) -> String {
match status {
AcceptedEvidence => "accepted-evidence"
RejectedEvidence => "rejected-evidence"
NeedsEvidence => "needs-evidence"
ActionRequired => "action-required"
}
}
///|
pub fn mission_clearance_review_decision_label(
decision : MissionClearanceReviewDecision,
) -> String {
match decision {
ClearanceAccept => "accept"
ClearanceReject => "reject"
ClearanceRequestEvidence => "request-evidence"
}
}
///|
pub fn clearance_transition(
transition_id : String,
clearance_id : String,
decision : MissionClearanceReviewDecision,
reviewer_id : String,
recorded_at_utc : String,
evidence_ref : String,
rationale : String,
) -> MissionClearanceTransition {
{
transition_id,
clearance_id,
decision,
reviewer_id,
recorded_at_utc,
evidence_ref,
rationale,
}
}
///|
fn initial_clearance_status(
decision : TraverseDecision,
) -> MissionClearanceStatus {
match decision {
Allow => AcceptedEvidence
Review | Block => ActionRequired
}
}
///|
fn accepted_evidence_id(
decision : TraverseDecision,
evidence_path : String,
) -> String {
match decision {
Allow => evidence_path
Review | Block => ""
}
}
///|
fn clearance_item(
clearance_id : String,
kind : MissionClearanceKind,
label : String,
decision : TraverseDecision,
source_check_id : String,
evidence_path : String,
observed : String,
required : String,
clearance_action : String,
) -> MissionClearanceItem {
{
clearance_id,
kind,
label,
decision,
status: initial_clearance_status(decision),
source_check_id,
evidence_path,
observed,
required,
clearance_action,
accepted_evidence_id: accepted_evidence_id(decision, evidence_path),
}
}
///|
fn clearance_transition_for(
transitions : Array[MissionClearanceTransition],
clearance_id : String,
) -> MissionClearanceTransition? {
for transition in transitions {
if transition.clearance_id == clearance_id {
return Some(transition)
}
}
None
}
///|
fn transition_decision(
decision : MissionClearanceReviewDecision,
) -> TraverseDecision {
match decision {
ClearanceAccept => Allow
ClearanceReject | ClearanceRequestEvidence => Block
}
}
///|
fn transition_status(
decision : MissionClearanceReviewDecision,
) -> MissionClearanceStatus {
match decision {
ClearanceAccept => AcceptedEvidence
ClearanceReject => RejectedEvidence
ClearanceRequestEvidence => NeedsEvidence
}
}
///|
fn transition_evidence_id(transition : MissionClearanceTransition) -> String {
match transition.decision {
ClearanceAccept => transition.evidence_ref
ClearanceReject | ClearanceRequestEvidence => ""
}
}
///|
fn transition_clearance_action(
item : MissionClearanceItem,
transition : MissionClearanceTransition,
) -> String {
let decision = mission_clearance_review_decision_label(transition.decision)
"\{decision} by \{transition.reviewer_id} at \{transition.recorded_at_utc}: \{transition.rationale}; prior action was \{item.clearance_action}"
}
///|
fn apply_clearance_transition(
item : MissionClearanceItem,
transition : MissionClearanceTransition,
) -> MissionClearanceItem {
{
clearance_id: item.clearance_id,
kind: item.kind,
label: item.label,
decision: transition_decision(transition.decision),
status: transition_status(transition.decision),
source_check_id: item.source_check_id,
evidence_path: item.evidence_path,
observed: item.observed,
required: item.required,
clearance_action: transition_clearance_action(item, transition),
accepted_evidence_id: transition_evidence_id(transition),
}
}
///|
fn apply_clearance_transitions(
items : Array[MissionClearanceItem],
transitions : Array[MissionClearanceTransition],
) -> Array[MissionClearanceItem] {
let reviewed : Array[MissionClearanceItem] = []
for item in items {
match clearance_transition_for(transitions, item.clearance_id) {
Some(transition) =>
reviewed.push(apply_clearance_transition(item, transition))
None => reviewed.push(item)
}
}
reviewed
}
///|
fn combine_clearance_decision(
left : TraverseDecision,
right : TraverseDecision,
) -> TraverseDecision {
match (left, right) {
(Block, _) | (_, Block) => Block
(Review, _) | (_, Review) => Review
(Allow, Allow) => Allow
}
}
///|
fn clearance_decision(items : Array[MissionClearanceItem]) -> TraverseDecision {
let mut decision = TraverseDecision::Allow
for item in items {
decision = combine_clearance_decision(decision, item.decision)
}
decision
}
///|
fn clearance_ids_with_decision(
items : Array[MissionClearanceItem],
decision : TraverseDecision,
) -> Array[String] {
let ids : Array[String] = []
for item in items {
if item.decision == decision {
ids.push(item.clearance_id)
}
}
ids
}
///|
fn accepted_clearance_ids(items : Array[MissionClearanceItem]) -> Array[String] {
let ids : Array[String] = []
for item in items {
if item.status == MissionClearanceStatus::AcceptedEvidence {
ids.push(item.clearance_id)
}
}
ids
}
///|
fn rejected_clearance_ids(items : Array[MissionClearanceItem]) -> Array[String] {
let ids : Array[String] = []
for item in items {
if item.status == MissionClearanceStatus::RejectedEvidence {
ids.push(item.clearance_id)
}
}
ids
}
///|
fn readiness_check_for(
readiness : MissionSimulationReadiness,
check_id : String,
) -> MissionReadinessCheck? {
for check in readiness.checks {
if check.check_id == check_id {
return Some(check)
}
}
None
}
///|
fn check_decision(
readiness : MissionSimulationReadiness,
check_id : String,
fallback : TraverseDecision,
) -> TraverseDecision {
match readiness_check_for(readiness, check_id) {
Some(check) => check.decision
None => fallback
}
}
///|
fn check_evidence_path(
readiness : MissionSimulationReadiness,
check_id : String,
fallback : String,
) -> String {
match readiness_check_for(readiness, check_id) {
Some(check) => check.evidence_path
None => fallback
}
}
///|
fn terrain_clearance_item(
route : RouteCandidate,
readiness : MissionSimulationReadiness,
) -> MissionClearanceItem {
let profile = conservative_rover_profile()
let check_id = "terrain-\{route.route_id}"
clearance_item(
"clear-terrain-grade-\{route.route_id}",
TerrainGradeClearance,
"Selected route grade and roughness clearance",
check_decision(readiness, check_id, route.decision),
check_id,
check_evidence_path(
readiness,
check_id,
route.terrain_remediation.output_path,
),
"grade \{route.terrain_remediation.max_neighbor_grade}, roughness \{route.terrain_remediation.roughness_m} m, blocking edges \{route.terrain_remediation.blocking_edge_count}/\{route.terrain_remediation.edge_count}, route confidence \{route.terrain_remediation.confidence}",
"grade < \{profile.max_grade}, roughness < \{profile.max_roughness_m} m, blocking edges 0, confidence >= \{profile.min_confidence}",
"accept a smoother measured route fixture or keep \{route.route_id} out of simulation until terrain remediation clears the rover profile",
)
}
///|
fn illumination_clearance_item(
route : RouteCandidate,
readiness : MissionSimulationReadiness,
) -> MissionClearanceItem {
let check_id = "illumination-\{route.route_id}"
clearance_item(
"clear-illumination-confidence-\{route.route_id}",
IlluminationConfidenceClearance,
"Selected route illumination confidence clearance",
check_decision(readiness, check_id, route.illumination.decision),
check_id,
check_evidence_path(
readiness,
check_id,
route.illumination.local_horizon.output_path,
),
"illumination confidence \{route.illumination.confidence}, relief-shadow risk \{route.illumination.relief_shadow_risk}, terrain-visible sunlight \{route.illumination.local_horizon.terrain_sunlit_hours} h, longest darkness \{route.illumination.local_horizon.longest_dark_hours} h",
"confidence >= \{route.illumination.constraint.min_confidence}, relief-shadow risk < \{route.illumination.constraint.max_relief_shadow_risk}, terrain-visible sunlight >= \{route.illumination.constraint.min_sunlit_hours} h, longest darkness <= \{route.illumination.constraint.max_dark_hours} h",
"accept hourly azimuth-matched route exposure for \{route.route_id} before any Simulation packet is consumable",
)
}
///|
fn energy_clearance_item(
energy : EnergyAssessment,
route : RouteCandidate,
readiness : MissionSimulationReadiness,
) -> MissionClearanceItem {
let check_id = "energy-window"
let horizon_path = if route.illumination.local_horizon.output_path != "" {
route.illumination.local_horizon.output_path
} else {
"mission/first-trusted-square/routes/\{route.route_id}.illumination.json"
}
let remediation = energy_remediation_for_selected_route(
energy,
route.route_id,
route.terrain_remediation.output_path,
horizon_path,
check_evidence_path(
readiness, check_id, "artifact/mission/first_trusted_square_energy_remediation.json",
),
)
clearance_item(
"clear-energy-margin",
EnergyMarginClearance,
"Rover energy margin clearance",
check_decision(readiness, check_id, remediation.decision),
check_id,
remediation.output_path,
"bounded energy margin \{remediation.bounded_margin_wh} Wh, available \{remediation.verified_available_energy_wh} Wh, required \{remediation.bounded_required_energy_wh} Wh, gap \{remediation.margin_gap_wh} Wh",
"bounded margin >= \{remediation.minimum_margin_wh} Wh with accepted power, selected-route drive, comms, and thermal assumptions",
"accept bounded energy remediation with non-negative conservative margin or reduce route/survival demand before simulation",
)
}
///|
fn operator_clearance_item(
route : RouteCandidate,
readiness : MissionSimulationReadiness,
) -> MissionClearanceItem {
let check_id = "operator-review"
let decision = check_decision(readiness, check_id, TraverseDecision::Block)
clearance_item(
"clear-operator-review-\{route.route_id}",
OperatorReviewClearance,
"Operator accepted review clearance",
decision,
check_id,
check_evidence_path(
readiness, check_id, "artifact/operator-review-transitions.json",
),
"Operator review status is \{traverse_decision_label(decision)} for selected route \{route.route_id}",
"accepted review transitions cover terrain, illumination, energy, and Simulation handoff evidence",
"record accepted operator review transitions for the selected route evidence bundle before simulation",
)
}
///|
fn clearance_next_action(decision : TraverseDecision) -> String {
match decision {
Allow =>
"selected-route blockers are cleared; simulation packet can advance to Simulation review"
Review =>
"resolve review clearance items before Simulation consumes the selected-route simulation packet"
Block =>
"clear required terrain grade, illumination confidence, energy margin, and operator review actions before simulation"
}
}
///|
pub fn selected_route_clearance_plan(
plan_id : String,
route : RouteCandidate,
energy : EnergyAssessment,
readiness : MissionSimulationReadiness,
) -> MissionClearancePlan {
selected_route_clearance_plan_with_transitions(
plan_id,
route,
energy,
readiness,
[],
)
}
///|
pub fn selected_route_clearance_plan_with_transitions(
plan_id : String,
route : RouteCandidate,
energy : EnergyAssessment,
readiness : MissionSimulationReadiness,
transitions : Array[MissionClearanceTransition],
) -> MissionClearancePlan {
let base_items = [
terrain_clearance_item(route, readiness),
illumination_clearance_item(route, readiness),
energy_clearance_item(energy, route, readiness),
operator_clearance_item(route, readiness),
]
let items = apply_clearance_transitions(base_items, transitions)
let decision = clearance_decision(items)
{
plan_id,
route_id: route.route_id,
decision,
items,
blocking_items: clearance_ids_with_decision(items, Block),
review_items: clearance_ids_with_decision(items, Review),
accepted_items: accepted_clearance_ids(items),
rejected_items: rejected_clearance_ids(items),
next_action: clearance_next_action(decision),
}
}