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:
2026-07-23 16:55:39 -04:00
parent ff0fb56c67
commit a02b555e66
1458 changed files with 10729 additions and 21750 deletions
@@ -1,14 +1,9 @@
//! Subagent role and persona configuration types.
//!
//! These are the canonical definitions for `SubagentRole`, `SubagentPersona`,
//! and `PersonaIOField`. The shell re-exports them via
//! `kigi_shell::config::{SubagentRole, SubagentPersona, PersonaIOField}`.
//!
//! Methods that remain in `kigi-shell` (on `SubagentsConfig`):
//! - `discover_personas()` / `discover_roles()` — filesystem discovery
//! coupled to the shell's config resolution pipeline.
//! - `resolve()` — config layering (CLI > env > TOML > remote) is
//! shell-specific. This crate receives already-resolved maps.
//! These are the canonical definitions; the shell re-exports them via
//! `kigi_shell::config`. Filesystem discovery and config layering
//! (CLI > env > TOML > remote) stay in `kigi-shell` on `SubagentsConfig` —
//! this crate only ever sees already-resolved maps.
use kigi_tools::implementations::skills::discovery::extract_first_paragraph;
use std::path::PathBuf;
@@ -17,38 +12,29 @@ use serde::Deserialize;
/// A declarative subagent role definition from config.
///
/// Roles provide named presets that callers can reference via the
/// `subagent_type` field in the task tool. Each role can specify
/// a default capability mode, model override, and custom prompt.
/// Roles are named presets callers reference via the `subagent_type` field in
/// the task tool. Every default here can be overridden per-spawn.
#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize)]
#[serde(default)]
pub struct SubagentRole {
/// Human-readable description of what this role does.
pub description: String,
/// Default capability mode for agents using this role.
/// One of: "read-only", "read-write", "execute", "all".
/// Can be overridden per-spawn via `capability_mode` in the task tool.
#[serde(default)]
pub default_capability_mode: Option<String>,
/// Model override for this role. If set, agents using this role
/// default to this model unless the spawn-time `model` override
/// is provided.
#[serde(default)]
pub model: Option<String>,
/// Default reasoning effort for this role (e.g. "low", "medium", "high").
/// Can be overridden per-spawn via `reasoning_effort` in the task tool.
/// One of: "low", "medium", "high".
#[serde(default)]
pub reasoning_effort: Option<String>,
/// Path to a prompt/instruction file (relative to workspace root).
/// Loaded at spawn time and prepended to the child's prompt as a
/// `<role-instructions>` block.
#[serde(default)]
pub prompt_file: Option<String>,
/// Default isolation mode ("none" or "worktree").
/// One of: "none", "worktree".
#[serde(default)]
pub default_isolation: Option<String>,
/// Base directory for resolving relative `prompt_file` references.
/// Set to the parent dir of the source `.toml` file during discovery.
/// Base directory for resolving a relative `prompt_file`: the parent dir
/// of the source `.toml`, filled in during discovery.
#[serde(skip)]
pub source_dir: Option<PathBuf>,
}
@@ -61,60 +47,49 @@ pub struct SubagentRole {
#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize)]
#[serde(default)]
pub struct SubagentPersona {
/// Inline instruction text applied as a persona layer.
pub instructions: Option<String>,
/// Optional short description shown in persona summaries.
/// Falls back to first-paragraph extraction from `instructions`.
/// When absent, summaries fall back to the first paragraph of
/// `instructions`.
pub description: Option<String>,
/// Path to an instruction file (relative to workspace root).
/// Content is loaded at spawn time and merged with `instructions`.
/// If both are set, `instructions` is prepended before file content.
/// Loaded at spawn time and merged with `instructions`, which is prepended
/// before the file content when both are set.
pub instructions_file: Option<String>,
/// Declared inputs this persona expects. The parent agent reads these
/// to know what file paths or context to provide in the prompt.
#[serde(default)]
pub inputs: Vec<PersonaIOField>,
/// Declared outputs this persona produces. The parent agent reads
/// these to know what artifacts to expect and pass to the next agent.
#[serde(default)]
pub outputs: Vec<PersonaIOField>,
/// Default isolation mode when this persona is used.
/// One of: "none", "worktree".
#[serde(default)]
pub default_isolation: Option<String>,
/// Model override when this persona is used.
#[serde(default)]
pub model: Option<String>,
/// Default reasoning effort for this persona (e.g. "low", "medium", "high").
/// One of: "low", "medium", "high".
#[serde(default)]
pub reasoning_effort: Option<String>,
/// Base directory for resolving relative file references.
/// Set to the parent dir of the source `.toml` file during discovery.
/// When `None`, relative paths resolve against the workspace cwd.
/// Base directory for resolving relative file references: the parent dir
/// of the source `.toml`, filled in during discovery. When `None`,
/// relative paths resolve against the workspace cwd.
#[serde(skip)]
pub source_dir: Option<PathBuf>,
/// Absolute path to the source file this persona was loaded from.
/// Populated during discovery; `None` for inline config personas.
/// Absolute path of the source file, filled in during discovery. `None`
/// for personas declared inline in config.
#[serde(skip)]
pub source_path: Option<String>,
}
/// A declared input or output for a persona.
///
/// Enables the parent agent to discover what a persona needs (inputs)
/// and what it produces (outputs) without hardcoded knowledge of the
/// persona's protocol.
/// Lets the parent agent discover what a persona needs and what it produces
/// without hardcoded knowledge of the persona's protocol.
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, serde::Serialize)]
#[serde(deny_unknown_fields)]
pub struct PersonaIOField {
/// Short identifier (e.g. "review_file", "summary_file").
pub name: String,
/// What kind of artifact: "file", "text", etc.
/// Kind of artifact: "file", "text", etc.
#[serde(default = "PersonaIOField::default_io_type")]
pub io_type: String,
/// Whether this input/output is required.
#[serde(default)]
pub required: bool,
/// Human-readable description shown in the task tool help.
pub description: String,
}
@@ -125,8 +100,8 @@ impl PersonaIOField {
}
impl SubagentPersona {
/// Render a human-readable summary of this persona's IO contract
/// for inclusion in the task tool description.
/// Renders this persona's IO contract as Markdown for the task tool
/// description.
pub fn render_io_summary(&self, name: &str) -> String {
let fallback;
let desc = if let Some(d) = self.description.as_deref().filter(|s| !s.trim().is_empty()) {
@@ -22,11 +22,13 @@ const MAX_VERBATIM_TURNS: usize = 3;
/// strips a related (but different) tag set for compaction.
const FORK_NOISE_TAGS: &[&str] = &[
"system-reminder",
"system_reminder", // Cursor wire format uses underscore
// Cursor wire format uses underscore
"system_reminder",
"user_info",
"git_status",
"project_layout",
"attached_files", // Alternate-agent file context; child reads files itself
// Alternate-agent file context; child reads files itself
"attached_files",
];
/// Normalize a forked parent conversation into the shape:
@@ -56,7 +58,7 @@ pub fn normalize_forked_context(items: Vec<ConversationItem>) -> (Vec<Conversati
// Collect non-system items as the parent context to render.
let parent_items: Vec<&ConversationItem> = items
.iter()
.skip(1) // skip System
.skip(1)
.filter(|i| !matches!(i, ConversationItem::System(_)))
.collect();
@@ -135,7 +137,8 @@ fn count_complete_turns(items: &[&ConversationItem]) -> Vec<usize> {
if i >= items.len() || !matches!(items[i], ConversationItem::Assistant(_)) {
break;
}
i += 1; // skip past Assistant
// skip past Assistant
i += 1;
// Consume the post-assistant run: ToolResults plus interleaved
// Reasoning / BackendToolCall siblings, until the next User/Assistant.
while i < items.len()
@@ -374,7 +377,8 @@ fn render_summary(out: &mut String, items: &[&ConversationItem]) {
fn truncate_str(s: &str, max_chars: usize) -> &str {
match s.char_indices().nth(max_chars) {
Some((byte_offset, _)) => &s[..byte_offset],
None => s, // string has <= max_chars characters
// string has <= max_chars characters
None => s,
}
}
@@ -716,8 +720,8 @@ mod tests {
let refs: Vec<&ConversationItem> = items.iter().collect();
let turns = count_complete_turns(&refs);
assert_eq!(turns.len(), 2);
assert_eq!(turns[0], 2); // after A1
assert_eq!(turns[1], 4); // after A2
assert_eq!(turns[0], 2);
assert_eq!(turns[1], 4);
}
#[test]
@@ -733,7 +737,8 @@ mod tests {
let refs: Vec<&ConversationItem> = items.iter().collect();
let turns = count_complete_turns(&refs);
assert_eq!(turns.len(), 2);
assert_eq!(turns[0], 4); // after 2 tool results
// after 2 tool results
assert_eq!(turns[0], 4);
assert_eq!(turns[1], 6);
}
@@ -742,7 +747,8 @@ mod tests {
let items = [
user_item("U1"),
assistant_item("A1"),
user_item("U2"), // trailing User with no Assistant
// trailing User with no Assistant
user_item("U2"),
];
let refs: Vec<&ConversationItem> = items.iter().collect();
let turns = count_complete_turns(&refs);
@@ -765,8 +771,10 @@ mod tests {
let refs: Vec<&ConversationItem> = items.iter().collect();
let turns = count_complete_turns(&refs);
assert_eq!(turns.len(), 2);
assert_eq!(turns[0], 3); // after reasoning + A1
assert_eq!(turns[1], 6); // after reasoning + A2
// after reasoning + A1
assert_eq!(turns[0], 3);
// after reasoning + A2
assert_eq!(turns[1], 6);
}
#[test]
@@ -797,14 +805,14 @@ mod tests {
#[test]
fn truncate_str_multibyte_emoji() {
// Each emoji is 4 bytes. Truncating at 2 chars should yield 2 emojis (8 bytes).
let s = "\u{1F600}\u{1F601}\u{1F602}\u{1F603}"; // 4 emojis
let s = "\u{1F600}\u{1F601}\u{1F602}\u{1F603}";
assert_eq!(truncate_str(s, 2), "\u{1F600}\u{1F601}");
}
#[test]
fn truncate_str_multibyte_cjk() {
// CJK chars are 3 bytes each. Truncating at 3 chars should yield 3 chars (9 bytes).
let s = "\u{4F60}\u{597D}\u{4E16}\u{754C}"; // 4 CJK chars
let s = "\u{4F60}\u{597D}\u{4E16}\u{754C}";
assert_eq!(truncate_str(s, 3), "\u{4F60}\u{597D}\u{4E16}");
}
@@ -813,7 +821,7 @@ mod tests {
assert_eq!(strip_fork_noise(""), "");
}
// --- strip_xml_block tests ---
// strip_xml_block tests
#[test]
fn strip_xml_block_removes_system_reminder() {
@@ -866,7 +874,7 @@ mod tests {
assert_eq!(result, input);
}
// --- strip_skill_instructions tests ---
// strip_skill_instructions tests
#[test]
fn strip_skill_instructions_preserves_command_metadata() {
@@ -906,7 +914,7 @@ mod tests {
assert_eq!(result, "prefix </command-args>");
}
// --- collapse_blank_lines tests ---
// collapse_blank_lines tests
#[test]
fn collapse_blank_lines_reduces_runs() {
@@ -922,7 +930,7 @@ mod tests {
assert_eq!(result, input);
}
// --- strip_fork_noise integration tests ---
// strip_fork_noise integration tests
#[test]
fn strip_fork_noise_strips_user_info() {
@@ -1017,7 +1025,7 @@ This is a very long skill body with many lines of instructions.\n\n\
assert!(!result.contains("**ARGUMENTS:**"));
}
// --- normalize_forked_context integration tests ---
// normalize_forked_context integration tests
#[test]
fn normalize_forked_context_empty_after_strip() {
@@ -1,8 +1,8 @@
//! Subagent configuration resolution crate.
//!
//! Extracts the pure-logic "resolution" phase of subagent spawning from
//! `kigi-shell` into a reusable library. Given a spawn request and a
//! resolution context (roles, personas, parent state), this crate resolves:
//! The pure-logic "resolution" phase of subagent spawning. Given a spawn
//! request and a resolution context (roles, personas, parent state), this
//! crate resolves:
//!
//! - Effective runtime config (model, persona, capability mode, isolation)
//! via precedence: explicit override > role > persona > parent.
@@ -10,20 +10,16 @@
//! - Role prompt file loading.
//! - Resume identity validation (type/persona match checks; model is soft-ignored).
//!
//! This crate has no dependency on session, coordinator, or transport types.
//! Designed to be consumed by local hosts (e.g. `kigi-shell`) and any
//! future remote spawn path that only needs pure resolution logic.
//! Nothing here may depend on session, coordinator, or transport types: local
//! hosts (e.g. `kigi-shell`) and any remote spawn path must both be able to
//! consume it.
//!
//! ## Planned composition API
//!
//! Future work may add a higher-level composition helper once shell call sites
//! are refactored onto this crate:
//!
//! - `resolve_subagent_spec()` composition function
//! - `SubagentSpec`, `ResolveSubagentRequest`, `ResolutionContext` boundary types
//! - Optional deps for `AgentDefinition` lookup and worktree creation
//! - Model override resolution chain (global > per-type > role > parent)
//! - Capability mode filtering (delegates to `SubagentCapabilityMode::filter_tool_config()`)
//! TODO: add a `resolve_subagent_spec()` composition entry point once shell
//! call sites move onto this crate. It needs `SubagentSpec` /
//! `ResolveSubagentRequest` / `ResolutionContext` boundary types, optional deps
//! for `AgentDefinition` lookup and worktree creation, the global > per-type >
//! role > parent model override chain, and capability mode filtering via
//! `SubagentCapabilityMode::filter_tool_config()`.
pub mod config;
pub mod context;
@@ -45,19 +45,19 @@ pub fn resolve_effective_overrides(
cwd: Option<&Path>,
role_name: Option<String>,
) -> EffectiveRuntimeConfig {
// ── Model resolution ─────────────────────────────────────────
// Model resolution
let model_from_override_or_role = overrides
.model
.clone()
.or_else(|| role.and_then(|r| r.model.clone()));
// ── Reasoning effort resolution ──────────────────────────────
// Reasoning effort resolution
let reasoning_from_override_or_role = overrides
.reasoning_effort
.clone()
.or_else(|| role.and_then(|r| r.reasoning_effort.clone()));
// ── Capability mode resolution ───────────────────────────────
// Capability mode resolution
let capability_mode = overrides.capability_mode.or_else(|| {
role.and_then(|r| {
r.default_capability_mode
@@ -66,7 +66,7 @@ pub fn resolve_effective_overrides(
})
});
// ── Persona resolution ───────────────────────────────────────
// Persona resolution
let persona = overrides.persona.clone();
let resolved_persona = persona.as_deref().and_then(|name| personas.get(name));
@@ -76,7 +76,7 @@ pub fn resolve_effective_overrides(
let reasoning_effort = reasoning_from_override_or_role
.or_else(|| resolved_persona.and_then(|p| p.reasoning_effort.clone()));
// ── Persona instructions loading ─────────────────────────────
// Persona instructions loading
// Fail-closed: if persona resolution produces an error (file unreadable,
// not found, empty), return early with only persona + error populated.
// All other fields are defaulted. This matches the shell's behavior where
@@ -96,7 +96,7 @@ pub fn resolve_effective_overrides(
};
}
// ── Role prompt file loading (soft degradation) ──────────────
// Role prompt file loading (soft degradation)
let mut role_prompt_warning = None;
let role_prompt = role.and_then(|r| {
let file_path = r.prompt_file.as_deref()?;
@@ -112,7 +112,7 @@ pub fn resolve_effective_overrides(
}
});
// ── Isolation resolution ─────────────────────────────────────
// Isolation resolution
let isolation = overrides
.isolation
.or_else(|| {
@@ -156,7 +156,8 @@ fn resolve_persona_instructions(
return (
None,
Some(format!("persona \"{name}\" not found in config")),
false, // not fatal — config error, other fields still resolve
// not fatal — config error, other fields still resolve
false,
);
};
@@ -176,7 +177,8 @@ fn resolve_persona_instructions(
"persona \"{name}\": failed to read instructions_file \
\"{file_path}\": {e}"
);
return (None, Some(err), true); // fatal — file I/O error
// fatal — file I/O error
return (None, Some(err), true);
}
},
None => {
@@ -184,7 +186,8 @@ fn resolve_persona_instructions(
"persona \"{name}\": cannot resolve instructions_file \
\"{file_path}\": no source_dir or cwd available"
);
return (None, Some(err), true); // fatal — unresolvable path
// fatal — unresolvable path
return (None, Some(err), true);
}
}
}
@@ -195,7 +198,8 @@ fn resolve_persona_instructions(
Some(format!(
"persona \"{name}\" has no instructions or instructions_file"
)),
false, // not fatal — config error, other fields still resolve
// not fatal — config error, other fields still resolve
false,
)
} else {
(Some(parts.join("\n\n")), None, false)
@@ -232,7 +236,7 @@ mod tests {
HashMap::new()
}
// ── Precedence tests ─────────────────────────────────────────
// Precedence tests
#[test]
fn explicit_model_overrides_role() {
@@ -329,7 +333,7 @@ mod tests {
assert!(result.capability_mode.is_none());
}
// ── Reasoning effort precedence ──────────────────────────────
// Reasoning effort precedence
#[test]
fn explicit_reasoning_effort_overrides_role_and_persona() {
@@ -387,7 +391,7 @@ mod tests {
assert_eq!(result.reasoning_effort.as_deref(), Some("medium"));
}
// ── Isolation precedence ─────────────────────────────────────
// Isolation precedence
#[test]
fn explicit_isolation_overrides_role() {
@@ -426,7 +430,7 @@ mod tests {
assert_eq!(result.isolation, SubagentIsolationMode::None);
}
// ── Persona instruction loading ──────────────────────────────
// Persona instruction loading
#[test]
fn persona_inline_instructions_only() {
@@ -543,7 +547,7 @@ mod tests {
);
}
// ── Role prompt file loading ─────────────────────────────────
// Role prompt file loading
#[test]
fn role_prompt_file_loaded_on_success() {
@@ -581,7 +585,7 @@ mod tests {
assert!(result.role_prompt_warning.is_some());
}
// ── No persona requested ─────────────────────────────────────
// No persona requested
#[test]
fn no_persona_no_instructions() {
@@ -592,7 +596,7 @@ mod tests {
assert!(result.persona_error.is_none());
}
// ── Persona with cwd fallback for instructions_file ──────────
// Persona with cwd fallback for instructions_file
#[test]
fn persona_instructions_file_uses_cwd_when_no_source_dir() {
@@ -619,7 +623,7 @@ mod tests {
assert!(result.persona_error.is_none());
}
// ── Persona error early-return (fail-closed) ─────────────────
// Persona error early-return (fail-closed)
#[test]
fn persona_not_found_error_is_non_fatal() {
@@ -679,7 +683,7 @@ mod tests {
);
}
// ── instructions_file with no base dir ────────────────────────
// instructions_file with no base dir
#[test]
fn persona_instructions_file_no_base_dir_returns_error() {
@@ -704,7 +708,7 @@ mod tests {
);
}
// ── Persona isolation fallback (role has no isolation, persona does) ──
// Persona isolation fallback (role has no isolation, persona does)
#[test]
fn persona_isolation_used_when_no_explicit_or_role() {
@@ -723,7 +727,7 @@ mod tests {
assert_eq!(result.isolation, SubagentIsolationMode::Worktree);
}
// ── Role prompt file cwd fallback ─────────────────────────────
// Role prompt file cwd fallback
#[test]
fn role_prompt_file_uses_cwd_when_no_source_dir() {
@@ -751,7 +755,7 @@ mod tests {
assert!(result.role_prompt_warning.is_none());
}
// ── role_name parameter is threaded through ───────────────────
// role_name parameter is threaded through
#[test]
fn role_name_parameter_threaded_through() {
@@ -92,7 +92,7 @@ mod tests {
}
}
// ── Matching cases ───────────────────────────────────────────
// Matching cases
#[test]
fn matching_type_no_persona() {
@@ -131,7 +131,7 @@ mod tests {
assert!(result.is_ok());
}
// ── Mismatching cases ────────────────────────────────────────
// Mismatching cases
#[test]
fn type_mismatch_rejected() {
@@ -168,7 +168,7 @@ mod tests {
));
}
// ── Validation order ─────────────────────────────────────────
// Validation order
#[test]
fn type_mismatch_checked_before_persona() {
@@ -9,7 +9,7 @@ use crate::resume::ResumeValidationError;
pub enum ContextSource {
/// Fresh session with no inherited history.
New,
/// Resumed from a previously completed peer subagent. The child inherits
/// Resumed from a earlier completed peer subagent. The child inherits
/// the source's raw transcript, tool state, and model. System prompt and
/// prompt context are freshly rendered.
Resumed,