Backlog source: docs/bugs.md
The backlog context and implementation plan below were moved out of docs/bugs.md.
Severity: High — the single biggest coordination gap in a multi-agent workflow.
A developer paired with an agent has immense leverage: one person can drive multiple agents across different parts of a codebase simultaneously. But that leverage collapses without coordination visibility. Today the user has to hold the full territorial picture in their head — which agent is working where, which files are in flight, whether two sessions are about to collide. That mental bookkeeping scales badly and is the first thing to break under load.
File edits are buried inside individual conversation streams. There is no cross-session view that answers “which agent last touched this file?”, “what is each agent working on right now?”, or “are two sessions about to collide on the same module?” The developer is the sole coordination layer, and the tool gives them nothing to coordinate with.
Why this matters more than most features:
Desired behavior:
Data sources:
filePath and changeType per agent turnTasks:
/api/territory endpoint that returns the aggregated activity mapThis is the concrete delivery plan for the territory visualization feature.
The developer paired with agents is the coordination layer. TermAl’s value scales with how many agents the developer can run concurrently, but that only works if the tool gives them a live picture of who is changing what. Territory visualization is not a dashboard — it is the coordination surface.
Every time an agent reads, writes, creates, or deletes a file, the backend records a touch:
struct Touch {
file_path: String,
session_id: String,
agent: Agent, // Claude, Codex, Gemini
action: TouchAction, // Read, Write, Create, Delete
lines_added: u32,
lines_removed: u32,
message_id: String, // for click-through to conversation
timestamp: DateTime<Utc>,
}
enum TouchAction {
Read,
Write,
Create,
Delete,
}
Sources:
DiffMessage on turn completion → Write / Create / Delete with line counts from the diffRead / WriteThe territory index maintains a rolled-up summary per file:
struct FileTerritory {
file_path: String,
touches: Vec<Touch>, // append-only log
dominant_agent: Option<Agent>,
agents_involved: HashSet<Agent>,
sessions_involved: HashSet<String>,
contested: bool, // true if multiple agents have written
total_writes: u32,
total_lines_changed: u32,
last_write: DateTime<Utc>,
last_read: DateTime<Utc>,
external_change_detected: bool,
}
Rules:
dominant_agent = the agent with the most total lines_changed (writes only, reads don’t count
for dominance)contested = true when two or more distinct agents have at least one Write / Create / Delete
on the same fileexternal_change_detected = true when the git poll finds changes not attributable to any sessionAggregate file-level data upward into directories so the tree view can show territory at any depth:
struct DirectoryTerritory {
dir_path: String,
dominant_agent: Option<Agent>,
contested: bool,
file_count: u32, // files with any touches under this dir
contested_file_count: u32,
agents_involved: HashSet<Agent>,
}
This is computed on demand from the file index, not stored separately.
The territory map is only useful if it is honest. Agent-tracked touches cover TermAl activity, but the developer also edits files in their editor, runs scripts, pulls from remote, etc. A periodic git poll fills that gap.
A background task runs on a configurable interval (default: 5 seconds):
git status --porcelain to get the list of modified, added, and deleted files in the
working tree.external_change_detected and
record an External touch with no session or agent attribution.git status output → clear the
external flag (the change was committed or reverted).A separate, less frequent background task (default: 60 seconds, configurable):
git fetch --quiet to update remote tracking refs.git rev-list --count HEAD..@{upstream} to check if upstream has new commits.git diff --name-only HEAD...@{upstream} to get the
list of files that would change on pull.upstream territory entries — files the remote has changed that the developer
hasn’t pulled yet.This does NOT auto-pull. It just makes the territory map aware that the ground has shifted.
Contested files are the highest-signal output of the territory system. The backend should proactively detect and categorize conflicts:
Level 1 — File-level contest: Two or more agents have written to the same file. Low urgency; this is information, not necessarily a problem.
Level 2 — Active collision: Two sessions with active (running) turns are both writing to the same file right now. Higher urgency; one of them is likely about to create a merge conflict.
Level 3 — External desync: An agent wrote a file, and then an external change was detected on the same file before the agent’s changes were committed. The agent’s mental model of that file is now stale.
Each conflict level should surface differently in the UI (color intensity, icon, notification).
GET /api/territory
Returns the full territory map:
{
"files": [
{
"filePath": "src/main.rs",
"dominantAgent": "Claude",
"agentsInvolved": ["Claude", "Codex"],
"sessionsInvolved": ["session-1", "session-4"],
"contested": true,
"totalWrites": 12,
"totalLinesChanged": 347,
"lastWrite": "2026-03-10T14:22:00Z",
"lastRead": "2026-03-10T14:25:00Z",
"externalChangeDetected": false,
"conflictLevel": 1
}
],
"conflicts": [
{
"filePath": "src/main.rs",
"level": 1,
"agents": ["Claude", "Codex"],
"sessions": ["session-1", "session-4"],
"description": "Both Claude and Codex have written to this file"
}
],
"summary": {
"totalTrackedFiles": 23,
"byAgent": {
"Claude": { "files": 8, "linesChanged": 412 },
"Codex": { "files": 17, "linesChanged": 891 }
},
"contestedFiles": 2,
"externalChanges": 1,
"activeConflicts": 0
},
"gitStatus": {
"upstreamBehind": 3,
"upstreamFiles": ["README.md", "Cargo.toml", "src/lib.rs"]
}
}
GET /api/territory/{filePath}
Returns the full touch log for one file, including the click-through messageId for each touch.
Useful for the detail drill-down.
Territory updates should piggyback on the existing /api/events SSE stream. When the territory
index changes (new touch, conflict detected, external change found), include a territory delta in
the next SSE snapshot so the frontend stays live without polling.
A new workspace tab type:
type WorkspaceTab =
| // ... existing types
| { id: string; kind: "territory" };
The tab renders a collapsible project tree with:
Always visible across all tabs (in the status area or header):
Claude: 8 files (412 lines) · Codex: 17 files (891 lines) · 2 contested · 1 external
Clicking the summary bar opens the territory tab. Conflict counts should pulse or highlight when a new conflict is detected.
A toggle in the territory tab that reranks the tree by activity intensity instead of alphabetical path order. Files with the most cross-agent churn float to the top. Useful for spotting hotspots when the project tree is large.
When a Level 2 (active collision) or Level 3 (external desync) conflict is detected, surface a non-blocking toast notification so the developer sees it even if they are not looking at the territory tab.
Touch and FileTerritory structs to backend state.dispatch_turn() completion to record touches from DiffMessage events.GET /api/territory returning the snapshot.territory as a WorkspaceTab kind.Backend:
git status outputFrontend:
Integration: