| 1 | # siGit Code parity roadmap: the three biggest wins |
| 2 | |
| 3 | Status: draft v1 (2026-07-04). Owner: product/eng. Audience: internal (private repo). |
| 4 | Scope: **siGit Code**, the Rust CLI / ACP agent (see [product-overview.md](product-overview.md)). |
| 5 | This document records where siGit Code stands against Claude Code and specifies the |
| 6 | three features that close the most important gaps. Each spec below is written to be |
| 7 | implementable on its own branch off `development` in the `sigit` repo; the three are |
| 8 | independent of each other. |
| 9 | |
| 10 | --- |
| 11 | |
| 12 | ## 1. Where we stand |
| 13 | |
| 14 | siGit Code already covers the core agent loop: file tools, shell, glob and search, |
| 15 | web fetch, todos, memory, Agent Skills, an MCP client (Streamable HTTP), project |
| 16 | instruction files (AGENTS.md / CLAUDE.md), a tool permission system with plan mode |
| 17 | (PR #20), commit co-author attribution (PR #21), ACP editor integration, a Unix TUI, |
| 18 | and local / cloud / BYO inference. Local inference is our differentiator; Claude |
| 19 | Code has no equivalent. |
| 20 | |
| 21 | What Claude Code has that we lack is not one feature. It is the machinery that lets |
| 22 | an agent work for a long time: |
| 23 | |
| 24 | | Capability | Claude Code | siGit Code today | |
| 25 | |---|---|---| |
| 26 | | Durable sessions (resume after restart) | yes | no, history lives in RAM | |
| 27 | | Context compaction | auto + manual | no, hard cap of 10 tool rounds | |
| 28 | | Subagents (delegate work to a fresh context) | yes | no | |
| 29 | | Background / long-running commands | yes | no, commands are killed at 120s | |
| 30 | | Web search | yes | fetch only | |
| 31 | | Custom slash commands | yes | built-ins only | |
| 32 | | Permission rule patterns (`Bash(git:*)`) | yes | mode + per-tool only | |
| 33 | | stdio MCP transport | yes | HTTP only | |
| 34 | | Headless mode for CI | yes | no | |
| 35 | |
| 36 | The three specs below are the biggest wins, ordered by implementation cost from |
| 37 | smallest to largest. Everything else on the list is a fast follow. |
| 38 | |
| 39 | --- |
| 40 | |
| 41 | ## 2. Spec: background and long-running commands |
| 42 | |
| 43 | **Branch:** `feature/background-commands` · **Touches:** `src/tools.rs` (self-contained) |
| 44 | |
| 45 | ### Problem |
| 46 | |
| 47 | `run_command` kills every child at the 120 second `COMMAND_TIMEOUT`. A cold |
| 48 | `cargo build`, a full test suite, a dev server, or a watch task cannot run at all. |
| 49 | This is the most visceral failure in daily use: the agent cannot even build the |
| 50 | projects it is asked to work on. |
| 51 | |
| 52 | ### Design |
| 53 | |
| 54 | - `run_command` gains an optional boolean argument `run_in_background`. When true, |
| 55 | the child is spawned, registered in a process-global task table |
| 56 | (`Mutex<HashMap<u64, BackgroundTask>>`, monotonically increasing ids, same |
| 57 | process-global pattern as `mcp.rs`), and the tool returns immediately with the |
| 58 | task id. A reader thread drains stdout/stderr into a capped buffer |
| 59 | (`COMMAND_OUTPUT_LIMIT` per task, oldest output dropped first). |
| 60 | - New tool `command_output` `{task_id}`: returns output accumulated since the last |
| 61 | poll plus the task status (running / exited with code). |
| 62 | - New tool `kill_command` `{task_id}`: terminates the child (SIGKILL on Unix, |
| 63 | `TerminateProcess` semantics via `Child::kill` everywhere) and reports the tail |
| 64 | of its output. |
| 65 | - Foreground behavior is unchanged, including the 120s timeout. Background tasks |
| 66 | have no timeout; all children die with the sigit process (document this). |
| 67 | - Tool descriptions must steer the model: run servers, builds, and anything that |
| 68 | may exceed two minutes in the background, then poll. |
| 69 | - When the permission system (PR #20) merges: `command_output` is read-only, |
| 70 | `kill_command` is mutating but only affects children the agent itself started. |
| 71 | |
| 72 | ### Acceptance |
| 73 | |
| 74 | - A background task outliving the 120s foreground timeout keeps running and its |
| 75 | output is retrievable via `command_output`. |
| 76 | - `kill_command` stops a running task; polling a finished task reports its exit code. |
| 77 | - Unit tests cover: spawn + poll + finish, kill, unknown task id, output capping. |
| 78 | |
| 79 | --- |
| 80 | |
| 81 | ## 3. Spec: subagent tool |
| 82 | |
| 83 | **Branch:** `feature/subagent-tool` · **Touches:** `src/tools.rs`, `src/backend.rs`, both surfaces' startup |
| 84 | |
| 85 | ### Problem |
| 86 | |
| 87 | One conversation does everything, so every file read pollutes the main context |
| 88 | forever. With on-device models this is fatal: a 3B model's context fills after a |
| 89 | few searches. Delegating research to a throwaway context keeps the main thread |
| 90 | small. |
| 91 | |
| 92 | ### Design |
| 93 | |
| 94 | - New tool `task` `{description, prompt}`: runs a nested agent loop in a fresh |
| 95 | conversation and returns only its final text answer (capped, e.g. 8k chars). |
| 96 | - The subagent's toolset is read-only: `read_file`, `list_directory`, |
| 97 | `search_files`, `glob`, `read_website`. No `task` (no recursion), no mutating |
| 98 | tools, so no permission prompts fire mid-subagent. |
| 99 | - Architecture: `tools.rs` is backend-agnostic, so it cannot construct a backend. |
| 100 | At startup each surface registers a subagent factory in a process-global |
| 101 | (`OnceLock<Box<dyn Fn() -> Option<Arc<dyn InferenceBackend>>>>`): for |
| 102 | `OpenAiBackend` the factory builds a fresh backend with the same base_url / key / |
| 103 | model and a short subagent system prompt; for `LocalBackend` the factory returns |
| 104 | `None` for now (onde has a single shared history; a second concurrent context |
| 105 | needs onde support first) and the tool returns a clear "not available on-device |
| 106 | yet" result the model can react to. |
| 107 | - The nested loop reuses `send_message_with_tools` / `send_tool_results` and |
| 108 | `execute_tool`, with its own round cap (8). |
| 109 | - The `task` tool is only offered when the factory reports availability, the same |
| 110 | conditional-spec pattern the `skill` tool uses. |
| 111 | |
| 112 | ### Acceptance |
| 113 | |
| 114 | - On an OpenAI-compatible backend, `task` completes a research prompt using only |
| 115 | read-only tools and returns a text answer; the main conversation history gains |
| 116 | one tool result, not the subagent transcript. |
| 117 | - On-device, the tool either is not offered or returns the documented fallback. |
| 118 | - Unit tests cover the loop against a scripted backend (the |
| 119 | `tests/acp_permissions.rs` fake-endpoint harness pattern) and the toolset |
| 120 | restriction. |
| 121 | |
| 122 | --- |
| 123 | |
| 124 | ## 4. Spec: durable sessions and context compaction |
| 125 | |
| 126 | **Branch:** `feature/session-persistence-compaction` · **Touches:** `src/backend.rs`, `src/main.rs`, `src/chat.rs`, new `src/session_store.rs` |
| 127 | |
| 128 | ### Problem |
| 129 | |
| 130 | History lives in RAM (`OpenAiBackend`'s message vec, onde's engine state). A |
| 131 | restart loses everything, ACP `session/load` cannot actually restore, and the hard |
| 132 | `MAX_TOOL_ROUNDS = 10` cap is the only defense against blowing the context window. |
| 133 | This is the single hardest ceiling on task size, and it hurts most on-device where |
| 134 | context windows are smallest. |
| 135 | |
| 136 | ### Design |
| 137 | |
| 138 | - **Persistence.** `InferenceBackend` gains `history_snapshot() -> Vec<serde_json::Value>` |
| 139 | and `restore_history(Vec<serde_json::Value>)`. `OpenAiBackend` serializes its |
| 140 | message vec as-is. `LocalBackend` flattens through onde's `history()` / |
| 141 | `push_history` (tool entries flatten to text in the MVP; acceptable loss). |
| 142 | A new `session_store` module writes one JSONL file per session under |
| 143 | `$SIGIT_CONFIG_DIR/sessions/<session-id>.jsonl` after every completed turn, and |
| 144 | loads it on demand. |
| 145 | - **Resume.** ACP `session/load` restores from the store when a file for that |
| 146 | session id exists. The TUI gets `/resume` to reload the most recent session. |
| 147 | `/clear` deletes the file along with the in-memory history. |
| 148 | - **Compaction.** A `compact_history()` path: estimate tokens (chars / 4), and when |
| 149 | the estimate crosses a per-model budget (default 24k tokens), ask the backend to |
| 150 | summarize the conversation, then rebuild history as system prompt + summary + |
| 151 | the last few turns. Exposed as `/compact`, and run automatically between tool |
| 152 | rounds when over budget. With compaction in place, raise `MAX_TOOL_ROUNDS` from |
| 153 | 10 to 24. |
| 154 | |
| 155 | ### Acceptance |
| 156 | |
| 157 | - Kill sigit mid-conversation, restart, `session/load` (or `/resume`): the model |
| 158 | answers a follow-up that requires earlier context. |
| 159 | - A conversation pushed past the token budget compacts instead of erroring, and |
| 160 | the model still answers questions whose answers survived in the summary. |
| 161 | - Unit tests cover snapshot/restore round-trips for both backends, store |
| 162 | read/write, threshold triggering, and post-compaction history shape. |
| 163 | |
| 164 | --- |
| 165 | |
| 166 | ## 5. Sequencing |
| 167 | |
| 168 | All three branch off `development` independently. Suggested land order: background |
| 169 | commands (smallest, immediately felt), subagent tool, then sessions and compaction |
| 170 | (largest). Fast follows after these: permission rule patterns on top of PR #20, |
| 171 | stdio MCP transport, web search, and a headless `sigit -p` mode for CI, which is |
| 172 | also what the [Cloud Agent](sigit-code-cloud-agent-plan.md) sandbox runner needs. |