diff --git a/src/toktrim/estimators.py b/src/toktrim/estimators.py
index f3f02fb..4eb3867 100644
--- a/src/toktrim/estimators.py
+++ b/src/toktrim/estimators.py
@@ -6,6 +6,7 @@ from functools import lru_cache
 
 @lru_cache(maxsize=1)
 def _get_encoder():
+    """Return a cached cl100k_base encoder if tiktoken is available, else None."""
     try:
         import tiktoken  # type: ignore
     except Exception:
diff --git a/src/toktrim/processors.py b/src/toktrim/processors.py
index 1f4baaa..eb45ad0 100644
--- a/src/toktrim/processors.py
+++ b/src/toktrim/processors.py
@@ -8,14 +8,17 @@ ANSI_RE = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])")
 
 
 def strip_ansi(text: str) -> str:
+    """Remove ANSI escape sequences (color codes, cursor moves) from `text`."""
     return ANSI_RE.sub("", text)
 
 
 def normalize_newlines(text: str) -> str:
+    """Convert CRLF and bare CR line endings to LF."""
     return text.replace("\r\n", "\n").replace("\r", "\n")
 
 
 def truncate_line(line: str, max_line_length: int) -> str:
+    """Trim `line` to at most `max_line_length` chars, marking truncation with an ellipsis."""
     if max_line_length <= 0 or len(line) <= max_line_length:
         return line
     if max_line_length <= 3:
@@ -24,6 +27,7 @@ def truncate_line(line: str, max_line_length: int) -> str:
 
 
 def collapse_consecutive_duplicates(lines: Iterable[str]) -> list[str]:
+    """Replace runs of identical adjacent lines with a single occurrence + repeat marker."""
     output: list[str] = []
     previous: str | None = None
     repeat_count = 0
@@ -54,6 +58,7 @@ def collapse_consecutive_duplicates(lines: Iterable[str]) -> list[str]:
 
 
 def collapse_blank_runs(lines: Iterable[str]) -> list[str]:
+    """Collapse runs of blank lines into a single blank line."""
     output: list[str] = []
     blank_open = False
 
@@ -73,6 +78,7 @@ def collapse_blank_runs(lines: Iterable[str]) -> list[str]:
 
 
 def trim_middle(lines: list[str], max_lines: int, tail_lines: int) -> list[str]:
+    """Keep the first `max_lines - tail_lines` and last `tail_lines` lines, eliding the middle."""
     if max_lines <= 0 or len(lines) <= max_lines:
         return lines
 
@@ -93,6 +99,7 @@ def compress_logs(
     tail_lines: int = 40,
     max_line_length: int = 240,
 ) -> str:
+    """Strip ANSI, dedupe adjacent lines, cap line length, and elide the middle if too long."""
     text = normalize_newlines(strip_ansi(text))
     lines = text.split("\n")
     baseline_lines = [truncate_line(line.rstrip(), max_line_length) for line in lines]
@@ -161,6 +168,7 @@ def compress_unified_diff(
     context_lines: int = 2,
     max_hunks: int = 12,
 ) -> str:
+    """Compress a unified diff: keep change lines + narrow context, cap total hunks."""
     text = normalize_newlines(text)
     baseline = text.strip()
     lines = text.split("\n")
@@ -217,6 +225,7 @@ def summarize_conversation(
     summary_lines: int = 10,
     max_line_length: int = 200,
 ) -> str:
+    """Keep the most recent turns verbatim; collapse earlier turns into deduped bullets."""
     text = normalize_newlines(text)
     baseline = text.strip()
     lines = [line.rstrip() for line in text.split("\n")]
diff --git a/src/toktrim/renderers.py b/src/toktrim/renderers.py
index d16932a..139fa9b 100644
--- a/src/toktrim/renderers.py
+++ b/src/toktrim/renderers.py
@@ -4,6 +4,7 @@ from .models import ConversationMessage, TaskProfile, TransformResult
 
 
 def build_instructions(profile: TaskProfile | None, override: str | None) -> str | None:
+    """Concatenate the profile's system prompt with the user's override, if any."""
     parts: list[str] = []
     if profile and profile.system_prompt:
         parts.append(profile.system_prompt.strip())
@@ -20,6 +21,7 @@ def build_request_text(
     prompt: str,
     transform: TransformResult,
 ) -> str:
+    """Format the user-facing prompt body with task header, request, and trimmed context."""
     sections: list[str] = [f"Task: {task_name}", f"Requested work:\n{prompt.strip()}"]
 
     if transform.transformed_text.strip():
@@ -38,6 +40,7 @@ def build_turn_messages(
     override_instructions: str | None,
     include_instructions: bool = True,
 ) -> list[ConversationMessage]:
+    """Build the developer + user message pair for a single turn."""
     messages: list[ConversationMessage] = []
     instructions = build_instructions(profile, override_instructions)
     if include_instructions and instructions:
@@ -57,6 +60,7 @@ def build_turn_messages(
 
 
 def format_transform_stats(transform: TransformResult) -> str:
+    """Render a human-readable before/after token + char summary for the given transform."""
     lines = [
         f"mode: {transform.mode.value}",
         f"original chars: {transform.original_chars}",
diff --git a/src/toktrim/transforms.py b/src/toktrim/transforms.py
index b706dfb..9c21158 100644
--- a/src/toktrim/transforms.py
+++ b/src/toktrim/transforms.py
@@ -11,6 +11,7 @@ def apply_transform(
     mode: TransformMode,
     options: TransformOptions,
 ) -> TransformResult:
+    """Run the mode-appropriate processor and wrap the result with before/after stats."""
     original_text = text
 
     if mode == TransformMode.RAW:
