Skip to content

docs: rewrite Rust codebase spec#67

Merged
BunsDev merged 1 commit into
mainfrom
codex/issue-58-rust-spec
Jun 11, 2026
Merged

docs: rewrite Rust codebase spec#67
BunsDev merged 1 commit into
mainfrom
codex/issue-58-rust-spec

Conversation

@BunsDev

@BunsDev BunsDev commented Jun 11, 2026

Copy link
Copy Markdown
Member

Summary

  • replace the stale spec/13_rust_codebase.md upstream/placeholder Rust spec with the current Coven Code src-rust workspace map
  • document the real coven-code binary, claurst-* crate list, provider/tool/runtime surfaces, and ~/.coven-code persistence
  • update spec/INDEX.md so Security: agent tool-filter fails open for unknown access strings #13 is no longer marked pending and Rust crate/source counts match the current workspace

Verification

  • stale identifier smoke check: no claude-code-rust, cc-*, ~/.claude, or CLAUDE.md references remain in spec/13_rust_codebase.md / spec/INDEX.md
  • current identifier smoke check: src-rust, claurst-*, coven-code, ~/.coven-code, AGENTS.md, 0.0.24, and 12-crate references are present
  • git diff --check

Fixes #58

Copilot AI review requested due to automatic review settings June 11, 2026 03:37
@vercel

vercel Bot commented Jun 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview Jun 11, 2026 3:37am

@BunsDev BunsDev merged commit b7a4a63 into main Jun 11, 2026
2 checks passed
@BunsDev BunsDev deleted the codex/issue-58-rust-spec branch June 11, 2026 03:37

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refreshes the Rust architecture/spec documentation to match the actual src-rust/ Coven Code workspace, replacing the previous upstream/placeholder Rust rewrite spec and updating the spec index accordingly.

Changes:

  • Rewrites spec/13_rust_codebase.md to document the real src-rust workspace layout, crates, CLI surface, tools runtime, persistence, and related subsystems.
  • Updates spec/INDEX.md to mark the Rust spec as current (not pending) and to align the Rust crate/source counts and section pointers with the rewritten spec.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
spec/INDEX.md Updates the spec index entry and quick-lookup pointers for the rewritten Rust workspace spec.
spec/13_rust_codebase.md Replaces stale upstream Rust-rewrite content with a current src-rust workspace reference (crates, CLI, tools/runtime, persistence).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread spec/13_rust_codebase.md
Comment on lines +237 to +238
Bash
Read
Comment thread spec/13_rust_codebase.md
Comment on lines +102 to +110
## Workspace Dependencies

### Tool: `EnterPlanModeTool` (`enter_plan_mode.rs`)
The workspace root centralizes common dependencies:

**Name:** `"EnterPlanMode"`
**Permission level:** `None`

Returns `ToolResult::success` with metadata `{ type: "enter_plan_mode" }`. Signals the session to switch to Plan permission mode.

### Tool: `ExitPlanModeTool` (`exit_plan_mode.rs`)

**Name:** `"ExitPlanMode"`
**Permission level:** `None`

Input schema: `{ summary: optional string }`

Returns success with metadata `{ type: "exit_plan_mode", summary }`. Signals return from Plan mode.

### Tool: `PowerShellTool` (`powershell.rs`)

**Name:** `"PowerShell"`
**Permission level:** `Execute`

Input schema: `{ command: string, timeout: optional u64 }`

Same execution pattern as `BashTool`. On Windows uses `powershell -NoProfile -NonInteractive -Command`. On other platforms uses `pwsh`.

### Tool: `EnterWorktreeTool`, `ExitWorktreeTool` (`worktree.rs`)

Global: `WORKTREE_SESSION: Lazy<Arc<RwLock<Option<WorktreeSession>>>>`

**`WorktreeSession`:** `{ branch: String, path: PathBuf, original_dir: PathBuf }`

**`EnterWorktreeTool`** (`"EnterWorktree"`):
- Input: `{ branch: string, path: optional string }`
- Runs `git worktree add -b <branch> <path>`
- Saves session to `WORKTREE_SESSION`

