shepherd-core 6.6.1

The harness-agnostic shepherd engine: domain types, configuration schema, and run state. Knows nothing about any CLI, harness, or process.
use alloc::{format, string::String, vec::Vec};

use super::{PlanError, PlanNode, PlanTopology};

pub fn render_topology(topology: &PlanTopology) -> Vec<u8> {
    let mut output = String::new();
    output.push_str("{\n");
    field(&mut output, 1, "schema", &topology.schema, true);
    field(&mut output, 1, "run", &topology.run, true);
    field(&mut output, 1, "seed", &topology.seed, true);
    field(&mut output, 1, "mesh", &topology.mesh, true);
    field(
        &mut output,
        1,
        "planning_evidence",
        &topology.planning_evidence,
        true,
    );
    field(&mut output, 1, "goal", &topology.goal, true);
    field(
        &mut output,
        1,
        "capacity_policy",
        &topology.capacity_policy,
        true,
    );
    string_array(&mut output, 1, "deliverables", &topology.deliverables, true);
    output.push_str("  \"capacity\": {\n");
    output.push_str(&format!(
        "    \"logical_lane_limit\": {},\n    \"host_process_ceiling\": {},\n    \"project_spawn_max_parallel\": {},\n    \"plan_process_ceiling\": {},\n    \"parent_role_cap\": {},\n    \"run_budget\": {},\n    \"simultaneous_process_ceiling\": {},\n    \"per_lane_child_wave_ceiling\": {},\n    \"disk_min_mib\": {},\n    \"model_quota\": {},\n",
        topology.capacity.logical_lane_limit,
        topology.capacity.host_process_ceiling,
        topology.capacity.project_spawn_max_parallel,
        topology.capacity.plan_process_ceiling,
        topology.capacity.parent_role_cap,
        topology.capacity.run_budget,
        topology.capacity.simultaneous_process_ceiling,
        topology.capacity.per_lane_child_wave_ceiling,
        topology.capacity.disk_min_mib,
        topology.capacity.model_quota,
    ));
    field(
        &mut output,
        2,
        "backpressure",
        &topology.capacity.backpressure,
        true,
    );
    binding_array(
        &mut output,
        2,
        "cargo_targets",
        &topology.capacity.cargo_targets,
        true,
    );
    binding_array(
        &mut output,
        2,
        "conductors",
        &topology.capacity.conductors,
        true,
    );
    output.push_str("    \"schedule\": [");
    for (index, wave) in topology.capacity.schedule.iter().enumerate() {
        if index != 0 {
            output.push_str(", ");
        }
        output.push_str(&format!(
            "\"{}@{}\"",
            escape(&wave.lanes.join("+")),
            wave.process_slots
        ));
    }
    output.push_str("],\n");
    output.push_str("    \"scale_outcome\": ");
    match &topology.capacity.scale_outcome {
        Some(outcome) => output.push_str(&format!("\"{}\"\n", escape(outcome))),
        None => output.push_str("null\n"),
    }
    output.push_str("  },\n");
    output.push_str("  \"lanes\": [\n");
    for (index, lane) in topology.lanes.iter().enumerate() {
        output.push_str("    {\n");
        field(&mut output, 3, "id", &lane.id, true);
        field(&mut output, 3, "conductor", &lane.conductor, true);
        field(&mut output, 3, "cargo_target", &lane.cargo_target, true);
        string_array(&mut output, 3, "deliverables", &lane.deliverables, true);
        string_array(&mut output, 3, "node_ids", &lane.node_ids, false);
        output.push_str(if index + 1 == topology.lanes.len() {
            "    }\n"
        } else {
            "    },\n"
        });
    }
    output.push_str("  ],\n");
    output.push_str("  \"nodes\": [\n");
    for (index, node) in topology.nodes.iter().enumerate() {
        render_node(&mut output, node);
        output.push_str(if index + 1 == topology.nodes.len() {
            "    }\n"
        } else {
            "    },\n"
        });
    }
    output.push_str("  ],\n");
    string_array(
        &mut output,
        1,
        "topological_order",
        &topology.topological_order,
        false,
    );
    output.push_str("}\n");
    output.into_bytes()
}

