docs(comments): rewrite comments across all crates to the guidelines
Sweep every first-party crate source (1956 .rs files) to the project comment guidelines: delete redundant restatements, decorative banners, change narration, and end-of-line comments; keep and tighten the crucial ones (invariants, bug rationale, SAFETY blocks, ported-source attribution). No functional code changed. Every edit is proven comment-only against the prior tree by a comment-stripping lexer (string/char/raw-string aware) plus a separate doctest-fence check. Where removing a comment made rustfmt or clippy want to re-lay-out adjacent code, the minimal triggering comment is restored so code tokens stay byte-identical. Gates green: cargo fmt --all --check (0 diffs), cargo check and cargo clippy --workspace --all-targets (0 warnings). Adds scripts/check_codegen_comment_guidelines.py — the enforcement gate for these guidelines (flags banners, end-of-line comments, change narration, and commented-out code).
This commit is contained in:
@@ -9,7 +9,7 @@ pub struct AcpTerminalRunner {
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
// Terminal release on cancel is now handled by kill_and_release_all_for_session()
|
||||
// Terminal release on cancel is handled by kill_and_release_all_for_session()
|
||||
// in cancel_running_task() — see acp_session.rs.
|
||||
impl AsyncTerminalRunner for AcpTerminalRunner {
|
||||
async fn run(&self, request: TerminalRunRequest) -> Result<TerminalRunResult, TerminalError> {
|
||||
@@ -42,7 +42,6 @@ impl AsyncTerminalRunner for AcpTerminalRunner {
|
||||
.await
|
||||
.map_err(|e| TerminalError::Other(e.to_string()))?;
|
||||
|
||||
// notify the client about the terminal
|
||||
let notification = acp::SessionNotification::new(
|
||||
session_id.clone(),
|
||||
acp::SessionUpdate::ToolCallUpdate(acp::ToolCallUpdate::new(
|
||||
|
||||
@@ -21,8 +21,6 @@ use kigi_tools::computer::types::{
|
||||
};
|
||||
use kigi_tools::notification::types::ToolNotificationHandle;
|
||||
|
||||
// ── Tracked task state ───────────────────────────────────────────────
|
||||
|
||||
struct TrackedTask {
|
||||
command: String,
|
||||
display_command: Option<String>,
|
||||
@@ -85,8 +83,6 @@ impl TrackedTask {
|
||||
|
||||
type TaskMap = Arc<Mutex<HashMap<String, TrackedTask>>>;
|
||||
|
||||
// ── Exit watcher ─────────────────────────────────────────────────────
|
||||
|
||||
/// Spawned per background task. Blocks on `WaitForTerminalExitRequest`,
|
||||
/// then fetches final output, emits `TaskCompleted`, and releases the
|
||||
/// terminal.
|
||||
@@ -169,8 +165,6 @@ async fn watch_for_exit(
|
||||
.await;
|
||||
}
|
||||
|
||||
// ── Helpers ──────────────────────────────────────────────────────────
|
||||
|
||||
/// Poll `TerminalOutputRequest` at 500ms intervals until `exit_status` is
|
||||
/// present, a deadline is hit, or 60 consecutive gateway errors occur.
|
||||
/// Returns `true` when an exit was detected.
|
||||
@@ -248,8 +242,6 @@ fn parse_exit(status: &Option<acp::TerminalExitStatus>) -> (Option<i32>, Option<
|
||||
}
|
||||
}
|
||||
|
||||
// ── Adapter ──────────────────────────────────────────────────────────
|
||||
|
||||
/// Wraps kigi-shell's ACP gateway to satisfy kigi-tools' TerminalBackend.
|
||||
pub struct AcpTerminalAdapter {
|
||||
gateway: GatewaySender,
|
||||
|
||||
@@ -35,23 +35,16 @@ pub struct TaskSnapshot {
|
||||
pub task_id: TaskId,
|
||||
/// Internal tool_call_id for terminal registry lookup
|
||||
pub tool_call_id: String,
|
||||
/// The command that was executed
|
||||
pub command: String,
|
||||
/// Working directory where command was run
|
||||
pub cwd: String,
|
||||
/// Wall-clock start time
|
||||
pub start_time: DateTime<Utc>,
|
||||
/// Wall-clock end time (set when task completes)
|
||||
pub end_time: Option<DateTime<Utc>>,
|
||||
/// In-memory output (may be truncated if > output_byte_limit)
|
||||
pub output: String,
|
||||
/// Path to full output file on disk
|
||||
pub output_file: PathBuf,
|
||||
/// Whether in-memory output was truncated
|
||||
pub truncated: bool,
|
||||
/// Exit code if completed
|
||||
pub exit_code: Option<i32>,
|
||||
/// Signal name if terminated by signal
|
||||
pub signal: Option<String>,
|
||||
/// Whether task has completed (exited or was killed)
|
||||
pub completed: bool,
|
||||
@@ -76,7 +69,6 @@ impl TaskSnapshot {
|
||||
struct TaskEntry {
|
||||
/// The task snapshot (protected by RwLock for concurrent reads)
|
||||
snapshot: RwLock<TaskSnapshot>,
|
||||
/// Notifier for waiters when task completes
|
||||
exit_notify: Arc<Notify>,
|
||||
}
|
||||
|
||||
@@ -88,13 +80,10 @@ struct TaskEntry {
|
||||
/// - Session cleanup automatically cleans up tasks
|
||||
/// - No global state pollution between agents
|
||||
pub struct BackgroundTaskRegistry {
|
||||
/// Map from task_id -> entry
|
||||
tasks: Mutex<HashMap<TaskId, Arc<TaskEntry>>>,
|
||||
/// Maximum number of concurrent tasks
|
||||
max_tasks: usize,
|
||||
}
|
||||
|
||||
/// Default maximum number of concurrent background tasks per session
|
||||
const DEFAULT_MAX_BACKGROUND_TASKS: usize = 10;
|
||||
|
||||
impl Default for BackgroundTaskRegistry {
|
||||
@@ -104,7 +93,6 @@ impl Default for BackgroundTaskRegistry {
|
||||
}
|
||||
|
||||
impl BackgroundTaskRegistry {
|
||||
/// Create a new registry with default max tasks limit.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
tasks: Mutex::new(HashMap::new()),
|
||||
@@ -127,7 +115,6 @@ impl BackgroundTaskRegistry {
|
||||
pub async fn register(&self, snapshot: TaskSnapshot) -> Result<(), String> {
|
||||
let mut tasks = self.tasks.lock().await;
|
||||
|
||||
// Cleanup completed tasks if at capacity
|
||||
if tasks.len() >= self.max_tasks {
|
||||
tasks.retain(|_, entry| {
|
||||
// Keep if not completed (check synchronously via try_read)
|
||||
@@ -155,9 +142,6 @@ impl BackgroundTaskRegistry {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get current snapshot of a task.
|
||||
///
|
||||
/// Returns `None` if task_id is not found.
|
||||
pub async fn get(&self, task_id: &str) -> Option<TaskSnapshot> {
|
||||
let tasks = self.tasks.lock().await;
|
||||
let entry = tasks.get(task_id)?;
|
||||
@@ -192,14 +176,10 @@ impl BackgroundTaskRegistry {
|
||||
snapshot.signal = signal;
|
||||
snapshot.end_time = Some(Utc::now());
|
||||
}
|
||||
// Notify all waiters that task has completed
|
||||
entry.exit_notify.notify_waiters();
|
||||
}
|
||||
}
|
||||
|
||||
/// Wait for task completion with optional timeout.
|
||||
///
|
||||
/// Returns the task snapshot after completion or timeout.
|
||||
/// Returns `None` if task_id is not found.
|
||||
pub async fn wait_for_completion(
|
||||
&self,
|
||||
@@ -236,16 +216,12 @@ impl BackgroundTaskRegistry {
|
||||
Some(entry.snapshot.read().await.clone())
|
||||
}
|
||||
|
||||
/// Get a cloneable notification handle for a specific task.
|
||||
///
|
||||
/// Used by multi-wait to select across multiple task exit notifications.
|
||||
/// Returns `None` if the task is not registered.
|
||||
pub async fn get_exit_notify(&self, task_id: &str) -> Option<Arc<Notify>> {
|
||||
let tasks = self.tasks.lock().await;
|
||||
tasks.get(task_id).map(|e| Arc::clone(&e.exit_notify))
|
||||
}
|
||||
|
||||
/// List all tasks in the registry.
|
||||
pub async fn list(&self) -> Vec<TaskSnapshot> {
|
||||
let tasks = self.tasks.lock().await;
|
||||
let mut result = Vec::with_capacity(tasks.len());
|
||||
@@ -281,8 +257,6 @@ pub fn get_task_output_path(session_id: &str, task_id: &str) -> PathBuf {
|
||||
tasks_dir.join(format!("{}.log", task_id))
|
||||
}
|
||||
|
||||
// ── Background task manifest for session resume ──
|
||||
|
||||
const MANIFEST_FILENAME: &str = "background_tasks_manifest.json";
|
||||
|
||||
/// Minimal snapshot of a running background task, persisted on session exit
|
||||
@@ -468,7 +442,6 @@ mod tests {
|
||||
.unwrap();
|
||||
registry.mark_completed("test-1", Some(0), None).await;
|
||||
|
||||
// Should return immediately since already completed
|
||||
let got = registry
|
||||
.wait_for_completion("test-1", Some(Duration::from_millis(100)))
|
||||
.await
|
||||
@@ -484,13 +457,13 @@ mod tests {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Start waiting with short timeout
|
||||
let got = registry
|
||||
.wait_for_completion("test-1", Some(Duration::from_millis(50)))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Should return with incomplete status (timed out)
|
||||
// wait_for_completion returns Some with completed=false on timeout,
|
||||
// not None.
|
||||
assert!(!got.completed);
|
||||
}
|
||||
|
||||
@@ -504,7 +477,6 @@ mod tests {
|
||||
|
||||
let registry_clone = registry.clone();
|
||||
|
||||
// Spawn task to complete after short delay
|
||||
tokio::spawn(async move {
|
||||
tokio::time::sleep(Duration::from_millis(50)).await;
|
||||
registry_clone
|
||||
@@ -512,7 +484,6 @@ mod tests {
|
||||
.await;
|
||||
});
|
||||
|
||||
// Wait for completion
|
||||
let got = registry
|
||||
.wait_for_completion("test-1", Some(Duration::from_secs(5)))
|
||||
.await
|
||||
@@ -526,7 +497,6 @@ mod tests {
|
||||
async fn test_max_tasks_limit() {
|
||||
let registry = BackgroundTaskRegistry::with_max_tasks(2);
|
||||
|
||||
// Register up to limit
|
||||
registry
|
||||
.register(make_test_snapshot("task-1"))
|
||||
.await
|
||||
@@ -536,7 +506,6 @@ mod tests {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Third should fail
|
||||
let result = registry.register(make_test_snapshot("task-3")).await;
|
||||
assert!(result.is_err());
|
||||
assert!(result.unwrap_err().contains("Maximum background tasks"));
|
||||
@@ -555,16 +524,14 @@ mod tests {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Mark first as completed
|
||||
registry.mark_completed("task-1", Some(0), None).await;
|
||||
|
||||
// Now third should succeed (completed task cleaned up)
|
||||
// Registering at capacity triggers cleanup of completed tasks first.
|
||||
registry
|
||||
.register(make_test_snapshot("task-3"))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Verify task-1 was cleaned up
|
||||
assert!(registry.get("task-1").await.is_none());
|
||||
assert!(registry.get("task-3").await.is_some());
|
||||
}
|
||||
@@ -603,8 +570,6 @@ mod tests {
|
||||
assert_eq!(registry.active_count().await, 1);
|
||||
}
|
||||
|
||||
// ── Manifest tests ──
|
||||
|
||||
fn make_manifest_entry(task_id: &str, secs_ago: u64) -> BackgroundTaskManifestEntry {
|
||||
BackgroundTaskManifestEntry {
|
||||
task_id: task_id.to_string(),
|
||||
|
||||
@@ -24,18 +24,15 @@ async fn read_stream(mut stream: impl AsyncReadExt + Unpin) -> Vec<u8> {
|
||||
/// by using char_indices to find a valid character boundary.
|
||||
fn truncate_buffer(buf: &mut Vec<u8>, limit: usize) -> bool {
|
||||
if buf.len() > limit {
|
||||
// Convert to string to work with character boundaries
|
||||
let s = String::from_utf8_lossy(buf);
|
||||
let excess = buf.len().saturating_sub(limit);
|
||||
|
||||
// Find the first char boundary at or after `excess` bytes
|
||||
let start_idx = s
|
||||
.char_indices()
|
||||
.find(|(i, _)| *i >= excess)
|
||||
.map(|(i, _)| i)
|
||||
.unwrap_or(s.len());
|
||||
|
||||
// Slice from that boundary and update buffer
|
||||
*buf = s[start_idx..].as_bytes().to_vec();
|
||||
|
||||
true
|
||||
@@ -47,7 +44,6 @@ fn truncate_buffer(buf: &mut Vec<u8>, limit: usize) -> bool {
|
||||
#[async_trait::async_trait]
|
||||
impl AsyncTerminalRunner for LocalTerminalRunner {
|
||||
async fn run(&self, request: TerminalRunRequest) -> Result<TerminalRunResult, TerminalError> {
|
||||
// Build and spawn the command via the platform shell.
|
||||
#[cfg(unix)]
|
||||
let mut cmd = {
|
||||
let mut c = Command::new(crate::terminal::default_shell_path());
|
||||
@@ -108,7 +104,6 @@ impl AsyncTerminalRunner for LocalTerminalRunner {
|
||||
let stdout_result = stdout_task.await.unwrap_or_else(|_| Vec::new());
|
||||
let stderr_result = stderr_task.await.unwrap_or_else(|_| Vec::new());
|
||||
|
||||
// Combine stdout and stderr, then truncate if needed
|
||||
let mut combined = stdout_result;
|
||||
combined.extend(stderr_result);
|
||||
let truncated = truncate_buffer(&mut combined, request.output_byte_limit);
|
||||
|
||||
@@ -19,7 +19,7 @@ pub use adapter::AcpTerminalAdapter;
|
||||
pub mod pty_session;
|
||||
|
||||
pub const DEFAULT_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10);
|
||||
pub const DEFAULT_OUTPUT_BYTE_LIMIT: usize = 30_000; // 30k characters
|
||||
pub const DEFAULT_OUTPUT_BYTE_LIMIT: usize = 30_000;
|
||||
|
||||
/// Resolved absolute path to bash. On Unix uses the `kigi_config` shell
|
||||
/// resolution cascade (`$KIGI_SHELL` > `$SHELL` > `which` > common dirs >
|
||||
|
||||
@@ -411,7 +411,6 @@ pub async fn background_terminal(session_id: &str, terminal_id: &str) {
|
||||
let mut state = entry.output_state.lock().await;
|
||||
state.backgrounded = true;
|
||||
}
|
||||
// Notify waiters so they can return early
|
||||
entry.exit_notify.notify_waiters();
|
||||
}
|
||||
|
||||
@@ -559,7 +558,6 @@ impl StreamingLocalTerminalRunner {
|
||||
// Open file handle once at start (more efficient than open/close per write)
|
||||
let mut file_handle: Option<tokio::fs::File> = match &output_file {
|
||||
Some(path) => {
|
||||
// Ensure parent directory exists
|
||||
if let Some(parent) = path.parent() {
|
||||
let _ = tokio::fs::create_dir_all(parent).await;
|
||||
}
|
||||
@@ -597,7 +595,6 @@ impl StreamingLocalTerminalRunner {
|
||||
{
|
||||
let state = output_state.lock().await;
|
||||
if state.backgrounded {
|
||||
// Flush file before returning
|
||||
if let Some(ref mut file) = file_handle {
|
||||
let _ = file.flush().await;
|
||||
}
|
||||
@@ -615,7 +612,6 @@ impl StreamingLocalTerminalRunner {
|
||||
&& stderr.is_none()
|
||||
&& let Some(process_status) = try_get_exit_status(&child_handle).await
|
||||
{
|
||||
// Flush file before returning
|
||||
if let Some(ref mut file) = file_handle {
|
||||
let _ = file.flush().await;
|
||||
}
|
||||
@@ -711,7 +707,6 @@ impl StreamingLocalTerminalRunner {
|
||||
}
|
||||
}
|
||||
_ = &mut sleep => {
|
||||
// Flush file before returning
|
||||
if let Some(ref mut file) = file_handle {
|
||||
let _ = file.flush().await;
|
||||
}
|
||||
@@ -991,7 +986,6 @@ async fn take_child_io(
|
||||
/// by using char_indices to find a valid character boundary.
|
||||
fn truncate_buffer(buf: &mut Vec<u8>, limit: usize) -> bool {
|
||||
if buf.len() > limit {
|
||||
// Convert to string to work with character boundaries
|
||||
let s = String::from_utf8_lossy(buf);
|
||||
let excess = buf.len().saturating_sub(limit);
|
||||
|
||||
@@ -1002,7 +996,6 @@ fn truncate_buffer(buf: &mut Vec<u8>, limit: usize) -> bool {
|
||||
.map(|(i, _)| i)
|
||||
.unwrap_or(s.len());
|
||||
|
||||
// Slice from that boundary and update buffer
|
||||
*buf = s[start_idx..].as_bytes().to_vec();
|
||||
|
||||
true
|
||||
@@ -1050,12 +1043,10 @@ async fn wait_background_completion(
|
||||
) {
|
||||
use kigi_tools::types::output::{BashOutput, ToolOutput};
|
||||
|
||||
// Wait for the process to exit
|
||||
loop {
|
||||
if let Some(process_status) = try_get_exit_status(&child_handle).await {
|
||||
let exit_status = extract_exit_status(process_status);
|
||||
|
||||
// Get final output from state
|
||||
let (output_buf, truncated) = {
|
||||
let state = output_state.lock().await;
|
||||
(state.output.clone(), state.truncated)
|
||||
@@ -1108,7 +1099,6 @@ async fn wait_background_completion(
|
||||
return;
|
||||
}
|
||||
|
||||
// Sleep briefly before checking again
|
||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||
}
|
||||
}
|
||||
@@ -1389,23 +1379,19 @@ mod tests {
|
||||
// Wait for both to start.
|
||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||
|
||||
// Mark one as backgrounded.
|
||||
background_terminal(&session_id, &bg_id).await;
|
||||
|
||||
// Sanity: both are in the registry.
|
||||
assert!(get_terminal_output(&session_id, &normal_id).await.is_some());
|
||||
assert!(get_terminal_output(&session_id, &bg_id).await.is_some());
|
||||
|
||||
// Kill all non-backgrounded terminals for the session.
|
||||
kill_and_release_all_for_session(&session_id).await;
|
||||
|
||||
// Normal terminal should be gone.
|
||||
assert!(
|
||||
get_terminal_output(&session_id, &normal_id).await.is_none(),
|
||||
"non-backgrounded terminal should be removed from registry"
|
||||
);
|
||||
|
||||
// Backgrounded terminal should still be present.
|
||||
assert!(
|
||||
get_terminal_output(&session_id, &bg_id).await.is_some(),
|
||||
"backgrounded terminal should remain in registry"
|
||||
@@ -1519,7 +1505,6 @@ mod tests {
|
||||
let session_a = format!("kill-all-a-{}", std::process::id());
|
||||
let session_b = format!("kill-all-b-{}", std::process::id());
|
||||
|
||||
// Create a terminal in session B.
|
||||
let id_b = create_terminal(
|
||||
&session_b,
|
||||
"sleep",
|
||||
@@ -1536,13 +1521,11 @@ mod tests {
|
||||
// Kill all for session A (different session).
|
||||
kill_and_release_all_for_session(&session_a).await;
|
||||
|
||||
// Session B terminal should be untouched.
|
||||
assert!(
|
||||
get_terminal_output(&session_b, &id_b).await.is_some(),
|
||||
"terminals in other sessions should not be affected"
|
||||
);
|
||||
|
||||
// Clean up.
|
||||
release_terminal(&session_b, &id_b).await;
|
||||
})
|
||||
.await;
|
||||
|
||||
Reference in New Issue
Block a user