Skip to main content

sim_lib_bridge/
frontier.rs

1use std::sync::Arc;
2
3use sim_codec_bridge::{BridgeBook, BridgeFramePayload, BridgePacket};
4use sim_kernel::{Cx, Expr, Result, Symbol};
5use sim_lib_agent_runner_core::shape_to_grammar;
6use sim_shape::{ExactExprShape, OneOfShape, Shape};
7use sim_value::build::entry;
8
9/// Legal next heads plus in-scope slot shapes for a BRIDGE packet.
10pub struct FrontierMenu {
11    /// Slot id to shape descriptor.
12    pub slots: Vec<(String, Expr)>,
13    /// Flat `OneOf` descriptor for legal next move heads.
14    pub heads: Expr,
15    /// Shape object backing the legal next move heads.
16    pub head_shape: Arc<dyn Shape>,
17    /// Grammar lowered from the same flat `OneOf`.
18    pub grammar: String,
19}
20
21/// Computes the shared BRIDGE frontier for BRIEF, ASK, LOOM, and COLLAB views.
22pub fn frontier(_cx: &mut Cx, packet: &BridgePacket) -> Result<FrontierMenu> {
23    let book = BridgeBook::standard();
24    let slots = frame_slots(&book, packet)?;
25    let heads = book
26        .moves
27        .legal_reply_intents(std::slice::from_ref(&packet.header.move_kind));
28    let head_shape = OneOfShape::new(
29        heads
30            .iter()
31            .map(|head| Arc::new(ExactExprShape::new(Expr::Symbol(head.clone()))) as Arc<dyn Shape>)
32            .collect(),
33    );
34    let grammar = shape_to_grammar(&head_shape)?;
35    Ok(FrontierMenu {
36        slots,
37        heads: Expr::Map(vec![
38            entry("shape", Expr::Symbol(Symbol::qualified("shape", "OneOf"))),
39            entry(
40                "choices",
41                Expr::Vector(heads.into_iter().map(Expr::Symbol).collect()),
42            ),
43        ]),
44        head_shape: Arc::new(head_shape),
45        grammar,
46    })
47}
48
49fn frame_slots(book: &BridgeBook, packet: &BridgePacket) -> Result<Vec<(String, Expr)>> {
50    let mut slots = Vec::new();
51    for part in &packet.body {
52        if part.kind != Symbol::qualified("bridge", "Frame") {
53            continue;
54        }
55        let payload = BridgeFramePayload::from_expr(&part.payload)?;
56        let spec = book.frames.require_spec(&payload.frame)?;
57        for hole in &spec.holes {
58            slots.push((
59                format!(
60                    "{}.{}",
61                    part.id.as_qualified_str(),
62                    hole.name.as_qualified_str()
63                ),
64                hole.kind.shape_expr(),
65            ));
66        }
67    }
68    Ok(slots)
69}