**`ExitWorktreeTool`** (`"ExitWorktree"`):
- Input: `{ action: "keep" | "remove", discard_changes: optional bool }`
- `keep`: locks worktree, clears session
- `remove`: checks for uncommitted changes (requires `discard_changes=true` to override), runs `git worktree remove --force <path>`, then `git branch -D <branch>`

### Tool: `SendMessageTool` (`send_message.rs`)

**Name:** `"SendMessage"`
**Permission level:** `None`

Global: `INBOX: Lazy<DashMap<String, Vec<AgentMessage>>>`

Input schema: `{ to: string, message: string, metadata: optional Value }`

- Delivers message to named recipient in `INBOX`
- `to = "*"` broadcasts to all existing keys
- `drain_inbox(recipient: &str) -> Vec<AgentMessage>` — removes and returns all messages
- `peek_inbox(recipient: &str) -> Vec<AgentMessage>` — returns without removing

### Tool: `SkillTool` (`skill_tool.rs`)

**Name:** `"Skill"`
**Permission level:** `None`

Input schema: `{ skill: string, arguments: optional string }`

**Algorithm:**
1. `skill = "list"` → enumerates `.claude/commands/*.md` and `~/.claude/commands/*.md`, extracts description from YAML frontmatter or first heading
2. Otherwise: resolves `<skill>.md` file from project then user commands directory
3. Strips YAML frontmatter (`---` block)
4. Substitutes `$ARGUMENTS` with provided arguments string
5. Returns file content as `ToolResult::success`

### Tool: `SleepTool` (`sleep.rs`)

**Name:** `"Sleep"`
**Permission level:** `None`

Input schema: `{ duration: f64 (seconds) }`

Calls `tokio::time::sleep(Duration::from_secs_f64(duration))`. Maximum 300 seconds.

### Tool: `ToolSearchTool` (`tool_search.rs`)

**Name:** `"ToolSearch"`
**Permission level:** `None`

Input schema: `{ query: string, max_results: optional u32 (default 5) }`

Static `TOOL_CATALOG: &[(&str, &str, &[&str])]` — 32 entries of `(name, description, keywords)`.

**Scoring algorithm:**
- `select:Name` syntax → score 100 for exact name match
- Otherwise for each catalog entry:
- exact name match: +20
- name contains query: +10
- description contains query: +5
- keyword exact match: +8
- keyword contains query: +3
- Returns top `max_results` entries with non-zero score

### Tool: `BriefTool` (`brief.rs`)

**Name:** `"Brief"`
**Permission level:** `None`

Input schema: `{ message: string, status: optional string, attachments: optional Array<string> (file paths) }`

Resolves attachment metadata (file size, is_image flag from extension). Returns `ToolResult::success("")` with metadata `{ message, status, sentAt, attachments: [{ path, size, isImage }] }`.

### Tool: `ConfigTool` (`config_tool.rs`)

**Name:** `"Config"`
**Permission level:** `None`

Input schema: `{ action: "get" | "set", key: string, value: optional Value }`

Reads/writes `~/.claude/settings.json`. Supported keys: `model`, `max_tokens`, `verbose`, `permission_mode`, `auto_compact`. Returns current value on `get`, writes and confirms on `set`.

### Tool: `ListMcpResourcesTool`, `ReadMcpResourceTool` (`mcp_resources.rs`)

| Tool | Name | Description |
|---|---|---|
| `ListMcpResourcesTool` | `"ListMcpResources"` | Calls `ctx.mcp_manager.list_all_resources()`, returns JSON |
| `ReadMcpResourceTool` | `"ReadMcpResource"` | Input: `{ uri: string }`. Calls `ctx.mcp_manager.read_resource(uri)` |

Both return error if `ctx.mcp_manager` is `None`.

### Relationship to TypeScript

`cc-tools` corresponds to the TypeScript tool implementations in `src/` (e.g., bash is in the tool system, file operations in ReadTool/EditTool/WriteTool, etc.). Tool names are identical to the TypeScript constants. The `ToolContext` mirrors the TypeScript `ToolUseContext`.

---

## Crate: `cc-query`

**Path:** `crates/query/src/`

The core agentic query loop crate. Contains 4 source files.

### Module: `lib.rs` — Main Query Loop

