yog 0.0.68

yog: the standalone server for litany loops — the world, the balls and the conversations, behind one wire
Documentation
//! The boundary glue a frame's gestures land in (§8.5), driven from where the
//! **engine** stands ([`crate::test_support::engine::act`]) — since bl-1747 the
//! window posts every act over the wire and holds no dispatch of its own, so
//! these read the chokepoint the wire's listener reaches, over a `ui.json`
//! opened fresh per gesture. Shares [`super::world`]'s hermetic fixture; spawns
//! a fake `litany`, so it lives in its own file (the `prepare.rs` discipline).

use super::{model, world};
use crate::boundary::Action;
use crate::boundary::reply::Reply;
use crate::cli_outbound::Cli;
use crate::opslog::{self, DETACHED_EXIT};
use crate::start::Prepared;
use crate::test_support::engine;
use std::path::Path;
use tempfile::tempdir;

/// The §3.5 spend ceiling refusing this door (bl-56d5, bl-a80a), its own file
/// at the cap.
mod ceiling;
/// The §9.4 workflow mark's set and clear (bl-b680), its own file at the cap.
mod workflow;

/// The door's fire over a wall that is signed in (bl-2291) — these beats are
/// about the mint and the launch, not the wall.
fn signed(deps: crate::boundary::dispatch::Deps) -> crate::boundary::dispatch::Deps {
    crate::boundary::dispatch::Deps {
        world: crate::test_support::signed(&deps.world),
        ..deps
    }
}

/// The conversation the retarget and the fork address. **Id-shaped** (ARCH
/// §2.3's compact stamp): the §8.5 chokepoint resolves the conversation a
/// gesture names (bl-49bc), and an id reads as one on its own.
const AGENT: &str = "20260101T000000Z-c1";

/// An everything-succeeds fake `litany`.
fn fake_litany(dir: &Path) -> Cli {
    let path = dir.join("litany");
    crate::test_support::write_exec(&path, "#!/bin/sh\nexit 0\n");
    Cli::new(path)
}

/// The composer's own hand-off, as the two `Prompt` drives below spend it.
fn prepared(w: &super::World) -> Prepared {
    Prepared {
        workspace: crate::naming::leaf(&w.ws_cobalt),
        binding: Some(w.ws_cobalt.clone()),
        goal: "prefill".into(),
        origin: crate::opslog::Origin::Conversation,
        lineage: None,
        role: None,
    }
}

#[test]
fn the_engines_dispatch_is_the_one_chokepoint_a_posted_act_reaches() {
    let bin = tempdir().unwrap();
    let w = world();
    let (_c, m) = model(&w);
    let litany = fake_litany(bin.path());
    let deps = m.boundary_deps(&litany, &Cli::new("/no/bl"));
    let action = Action::Scan {
        workspace: crate::naming::leaf(&(w.ws_cobalt.clone())),
    };
    let Reply::Outcome(outcome) = engine::act(&m, &deps, "TS", &action).unwrap() else {
        panic!("a verb answers an outcome");
    };
    assert!(outcome.ok());
    let ops = opslog::tail(&w.roots.yog_state, 4);
    assert_eq!(ops.last().map(|e| e.ts.clone()), Some("TS".to_owned()));
}

/// The §9.4 exit rides the same chokepoint (bl-2d19), and what reaches the
/// substrate is the workspace-bound `litany retarget <ws> <agent>` — asserted
/// off the §4.2 trail, which records the argv actually spawned.
#[test]
fn the_retarget_exit_spawns_the_bound_litany_verb() {
    let bin = tempdir().unwrap();
    let w = world();
    let (_c, m) = model(&w);
    let litany = fake_litany(bin.path());
    let deps = m.boundary_deps(&litany, &Cli::new("/no/bl"));
    let action = Action::Retarget {
        workspace: crate::naming::leaf(&(w.ws_cobalt.clone())),
        agent: AGENT.into(),
    };
    let Reply::Outcome(outcome) = engine::act(&m, &deps, "TR", &action).unwrap() else {
        panic!("a verb answers an outcome");
    };
    assert!(outcome.ok());
    let ops = opslog::tail(&w.roots.yog_state, 4);
    let last = ops.last().expect("the verb is on the trail");
    assert_eq!(
        last.argv[1..],
        [
            "retarget".to_owned(),
            w.ws_cobalt.display().to_string(),
            AGENT.to_owned()
        ]
    );
    assert_eq!(last.cwd, w.ws_cobalt.display().to_string());
}