fn render_node(output: &mut String, node: &PlanNode) {
    output.push_str("    {\n");
    field(output, 3, "id", &node.id, true);
    string_array(
        output,
        3,
        "seed_deliverables",
        &node.seed_deliverables,
        true,
    );
    field(output, 3, "lane", &node.lane, true);
    field(output, 3, "role", &node.role, true);
    field(output, 3, "work_kind", &node.work_kind, true);
    field(output, 3, "outcome", &node.outcome, true);
    string_array(output, 3, "owns", &node.owns, true);
    string_array(output, 3, "forbidden", &node.forbidden, true);
    string_array(output, 3, "consumes", &node.consumes, true);
    string_array(output, 3, "produces", &node.produces, true);
    string_array(output, 3, "depends_on", &node.depends_on, true);
    output.push_str("      \"red\": {\n");
    string_array(output, 4, "command", &node.red.command, true);
    field(output, 4, "expects", &node.red.expects, true);
    field(output, 4, "reason", &node.red.reason, false);
    output.push_str("      },\n");
    output.push_str("      \"green\": {\n");
    string_array(output, 4, "command", &node.green.command, true);
    field(output, 4, "expects", &node.green.expects, true);
    field(output, 4, "reason", &node.green.reason, false);
    output.push_str("      },\n");
    output.push_str("      \"eval\": {\n");
    string_array(output, 4, "command", &node.eval.command, true);
    output.push_str("        \"threshold\": ");
    match node.eval.threshold {
        Some(threshold) => output.push_str(&format!("{threshold}\n")),
        None => output.push_str("null\n"),
    }
    output.push_str("      },\n");
    field(output, 3, "evidence", &node.evidence, true);
    output.push_str("      \"review\": {\n");
    field(output, 4, "role", &node.review.role, true);
    field(output, 4, "predicate", &node.review.predicate, false);
    output.push_str("      },\n");
    field(output, 3, "failure_route", &node.failure_route, true);
    field(output, 3, "rollback", &node.rollback, false);
}

pub fn render_lane(topology: &PlanTopology, lane_id: &str) -> Result<Vec<u8>, PlanError> {
    let lane = topology
        .lanes
        .iter()
        .find(|lane| lane.id == lane_id)
        .ok_or_else(|| PlanError::UnknownLane(lane_id.into()))?;
    let mut output = format!(
        "# Lane: {}\n\n## Outcome and non-goals\n\n- Seed deliverables: {}\n- Goal: {}\n- Non-goals: graph rescoping, planning, Conductor implementation, self-review\n\n## Inputs and predecessor contracts\n\n- Verified seed: {}\n- Mesh: {}\n- Planning evidence: {}\n- Conductor: {}\n- Cargo target: {}\n\n## Owned paths\n\n### Allowed\n",
        lane.id,
        lane.deliverables.join(", "),
        topology.goal,
        topology.seed,
        topology.mesh,
        topology.planning_evidence,
        lane.conductor,
        lane.cargo_target,
    );
    let nodes = lane_nodes(topology, lane_id);
    for node in &nodes {
        for path in &node.owns {
            output.push_str(&format!("- `{path}` ({})\n", node.id));
        }
    }
    output.push_str("\n### Forbidden\n");
    for node in &nodes {
        for path in &node.forbidden {
            output.push_str(&format!("- `{path}` ({})\n", node.id));
        }
    }
    output.push_str("\n## Ordered nodes\n\n| order | node id | role | outcome | red | green | evidence |\n|---:|---|---|---|---|---|---|\n");
    for (index, id) in topology.topological_order.iter().enumerate() {
        let Some(node) = nodes.iter().find(|node| &node.id == id) else {
            continue;
        };
        output.push_str(&format!(
            "| {} | {} | {} | {} | `{}` | `{}` | `{}` |\n",
            index + 1,
            node.id,
            node.role,
            node.outcome,
            node.red.command.join(" "),
            node.green.command.join(" "),
            node.evidence,
        ));
    }
    output.push_str("\n## Coder briefs\n");
    render_briefs(&mut output, &nodes, "coder");
    output.push_str("\n## Worker briefs\n");
    render_briefs(&mut output, &nodes, "worker");
    output.push_str(
        "\n## Review points and bounded redo\n\nEach node receives independent review. Rejections one through three retain exact scope and evidence. Rejection four terminally marks the instance malignant, revokes and quarantines it, preserves evidence, forbids resume, and returns exact-lineage replacement custody to root.\n",
    );
    output.push_str("\n## Local gate and integration handoff\n\n");
    for node in &nodes {
        output.push_str(&format!(
            "- `{}`: GREEN `{}`; review {}: {}; evidence `{}`\n",
            node.id,
            node.green.command.join(" "),
            node.review.role,
            node.review.predicate,
            node.evidence,
        ));
    }
    output.push_str("\n## Risks and stop conditions\n\n- Stop on baseline drift, capacity breach, scope overlap, stale review, or immutable-slice contradiction.\n");
    output.push_str("\n## Rollback boundary\n\n");
    for node in &nodes {
        output.push_str(&format!("- `{}`: {}\n", node.id, node.rollback));
    }
    Ok(output.into_bytes())
}