**`QueryOutcome` enum:**
- `EndTurn { message: Message, usage: UsageInfo }` — model issued `end_turn`
- `MaxTokens { partial_message: Message, usage: UsageInfo }` — hit token limit
- `Cancelled` — cancellation token fired
- `Error(ClaudeError)` — unrecoverable error

**`QueryConfig` struct:**
- `model: String`
- `max_tokens: u32`
- `max_turns: u32` (default: `MAX_TURNS_DEFAULT = 10`)
- `system_prompt: Option<String>`
- `append_system_prompt: Option<String>`
- `thinking_budget: Option<u32>`
- `temperature: Option<f32>`
- `QueryConfig::default()` uses `DEFAULT_MODEL` + `DEFAULT_MAX_TOKENS`
- `QueryConfig::from_config(cfg: &Config)` — reads model + max_tokens from Config

**`QueryEvent` enum:**
- `Stream(StreamEvent)` — raw API stream event
- `ToolStart { tool_name, tool_id }` — tool beginning execution
- `ToolEnd { tool_name, tool_id, result, is_error }` — tool completed
- `TurnComplete { turn: u32, stop_reason: String }` — model turn finished
- `Status(String)` — informational message
- `Error(String)` — error notification

**`run_query_loop(client, messages, tools, tool_ctx, config, cost_tracker, event_tx, cancel_token) -> QueryOutcome`** (async):

Main agentic loop:
1. Increments turn counter; returns `EndTurn` if `> max_turns`
2. Checks `cancel_token.is_cancelled()` → `Cancelled`
3. Converts `messages` → `Vec<ApiMessage>`, tools → `Vec<ApiToolDefinition>`
4. Calls `build_system_prompt(config)` to construct `SystemPrompt`
5. Builds `CreateMessageRequest` (with thinking config if `budget` provided)
6. Creates `ChannelStreamHandler` or `NullStreamHandler`
7. Calls `client.create_message_stream()`, receives `mpsc::Receiver<StreamEvent>`
8. Inner loop: `tokio::select!` on cancellation or stream events; feeds `StreamAccumulator`
9. On `MessageStop` or channel close: calls `accumulator.finish()`
10. Tracks costs via `cost_tracker.add_usage()`
11. Appends assistant message to `messages`
12. Calls `auto_compact_if_needed()` if stop reason is `end_turn` or `tool_use`
13. Dispatches on `stop_reason`:
- `"end_turn"` / `"stop_sequence"` / unknown → fires `Stop` hook → returns `EndTurn`
- `"max_tokens"` → returns `MaxTokens`
- `"tool_use"` → executes all tool_use blocks (see below), appends results, `continue`

**Tool execution in `tool_use` turn:**
1. For each `ContentBlock::ToolUse { id, name, input }`:
2. Emits `QueryEvent::ToolStart`
3. Fires `PreToolUse` hooks via `cc_core::hooks::run_hooks()`; if `HookOutcome::Blocked` → `ToolResult::error("Blocked by hook: ...")`
4. Otherwise calls `execute_tool(name, input, tools, ctx)`
5. Fires `PostToolUse` hooks
6. Emits `QueryEvent::ToolEnd`
7. Pushes `ContentBlock::ToolResult` to result_blocks
8. Appends `Message::user_blocks(result_blocks)` to conversation

**`execute_tool(name, input, tools, ctx) -> ToolResult`** (async):
- Finds tool by name in slice, calls `tool.execute(input, ctx)`
- Unknown tool → `ToolResult::error("Unknown tool: {name}")`

**`build_system_prompt(config) -> SystemPrompt`:**
- Joins `system_prompt` and `append_system_prompt` with `\n\n`
- Empty → default `"You are Claude, an AI assistant by Anthropic."`

**`run_single_query(client, messages, config) -> Result<Message>`** (async):
- Single API call, no tool loop, `NullStreamHandler`
- Returns complete assistant message

**`ChannelStreamHandler`** — implements `StreamHandler`:
- `on_event(&self, event)` forwards to `mpsc::UnboundedSender<QueryEvent>`

### Module: `compact.rs` — Auto-Compact

**Constants:**
- `AUTOCOMPACT_BUFFER_TOKENS = 13_000`
- `WARNING_THRESHOLD_BUFFER_TOKENS = 20_000`
- `AUTOCOMPACT_TRIGGER_FRACTION = 0.90`
- `KEEP_RECENT_MESSAGES = 10`
- `MAX_CONSECUTIVE_FAILURES = 3`

