Add siGit Code parity roadmap: the three biggest wins

Records where siGit Code stands against Claude Code and specifies the three features that close the most important gaps: background commands, a subagent tool, and durable sessions with context compaction. Each spec is scoped to its own branch off development in the sigit repo, with design notes and acceptance criteria detailed enough to implement from.

Seto Elkahfi committed Jul 4, 2026 at 22:04 UTC 1710db46eb52b34b0824ed6149a9a170aa928991
1 file changed +172
docs/product/sigit-code-parity-roadmap.md new
+172
@@ -0,0 +1,172 @@
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.