This commit is contained in:
eric
2026-04-04 05:57:58 +02:00
commit 97f329c825
55 changed files with 10026 additions and 0 deletions

98
src/planning/forwarder.rs Normal file
View File

@@ -0,0 +1,98 @@
use serde_json::json;
use crate::model::{self, ControllerState, PlannerResponse, TaskConfig};
pub fn planning_schema() -> serde_json::Value {
json!({
"type": "object",
"additionalProperties": false,
"required": ["kind", "question", "goal_md", "standards_md", "plan"],
"properties": {
"kind": { "type": "string", "enum": ["question", "final"] },
"question": { "type": ["string", "null"] },
"goal_md": { "type": ["string", "null"] },
"standards_md": { "type": ["string", "null"] },
"plan": {
"anyOf": [
model::plan_schema(),
{ "type": "null" }
]
}
}
})
}
pub fn build_planning_prompt(
config: &TaskConfig,
goal_md: &str,
standards_md: &str,
state: &ControllerState,
latest_user_input: &str,
) -> String {
let transcript = state
.planning_session
.transcript
.iter()
.map(|turn| format!("{}: {}", turn.role, turn.content))
.collect::<Vec<_>>()
.join("\n");
format!(
concat!(
"You are embedded Codex planning mode for a Rust autonomous controller.\n",
"You are only handling the planning phase.\n\n",
"Rules:\n",
"- Ask at most one follow-up question if the goal is still ambiguous.\n",
"- If you have enough information, return kind=final.\n",
"- Always include all response keys.\n",
"- Use null for any field that does not apply in the current response.\n",
"- The final plan must be decision-complete for autonomous execution.\n",
"- The plan should be maintainable and production-quality.\n",
"- The controller directory contains only Markdown and TOON files.\n\n",
"Task config paths:\n",
"- goal: {goal}\n",
"- plan: {plan}\n",
"- state: {state}\n",
"- standards: {standards}\n\n",
"Current goal markdown:\n{goal_md}\n\n",
"Current standards markdown:\n{standards_md}\n\n",
"Transcript so far:\n{transcript}\n\n",
"Latest user input:\n{latest}\n\n",
"When returning kind=final, include:\n",
"- goal_md: rewritten goal markdown\n",
"- standards_md: rewritten standards markdown\n",
"- plan: structured machine-readable plan object with ordered steps, verification, cleanup requirements, and statuses.\n"
),
goal = config.goal_file.display(),
plan = config.plan_file.display(),
state = config.state_file.display(),
standards = config.standards_file.display(),
goal_md = goal_md,
standards_md = standards_md,
transcript = transcript,
latest = latest_user_input,
)
}
pub fn parse_planning_response(raw: &str) -> anyhow::Result<PlannerResponse> {
Ok(serde_json::from_str(raw)?)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn planning_schema_requires_all_declared_keys() {
let schema = planning_schema();
assert_eq!(
schema["required"],
json!(["kind", "question", "goal_md", "standards_md", "plan"])
);
assert_eq!(
schema["properties"]["question"]["type"],
json!(["string", "null"])
);
assert!(schema["properties"]["plan"]["anyOf"].is_array());
}
}