yog 0.0.51

yog: the standalone server for litany loops — the world, the balls and the conversations, behind one wire
Documentation
//! A real litany workspace on disk (litany ARCH §2.2), reduced to what yog's
//! own policy authoring reads: a bare `repo.git` whose orphan `config/default`
//! root carries the control files — `workflow.yaml` for §8.6's capability
//! control, `manifest.yaml` for §3.7's instruction glob, `instructions.yaml`
//! for §3.7's filename override. Shared by the authoring tests and the
//! start-flow tests that prove a workspace which cannot be converged aborts the
//! start.

use std::path::Path;

/// The single-file case: a `config/default` carrying only `workflow.yaml` —
/// what §8.6's tests read, and a workspace whose manifest yog leaves alone.
pub(crate) fn seed_workspace_workflow(workspace: &Path, workflow: &str) {
    seed_workspace_config(workspace, &[("workflow.yaml", workflow)]);
}

/// Author a workspace's `config/default` carrying `files` — the shape `litany
/// new` leaves behind, reduced to the control files yog reads and rewrites.
pub(crate) fn seed_workspace_config(workspace: &Path, files: &[(&str, &str)]) {
    let repo = workspace.join("repo.git");
    let author = workspace.join(".author");
    std::fs::create_dir_all(&repo).unwrap();
    let run = |args: &[&str]| {
        let status = crate::git_env::status(crate::git_env::git().args(args)).unwrap();
        assert!(status.success(), "git {args:?}");
    };
    let (repo_s, author_s) = (repo.display().to_string(), author.display().to_string());
    run(&["init", "-q", "--bare", "-b", "config/default", &repo_s]);
    run(&[
        "-C",
        &repo_s,
        "worktree",
        "add",
        "-q",
        "--orphan",
        "-b",
        "config/default",
        &author_s,
    ]);
    for (name, body) in files {
        std::fs::write(author.join(name), body).unwrap();
    }
    run(&["-C", &author_s, "add", "-A"]);
    run(&[
        "-C",
        &author_s,
        "-c",
        "user.email=t@t.local",
        "-c",
        "user.name=T",
        "-c",
        "commit.gpgsign=false",
        "commit",
        "-q",
        "-m",
        "config: init [config/default]",
    ]);
    run(&["-C", &repo_s, "worktree", "remove", &author_s]);
}

/// Fork `config/<name>` off `config/default` — the §8.7 birth policy an
/// operator installs by *creating a lineage*, and the whole of what makes a
/// ball tag selectable. Nothing else is written: the policy is the ref.
pub(crate) fn seed_workspace_lineage(workspace: &Path, name: &str) {
    let repo = workspace.join("repo.git").display().to_string();
    let branch = format!("config/{name}");
    let status = crate::git_env::status(crate::git_env::git().args([
        "-C",
        &repo,
        "branch",
        &branch,
        "config/default",
    ]))
    .unwrap();
    assert!(status.success(), "git branch {branch}");
}

/// Fork an **agent branch** off `config/default` carrying litany's own
/// founding subject (its ARCH §2.5): `dispatch: <role> [<agent>]` for a child,
/// and the root's `step 001: dispatch [<agent>]` when `role` is `None` — the
/// one home an agent's role is derived from, and therefore what
/// [`crate::tool_host::grant`] reads.
pub(crate) fn seed_agent_branch(workspace: &Path, agent: &str, role: Option<&str>) {
    let repo = workspace.join("repo.git").display().to_string();
    let tree = workspace.join("agents").join(agent);
    let tree_s = tree.display().to_string();
    // The **branch**, which is `agents/<id>` and not the bare id (litany ARCH
    // §2.3, and what `git for-each-ref` shows on a live workspace).
    let branch = format!("agents/{agent}");
    let subject = match role {
        Some(role) => format!("dispatch: {role} [{agent}]"),
        None => format!("step 001: dispatch [{agent}]"),
    };
    let run = |args: &[&str]| {
        let status = crate::git_env::status(crate::git_env::git().args(args)).unwrap();
        assert!(status.success(), "git {args:?}");
    };
    run(&[
        "-C",
        &repo,
        "worktree",
        "add",
        "-q",
        "-b",
        &branch,
        &tree_s,
        "config/default",
    ]);
    std::fs::write(tree.join("goal.md"), "g").unwrap();
    run(&["-C", &tree_s, "add", "-A"]);
    run(&[
        "-C",
        &tree_s,
        "-c",
        "user.email=t@t.local",
        "-c",
        "user.name=T",
        "-c",
        "commit.gpgsign=false",
        "commit",
        "-q",
        "-m",
        &subject,
    ]);
}