Skip to main content

codetether_agent/session/tasks/
render.rs

1//! Render a [`TaskState`] as a system-prompt governance block.
2
3use super::state::TaskState;
4
5/// Format the goal + open tasks as a prompt block.
6///
7/// Returns `None` when there is no active goal **and** no open tasks
8/// — in which case the governance middleware does not need to inject
9/// anything. A default "credential safety" rule is always included
10/// when a goal is present.
11pub fn governance_block(state: &TaskState) -> Option<String> {
12    if state.goal.is_none() && state.tasks.is_empty() {
13        return None;
14    }
15
16    let mut out = String::from("## Goal Governance\n");
17
18    if let Some(g) = &state.goal {
19        out.push_str("\nOBJECTIVE: ");
20        out.push_str(&g.objective);
21        out.push('\n');
22
23        if !g.success_criteria.is_empty() {
24            out.push_str("\nDONE WHEN:\n");
25            for c in &g.success_criteria {
26                out.push_str("- ");
27                out.push_str(c);
28                out.push('\n');
29            }
30        }
31
32        out.push_str("\nFORBIDDEN:\n");
33        out.push_str(
34            "- Entering credentials, passwords, or OAuth codes on behalf of the user. \
35             Stop at login walls and hand control back.\n",
36        );
37        for f in &g.forbidden {
38            out.push_str("- ");
39            out.push_str(f);
40            out.push('\n');
41        }
42
43        out.push_str(
44            "\nIf the next action does not advance OBJECTIVE, stop and ask the user \
45             before proceeding. Do not attempt to solve sub-problems that require \
46             information you do not have.\n",
47        );
48
49        if state.tool_calls_since_reaffirm >= 10 || state.errors_since_reaffirm >= 3 {
50            out.push_str(&format!(
51                "\nDRIFT WARNING: {} tool calls and {} errors since last goal \
52                 reaffirmation. State concretely what is done, what remains, or \
53                 whether you are blocked — then call `session_task` with \
54                 `action = \"reaffirm\"`.\n",
55                state.tool_calls_since_reaffirm, state.errors_since_reaffirm,
56            ));
57        }
58    }
59
60    let open = state.open_tasks();
61    if !open.is_empty() {
62        out.push_str("\nOPEN TASKS:\n");
63        for t in open {
64            let marker = match t.status {
65                super::event::TaskStatus::InProgress => "◐",
66                _ => "○",
67            };
68            out.push_str(&format!("{} [{}] {}\n", marker, t.id, t.content));
69        }
70    }
71
72    Some(out)
73}