fn lane_nodes<'a>(topology: &'a PlanTopology, lane: &str) -> Vec<&'a PlanNode> {
    topology
        .nodes
        .iter()
        .filter(|node| node.lane == lane)
        .collect()
}

fn render_briefs(output: &mut String, nodes: &[&PlanNode], role: &str) {
    let mut count = 0;
    for node in nodes.iter().filter(|node| node.role == role) {
        count += 1;
        output.push_str(&format!(
            "\n### {}\n\n- Outcome: {}\n- Work kind: {}\n- Acceptance: `{}` and {} review\n- Result: `{}`\n",
            node.id,
            node.outcome,
            node.work_kind,
            node.green.command.join(" "),
            node.review.role,
            node.evidence,
        ));
    }
    if count == 0 {
        output.push_str("\nNone.\n");
    }
}

fn field(output: &mut String, indent: usize, name: &str, value: &str, comma: bool) {
    output.push_str(&"  ".repeat(indent));
    output.push_str(&format!(
        "\"{}\": \"{}\"{}\n",
        escape(name),
        escape(value),
        if comma { "," } else { "" }
    ));
}

fn string_array(output: &mut String, indent: usize, name: &str, values: &[String], comma: bool) {
    output.push_str(&"  ".repeat(indent));
    output.push_str(&format!("\"{}\": [", escape(name)));
    for (index, value) in values.iter().enumerate() {
        if index != 0 {
            output.push_str(", ");
        }
        output.push_str(&format!("\"{}\"", escape(value)));
    }
    output.push(']');
    if comma {
        output.push(',');
    }
    output.push('\n');
}

fn binding_array(
    output: &mut String,
    indent: usize,
    name: &str,
    values: &[super::LaneBinding],
    comma: bool,
) {
    let rendered = values
        .iter()
        .map(|binding| format!("{}={}", binding.lane, binding.value))
        .collect::<Vec<_>>();
    string_array(output, indent, name, &rendered, comma);
}

fn escape(value: &str) -> String {
    let mut escaped = String::new();
    for character in value.chars() {
        match character {
            '"' => escaped.push_str("\\\""),
            '\\' => escaped.push_str("\\\\"),
            '\n' => escaped.push_str("\\n"),
            '\r' => escaped.push_str("\\r"),
            '\t' => escaped.push_str("\\t"),
            character if character.is_control() => {
                escaped.push_str(&format!("\\u{:04x}", character as u32));
            }
            character => escaped.push(character),
        }
    }
    escaped
}