**`AutoCompactState` struct:**
- `compaction_count: u32`
- `consecutive_failures: u32`
- `disabled: bool` — circuit breaker; set after 3 consecutive failures

**`TokenWarningState` enum:** `Ok`, `Warning`, `Critical`

**`context_window_for_model(model: &str) -> u32`:**
- `200_000` for models matching "opus-4", "sonnet-4", "haiku-4", "claude-3-5"
- `100_000` otherwise

**`calculate_token_warning_state(input_tokens, model) -> TokenWarningState`:**
- Uses `WARNING_THRESHOLD_BUFFER_TOKENS` to determine Warning vs Critical

**`should_auto_compact(state, input_tokens, model) -> bool`:**
- Returns false if `state.disabled`
- Returns true if `input_tokens / context_window > AUTOCOMPACT_TRIGGER_FRACTION`

**`summarise_head(client, messages_to_summarize, model) -> Result<String>`** (async):
- Calls API with prompt asking to summarize the provided conversation
- Returns summary wrapped in `<compact-summary>...</compact-summary>` XML tags

**`compact_conversation(client, messages, model) -> Result<Vec<Message>>`** (async):
- Splits conversation: head = `messages[0..total-KEEP_RECENT_MESSAGES]`, tail = last 10 messages
- Calls `summarise_head()` on head
- Returns `[Message::user(summary)] + tail`

**`auto_compact_if_needed(client, messages, input_tokens, model, state) -> Option<Vec<Message>>`** (async):
- Checks `should_auto_compact()`, calls `compact_conversation()`
- On success: resets `consecutive_failures`, increments `compaction_count`
- On failure: increments `consecutive_failures`; disables if `>= MAX_CONSECUTIVE_FAILURES`
- Returns `Some(new_messages)` on success, `None` if not needed or failed

### Module: `agent_tool.rs` — Sub-Agent Tool

**`AgentTool`** implements `Tool`:
- **Name:** `"Task"` (constant `TOOL_NAME_AGENT`)
- **Permission level:** `Execute`

Input schema: `{ description: string, prompt: string, tools: optional Array<string>, system_prompt: optional string, max_turns: optional u32, model: optional string }`

**Algorithm:**
1. Creates dedicated `AnthropicClient` from `ANTHROPIC_API_KEY` env var
2. Filters tool list: if `tools` field provided, uses that subset; always excludes `TOOL_NAME_AGENT` (prevents recursion)
3. Calls `run_query_loop()` with:
- `event_tx = None` (no TUI forwarding for sub-agent)
- New `ToolContext` with same working_dir, permission_mode, etc.
4. Returns final assistant message text as `ToolResult::success()`

### Module: `cron_scheduler.rs` — Background Cron

**`start_cron_scheduler(tools, tool_ctx, cancel_token) -> JoinHandle<()>`:**
- Spawns `tokio::spawn(run_scheduler_loop(...))`

**`run_scheduler_loop(tools, tool_ctx, cancel_token)`** (async loop):
1. Computes seconds until next minute boundary: `sleep(60 - now.second() + 1)`
2. Calls `cc_tools::cron::pop_due_tasks()` to get matching tasks
3. For each due task: spawns `run_query_loop()` with:
- Single user message from `task.prompt`
- `event_tx = None` (background, no UI)
- `cancel_token` clone
4. Loop continues until cancellation

### Relationship to TypeScript

`cc-query` corresponds to `src/query.ts`, `src/query/`, `src/services/compact/autoCompact.ts`, `src/coordinator/`, and parts of `src/services/autoDream/`. The `AgentTool` corresponds to the TypeScript `Task` tool.
| Area | Dependencies |
|---|---|
| Async/runtime | `tokio`, `tokio-stream`, `futures`, `async-trait`, `async-stream` |
| HTTP/streaming | `reqwest`, `tokio-tungstenite`, `tower-http`, `sse-stream` |
| Serialization/config | `serde`, `serde_json`, `toml`, `schemars` |
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rewrite spec/13_rust_codebase.md — describes a codebase that doesn't exist

2 participants