Skip to main content

oxios_kernel/token_maxing/
planner.rs

1//! WorkPlanner — three-source task synthesis (RFC-031 §7).
2//!
3//! Selects the next task for the TokenMaxer from three prioritized sources,
4//! each filtered to non-destructive, bounded work (maxing runs unattended — no
5//! `rm`, no deploys, no outbound network beyond read):
6//!
7//! - **Source A — autonomous skills**: skills with frontmatter `autonomous:
8//!   true`. This is the "자주 실행되던 스킬" axis.
9//! - **Source B — registered projects/mounts**: a bounded, read-mostly review
10//!   task synthesized from each project's paths.
11//! - **Source C — recurring patterns**: stubbed (lowest priority; avoids
12//!   inventing work).
13
14use std::collections::HashSet;
15use std::path::PathBuf;
16use std::sync::Arc;
17
18use super::session::TaskSource;
19
20/// A unit of work the TokenMaxer can dispatch on one provider.
21#[derive(Debug, Clone)]
22pub struct PlannedTask {
23    /// Which source produced this task.
24    pub source: TaskSource,
25    /// Human label (skill/project name) — for the status panel + report.
26    pub source_name: String,
27    /// The synthesized goal prompt handed to the agent.
28    pub goal: String,
29    /// Workspace paths to sandbox the agent to (project tasks only).
30    pub mount_paths: Vec<PathBuf>,
31}
32
33/// Selects the next task for a maxing run.
34pub struct WorkPlanner {
35    skills: Arc<crate::skill::SkillManager>,
36    projects: Option<Arc<crate::project::ProjectManager>>,
37}
38
39impl WorkPlanner {
40    pub fn new(
41        skills: Arc<crate::skill::SkillManager>,
42        projects: Option<Arc<crate::project::ProjectManager>>,
43    ) -> Self {
44        Self { skills, projects }
45    }
46
47    /// Pick the next task not already done this session (`done_goals`).
48    ///
49    /// Returns `None` when no eligible work remains — the maxer then terminates
50    /// the window early ("stopped: nothing to do") rather than fabricating work.
51    pub async fn next_task(&self, done_goals: &HashSet<String>) -> Option<PlannedTask> {
52        // Source A — autonomous-eligible skills.
53        for entry in self.skills.list_skills().await {
54            let autonomous = entry
55                .metadata
56                .as_ref()
57                .map(|m| m.autonomous)
58                .unwrap_or(false);
59            if !autonomous {
60                continue;
61            }
62            let goal = format!(
63                "[Skill: {}] {}. Perform this skill's routine work autonomously and \
64                 summarize the outcome. Do not make destructive changes or run deploys.",
65                entry.skill.name, entry.skill.description
66            );
67            if done_goals.contains(&goal) {
68                continue;
69            }
70            return Some(PlannedTask {
71                source: TaskSource::Skill,
72                source_name: entry.skill.name.clone(),
73                goal,
74                mount_paths: Vec::new(),
75            });
76        }
77
78        // Source B — registered projects (read-mostly review).
79        if let Some(pm) = &self.projects {
80            for proj in pm.list_projects() {
81                if proj.paths.is_empty() {
82                    // Non-code project — no filesystem to review.
83                    continue;
84                }
85                let goal = format!(
86                    "[Project: {}] Review the recent state of this project: summarize open \
87                     work, recent changes, and any TODOs/FIXMEs. Read-only — do not modify \
88                     files or run deploys.",
89                    proj.name
90                );
91                if done_goals.contains(&goal) {
92                    continue;
93                }
94                return Some(PlannedTask {
95                    source: TaskSource::Project,
96                    source_name: proj.name.clone(),
97                    goal,
98                    mount_paths: proj.paths.clone(),
99                });
100            }
101        }
102
103        // Source C — recurring patterns: intentionally stubbed (RFC-031 §7).
104        None
105    }
106}