Skip to main content

meerkat_runtime/
policy_table.rs

1//! Generated admission-policy projection facade.
2//!
3//! Policy/default facts are resolved by `MeerkatMachine::ResolveAdmissionPlan`.
4//! This module remains as a compatibility facade for tests and older callers
5//! that ask for a `PolicyDecision` directly.
6
7use crate::identifiers::{InputKind, KindId, PolicyVersion};
8use crate::ingress_types::RuntimeInputSemantics;
9use crate::input::Input;
10use crate::meerkat_machine::dsl as mm_dsl;
11use crate::policy::PolicyDecision;
12use meerkat_core::lifecycle::RunId;
13use meerkat_core::types::SessionId;
14
15pub struct DefaultPolicyTable;
16
17impl DefaultPolicyTable {
18    #[allow(clippy::expect_used)]
19    pub fn resolve(input: &Input, runtime_idle: bool) -> PolicyDecision {
20        Self::try_resolve(input, runtime_idle)
21            .expect("generated admission authority must resolve compatibility policy projection")
22    }
23
24    pub fn try_resolve(input: &Input, runtime_idle: bool) -> Result<PolicyDecision, String> {
25        generated_admission_projection_for_input(input, runtime_idle)
26            .map(|projection| projection.policy)
27    }
28
29    #[allow(clippy::expect_used)]
30    pub fn resolve_by_kind(kind: KindId, runtime_idle: bool) -> PolicyDecision {
31        Self::try_resolve_by_kind(kind, runtime_idle)
32            .expect("generated admission authority must resolve compatibility policy projection")
33    }
34
35    pub fn try_resolve_by_kind(kind: KindId, runtime_idle: bool) -> Result<PolicyDecision, String> {
36        generated_admission_projection_for_kind(kind, runtime_idle)
37            .map(|projection| projection.policy)
38    }
39}
40
41pub fn generated_default_policy_version() -> PolicyVersion {
42    DefaultPolicyTable::resolve_by_kind(KindId::new(InputKind::Prompt), true).policy_version
43}
44
45pub(crate) struct GeneratedAdmissionProjection {
46    pub policy: PolicyDecision,
47    pub runtime_semantics: RuntimeInputSemantics,
48}
49
50pub(crate) fn generated_admission_projection_for_input(
51    input: &Input,
52    runtime_idle: bool,
53) -> Result<GeneratedAdmissionProjection, String> {
54    // The shell mirrors the machine's emitted projection: it threads the typed
55    // input facts (kind, requested lane, continuation kind) into
56    // `ResolveAdmissionPlan` and returns whatever lane/runtime-semantics the
57    // machine emits. No shell-side reclassification or override.
58    generated_admission_projection(
59        input.id().to_string(),
60        input.kind(),
61        input.handling_mode().map(mm_dsl::InputLane::from),
62        mm_dsl::AdmissionContinuationKind::from(input.continuation_kind()),
63        runtime_idle,
64    )
65}
66
67pub(crate) fn generated_admission_projection_for_kind(
68    kind: KindId,
69    runtime_idle: bool,
70) -> Result<GeneratedAdmissionProjection, String> {
71    generated_admission_projection(
72        format!("policy-projection:{:?}", kind.kind()),
73        kind.kind(),
74        None,
75        mm_dsl::AdmissionContinuationKind::Ordinary,
76        runtime_idle,
77    )
78}
79
80fn generated_admission_projection(
81    input_id: String,
82    input_kind: InputKind,
83    requested_lane: Option<mm_dsl::InputLane>,
84    continuation_kind: mm_dsl::AdmissionContinuationKind,
85    runtime_idle: bool,
86) -> Result<GeneratedAdmissionProjection, String> {
87    let mut authority = projection_authority(runtime_idle)?;
88    let transition = mm_dsl::MeerkatMachineMutator::apply(
89        &mut authority,
90        mm_dsl::MeerkatMachineInput::ResolveAdmissionPlan {
91            input_id: input_id.clone(),
92            input_kind: mm_dsl::AdmissionInputKind::from(input_kind),
93            requested_lane,
94            continuation_kind,
95            silent_intent_match: false,
96            existing_superseded_input_id: None,
97            runtime_running: !runtime_idle,
98            active_turn_boundary_available: false,
99            without_wake: false,
100        },
101    )
102    .map_err(|err| {
103        format!("generated admission authority rejected policy projection for '{input_id}': {err}")
104    })?;
105
106    transition
107        .into_effects()
108        .into_iter()
109        .find_map(|effect| match effect {
110            mm_dsl::MeerkatMachineEffect::AdmissionResolved {
111                input_id: effect_input_id,
112                policy_version,
113                policy_apply_mode,
114                policy_wake_mode,
115                policy_queue_mode,
116                policy_consume_point,
117                policy_drain_policy,
118                policy_routing_disposition,
119                runtime_boundary,
120                runtime_execution_kind,
121                runtime_peer_response_terminal_apply_intent,
122                record_transcript,
123                execution_handling_mode,
124                live_interrupt_required,
125                ..
126            } if effect_input_id == input_id => {
127                let apply_mode: crate::policy::ApplyMode = policy_apply_mode.into();
128                let boundary: meerkat_core::lifecycle::run_primitive::RunApplyBoundary =
129                    runtime_boundary.into();
130                let routing_disposition: crate::policy::RoutingDisposition =
131                    policy_routing_disposition.into();
132                // #24: read the machine-emitted idle-steer normalization directly
133                // (`Option<InputLane>` -> `HandlingMode`); the shell normalizer
134                // `idle_steer_execution_handling_mode` is deleted.
135                let execution_handling_mode = execution_handling_mode.map(|lane| match lane {
136                    mm_dsl::InputLane::Queue => meerkat_core::types::HandlingMode::Queue,
137                    mm_dsl::InputLane::Steer => meerkat_core::types::HandlingMode::Steer,
138                });
139                Some(GeneratedAdmissionProjection {
140                    policy: PolicyDecision {
141                        apply_mode,
142                        wake_mode: policy_wake_mode.into(),
143                        queue_mode: policy_queue_mode.into(),
144                        consume_point: policy_consume_point.into(),
145                        drain_policy: policy_drain_policy.into(),
146                        routing_disposition,
147                        record_transcript,
148                        emit_operator_content: record_transcript,
149                        policy_version: PolicyVersion(policy_version),
150                    },
151                    runtime_semantics: RuntimeInputSemantics {
152                        boundary,
153                        execution_kind: runtime_execution_kind.into(),
154                        execution_handling_mode,
155                        peer_response_terminal_apply_intent:
156                            runtime_peer_response_terminal_apply_intent.map(Into::into),
157                        live_interrupt_required,
158                    },
159                })
160            }
161            _ => None,
162        })
163        .ok_or_else(|| {
164            format!("generated admission authority emitted no policy projection for '{input_id}'")
165        })
166}
167
168fn projection_authority(runtime_idle: bool) -> Result<mm_dsl::MeerkatMachineAuthority, String> {
169    let session_id = SessionId::new();
170    let mut authority =
171        crate::meerkat_machine::dsl_authority::new_registered_authority(&session_id)
172            .map_err(|err| format!("generated admission authority registration failed: {err}"))?;
173    if !runtime_idle {
174        mm_dsl::MeerkatMachineMutator::apply(
175            &mut authority,
176            mm_dsl::MeerkatMachineInput::Prepare {
177                session_id: mm_dsl::SessionId::from_domain(&session_id),
178                run_id: mm_dsl::RunId::from_domain(&RunId::new()),
179            },
180        )
181        .map_err(|err| format!("generated admission authority prepare failed: {err}"))?;
182    }
183    Ok(authority)
184}
185
186#[cfg(test)]
187mod tests {
188    use super::*;
189    use crate::policy::{ApplyMode, WakeMode};
190
191    #[test]
192    fn compatibility_projection_uses_generated_admission_authority() {
193        let idle = DefaultPolicyTable::resolve_by_kind(KindId::new(InputKind::PeerMessage), true);
194        let running =
195            DefaultPolicyTable::resolve_by_kind(KindId::new(InputKind::PeerMessage), false);
196
197        assert_eq!(idle.apply_mode, ApplyMode::StageRunStart);
198        assert_eq!(idle.wake_mode, WakeMode::WakeIfIdle);
199        assert_eq!(running.apply_mode, ApplyMode::StageRunStart);
200        assert_eq!(running.wake_mode, WakeMode::InterruptYielding);
201        assert_eq!(generated_default_policy_version(), idle.policy_version);
202    }
203}