#[test]
fn the_prompt_door_launches_detached_and_mints_off_the_seat_s_own_seed() {
    let bin = tempdir().unwrap();
    let w = world();
    let (_c, m) = model(&w);
    let litany = fake_litany(bin.path());
    let deps = signed(m.boundary_deps(&litany, &Cli::new("/no/bl")));
    let action = Action::Prompt {
        prepared: prepared(&w),
        goal: "go".into(),
        // The §3.3 seed is the firing seat's, carried on the gesture since
        // bl-1747 rather than reached into `Deps` — this is a window's own
        // preview seed crossing with the act it predicted.
        seed: Some(7),
    };
    let Reply::Started { conversation } = engine::act(&m, &deps, "T1", &action).unwrap() else {
        panic!("the prompt door answers the minted name");
    };
    assert!(
        !conversation.is_empty(),
        "the minted conversation name rides back"
    );
    let ops = opslog::tail(&w.roots.yog_state, 4);
    assert_eq!(
        ops.last().map(|e| e.exit),
        Some(DETACHED_EXIT),
        "the handoff's §4.2 sentinel row"
    );

    // The same door refuses when the fork cannot land, error text riding back.
    let dead = signed(m.boundary_deps(&Cli::new("/no/such/litany"), &Cli::new("/no/bl")));
    let err = engine::act(&m, &dead, "T2", &action).unwrap_err();
    assert!(!err.is_empty());
}

/// **A caller that predicted no name still mints one, and never the same one
/// twice** (bl-1747, bl-d88f). `seed: None` is the deposited line and the §4.3
/// loop, and the door draws its own — per **creation**, not per second. The
/// two fires here are identical in every input the mint reads: the same `ts`,
/// the same workspace, and the same occupied set, because neither has landed a
/// dispatch commit the other could see. On the old seed —
/// `content_hash(ts)`, and `ts` is unix seconds — that made two conversations
/// under one name and every seat verb then addressed neither.
#[test]
fn two_seedless_prompts_in_one_second_mint_two_names() {
    let bin = tempdir().unwrap();
    let w = world();
    let (_c, m) = model(&w);
    let litany = fake_litany(bin.path());
    let deps = signed(m.boundary_deps(&litany, &Cli::new("/no/bl")));
    let action = Action::Prompt {
        prepared: prepared(&w),
        goal: "go".into(),
        seed: None,
    };
    let mut minted = Vec::new();
    for _ in 0..2 {
        let Reply::Started { conversation } = engine::act(&m, &deps, "T9", &action).unwrap() else {
            panic!("the prompt door answers the minted name");
        };
        assert!(!conversation.is_empty(), "a name was minted regardless");
        minted.push(conversation);
    }
    assert_ne!(minted[0], minted[1], "one stamp, two creations: {minted:?}");
}

/// **S12-T5 three-spellings** (the executor half): one attempt crosses the
/// boundary as the ordinary `litany dispatch --from`, and the whole argv —
/// role, parent, verbatim goal, fork point, skill pin — lands on the §4.2
/// trail. A cohort is this, N times: nothing here knows how many there were.
#[test]
fn an_attempt_dispatches_the_ordinary_fork_and_logs_its_whole_argv() {
    let bin = tempdir().unwrap();
    let w = world();
    let (_c, m) = model(&w);
    let litany = fake_litany(bin.path());
    let deps = m.boundary_deps(&litany, &Cli::new("/no/bl"));
    let action = Action::Fork {
        workspace: crate::naming::leaf(&(w.ws_cobalt.clone())),
        parent: AGENT.into(),
        attempt: crate::fork::Attempt {
            from: "aaaa1111".into(),
            role: "worker".into(),
            skills: vec!["bash".into()],
        },
        goal: "try it the other way".into(),
    };
    let Reply::Outcome(outcome) = engine::act(&m, &deps, "TS", &action).unwrap() else {
        panic!("a verb answers an outcome");
    };
    assert!(outcome.ok());
    let argv = opslog::tail(&w.roots.yog_state, 4)
        .last()
        .map(|e| e.argv.clone())
        .unwrap_or_default();
    assert!(argv.contains(&"dispatch".to_owned()), "{argv:?}");
    assert!(argv.contains(&"worker".to_owned()), "{argv:?}");
    assert!(argv.contains(&AGENT.to_owned()), "{argv:?}");
    assert!(argv.contains(&"--from".to_owned()), "{argv:?}");
    assert!(argv.contains(&"aaaa1111".to_owned()), "{argv:?}");
    assert!(
        argv.contains(&"try it the other way".to_owned()),
        "{argv:?}"
    );
    // The pin's source is the **world's** pool, never an ambient litany's.
    let pin = argv.iter().find(|a| a.starts_with("skills/bash/SKILL.md="));
    let pin = pin.expect("the skill rides as a pin");
    assert!(pin.contains("world/litany/skills/bash/SKILL.md"), "{pin}");
}