Skip to content

Commit ebc6b9b

Browse files
committed
[Spec 0602] Update spec for v3.0.0 changes
- Rename all af references to afx (Spec 647) - Update version from v1.x to v3.x - Add .codev/config.json layered config system - Add forge-agnostic language (GitHub/GitLab/Gitea support) - Add afx spawn --branch support to Spawn Builder command - Add author attribution (@username) to TreeView items (Spec 637) - Add EscapeBuffer for incomplete ANSI sequences across WS frames (Bugfix #630) - Add resize deferral during replay (Bugfix #625) - Document workspace-scoped vs global API routes in Connection Manager - Add SSE heartbeat handling (Bugfix #580) - Expand references with Specs 618, 627, 637, 647
1 parent 409195b commit ebc6b9b

1 file changed

Lines changed: 66 additions & 30 deletions

File tree

codev/specs/0602-vscode-extension.md

Lines changed: 66 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
- **Status**: draft
66
- **Created**: 2026-03-11
77
- **Protocol**: SPIR
8-
- **Supersedes**: Spec 0066 (VSCode Companion Extension — outdated, pre-shellper architecture)
8+
- **Related**: Spec 0066
99

1010
## Clarifying Questions Asked
1111

@@ -16,7 +16,7 @@ A1: No. The extension coexists with the browser dashboard. Users choose their pr
1616
A2: Yes. VS Code's `createTerminal({ pty })` with a custom `Pseudoterminal` can proxy I/O over WebSocket to Tower's PTY sessions. Unlike the old spec (0066), tmux is no longer in the stack — shellper handles persistence natively.
1717

1818
**Q3: How does architect-builder communication work?**
19-
A3: `af send` posts to `POST /api/send` on Tower. Tower resolves the target builder's PTY session, checks idle state (3s threshold), formats the message with `### [ARCHITECT INSTRUCTION | timestamp] ###` framing, and writes it to the PTY via shellper. The VS Code extension doesn't change this — it's a different viewport onto the same Tower infrastructure.
19+
A3: `afx send` posts to `POST /api/send` on Tower. Tower resolves the target builder's PTY session, checks idle state (3s threshold), formats the message with `### [ARCHITECT INSTRUCTION | timestamp] ###` framing, and writes it to the PTY via shellper. The VS Code extension doesn't change this — it's a different viewport onto the same Tower infrastructure.
2020

2121
**Q4: What about the old spec 0066?**
2222
A4: Spec 0066 was written 2026-01-12 against the old tmux/ttyd architecture. The stack has since moved to shellper + node-pty + custom WebSocket protocol. This spec starts fresh with the current architecture.
@@ -32,23 +32,29 @@ Codev's Agent Farm operates through a browser-based dashboard served by the Towe
3232

3333
## Current State
3434

35-
**Tower Architecture (v1.x, March 2026):**
35+
**Tower Architecture (v3.x, April 2026):**
3636
- Node.js HTTP/WebSocket server on localhost:4100
3737
- Shellper daemon for PTY persistence (survives Tower restarts)
3838
- React 19 + xterm.js 5.5 dashboard served at `/`
3939
- SQLite state (local state.db per workspace + global.db system-wide)
4040
- Binary WebSocket protocol: `0x00` = control frames (JSON), `0x01` = data frames (raw PTY bytes)
41-
- SSE at `/api/events` for real-time push notifications
42-
- REST API: 30+ HTTP endpoints for state, terminals, overview, analytics, file browsing
43-
- `af send` for architect↔builder messaging via `POST /api/send`
41+
- SSE at `/api/events` for real-time push notifications (30s heartbeat)
42+
- REST API: 30+ HTTP endpoints — global routes (`/api/overview`, `/api/send`) and workspace-scoped routes (`/workspace/:base64path/api/state`, `/workspace/:base64path/api/team`)
43+
- `afx send` for architect↔builder messaging via `POST /api/send`
4444
- Send buffer with typing-aware delivery (3s idle threshold, 60s max buffer age)
45+
- Layered config system: `.codev/config.json` (project) → `~/.codev/config.json` (global) → framework defaults
46+
- Forge abstraction layer: provider-agnostic issue/PR operations (GitHub, GitLab, Gitea) via concept commands
47+
- `afx spawn --branch` for continuing work on existing PR branches
4548

4649
**What works well:**
4750
- Shellper persistence — terminals survive Tower restarts and browser refreshes
4851
- Binary WebSocket protocol — efficient, supports resize/reconnect/replay via sequence numbers
4952
- Ring buffer — 1000-line scrollback with sequence-number-based resume
50-
- Overview API — consolidated view of builders, PRs, backlog
51-
- SSE — real-time push for state changes
53+
- EscapeBuffer — buffers incomplete ANSI escape sequences split across WebSocket frames (Bugfix #630)
54+
- ScrollController — unified scroll state machine with phase-aware resize deferral (Spec 627, Bugfix #625)
55+
- Overview API — consolidated view of builders, PRs, backlog (with author attribution, Spec 637)
56+
- Forge system — provider-agnostic issue/PR data; extension never needs to know which forge is configured
57+
- SSE — real-time push for state changes (30s heartbeat, Bugfix #580)
5258

5359
**What doesn't translate to VS Code:**
5460
- xterm.js in-browser rendering (replaced by VS Code's native terminal)
@@ -62,9 +68,9 @@ A VS Code extension (`codev-vscode`) that provides:
6268
1. **Native terminals** — Architect and builder PTY sessions rendered in VS Code's terminal panel, connected to Tower via WebSocket
6369
2. **Work View sidebar** — TreeView showing builders (status, phase, blocked gates), PRs, and backlog
6470
3. **Status bar** — Builder count, active phase, blocked gate notifications
65-
4. **Command Palette** — All `af` and `porch` commands accessible without leaving the IDE
66-
5. **Native file opening**`af open file.ts:42` opens in VS Code's editor at line 42, not the browser
67-
6. **Message sending**`af send` from Command Palette with builder picker and message input
71+
4. **Command Palette** — All `afx` and `porch` commands accessible without leaving the IDE
72+
5. **Native file opening**`afx open file.ts:42` opens in VS Code's editor at line 42, not the browser
73+
6. **Message sending**`afx send` from Command Palette with builder picker and message input
6874
7. **Review comments** — Native Comments API gutter "+" for adding `REVIEW(@author)` comments, interoperable with browser dashboard annotations
6975
8. **Shell terminals** — Ad-hoc shell sessions via Tower, with shellper persistence (survives restarts)
7076
9. **Needs Attention** — TreeView section + status bar for PRs needing review and builders blocked on approval gates
@@ -86,8 +92,8 @@ Users can use the browser dashboard, the VS Code extension, or both simultaneous
8692
- [ ] Builder terminals open in VS Code terminal panel with correct naming (`Builder #42 [implement]`)
8793
- [ ] Work View TreeView shows builders, PRs, and backlog from `/api/overview`
8894
- [ ] Status bar shows builder count and blocked gate count
89-
- [ ] `af spawn`, `af send`, `af cleanup`, `porch approve` available via Command Palette
90-
- [ ] `af open file.ts:42` opens file in VS Code editor at correct line
95+
- [ ] `afx spawn`, `afx send`, `afx cleanup`, `porch approve` available via Command Palette
96+
- [ ] `afx open file.ts:42` opens file in VS Code editor at correct line
9197
- [ ] Review comments via Comments API gutter "+" — interoperable with browser dashboard annotations
9298
- [ ] Shell terminals created via Command Palette, connected to Tower with shellper persistence
9399
- [ ] Needs Attention section in TreeView shows blocked builders and PRs needing review
@@ -126,13 +132,15 @@ Users can use the browser dashboard, the VS Code extension, or both simultaneous
126132
### Business Constraints
127133
- Must not break existing browser dashboard users
128134
- Minimal additional maintenance burden — share API layer, not UI code
129-
- Extension must work with Tower running independently (`af tower start` still required)
135+
- Extension must work with Tower running independently (`afx tower start` still required)
130136

131137
## Assumptions
132-
- Tower server is running on localhost:4100 (or user-configured port)
138+
- Tower server is running on localhost:4100 (or port from `.codev/config.json`)
133139
- VS Code has access to the same filesystem as Tower
134140
- `~/.agent-farm/local-key` exists for authentication
141+
- `.codev/config.json` exists at project root (v3.0.0+ config system with layered merging)
135142
- Node.js available in extension host environment (standard for VS Code extensions)
143+
- Forge provider is configured and operational (extension is provider-agnostic)
136144

137145
## Solution Approaches
138146

@@ -148,7 +156,7 @@ Users can use the browser dashboard, the VS Code extension, or both simultaneous
148156
│ ┌──────────────┐ ┌──────────────┐ ┌───────────────┐ │
149157
│ │ Command │ │ Status Bar │ │ Work View │ │
150158
│ │ Palette │ │ (builders, │ │ TreeView │ │
151-
│ │ (af/porch) │ │ gates) │ │ (sidebar) │ │
159+
│ │ (afx/porch) │ │ gates) │ │ (sidebar) │ │
152160
│ └──────────────┘ └──────────────┘ └───────────────┘ │
153161
│ │
154162
│ ┌──────────────────────────┐ ┌──────────────────────┐ │
@@ -215,12 +223,20 @@ Users can use the browser dashboard, the VS Code extension, or both simultaneous
215223
Singleton service managing all communication with Tower:
216224

217225
- **State machine**: `DISCONNECTED``CONNECTING``CONNECTED``RECONNECTING`
218-
- **SSE client**: Subscribes to `/api/events`, routes events to TreeView/Status Bar refresh
226+
- **SSE client**: Subscribes to `/api/events`, routes events to TreeView/Status Bar refresh. Handles 30s heartbeat events without triggering state refreshes.
219227
- **REST client**: Authenticated calls to all `/api/*` endpoints
220228
- **WebSocket pool**: One WebSocket per open terminal, managed lifecycle
221229
- **Auth**: Reads `~/.agent-farm/local-key`, sends as `codev-web-key` header (HTTP) or query param (WebSocket)
222230
- **Health check**: Pings `/api/health` on activation and after SSE drops
223231
- **Reconnection**: Exponential backoff (1s → 2s → 4s → 8s → max 30s)
232+
- **Config**: Reads `.codev/config.json` for Tower port override and project-level settings
233+
234+
**Workspace-scoped routing:**
235+
Tower has two route layers. The extension must use the correct one:
236+
- **Global routes** (no prefix): `/api/overview`, `/api/send`, `/api/events`, `/api/health`, `/api/analytics`, `/api/cron/*`, `/api/workspaces`
237+
- **Workspace-scoped routes** (prefixed): `/workspace/:base64urlPath/api/state`, `/workspace/:base64urlPath/api/team`, `/workspace/:base64urlPath/api/tabs/shell`, `/workspace/:base64urlPath/ws/terminal/:id`
238+
239+
The Connection Manager encodes the active workspace path as base64url and prefixes all workspace-scoped requests. The workspace path is determined by matching VS Code's workspace folder against Tower's known workspaces (via `GET /api/workspaces`).
224240

225241
All consumers (TreeView, Status Bar, Terminals, Commands) go through this singleton. When Tower goes offline, a single state change propagates to all UI surfaces.
226242

@@ -246,6 +262,12 @@ Each Tower PTY session maps to a VS Code `Pseudoterminal`:
246262
- On reconnect, send last-seen sequence number for ring buffer replay
247263
- Terminal scrollback is preserved via shellper — no data loss
248264

265+
**Escape sequence buffering:**
266+
WebSocket frames can split ANSI escape sequences mid-sequence (e.g., CSI, OSC, DCS). Writing a partial escape to `onDidWrite` corrupts terminal state (production Bugfix #630). The Pseudoterminal adapter must buffer incomplete trailing sequences and prepend them to the next frame — same logic as `dashboard/src/lib/escapeBuffer.ts`. This should be extracted into `@cluesmith/codev-api-client` as a shared utility.
267+
268+
**Resize deferral during replay:**
269+
On reconnect, the ring buffer replays potentially large scrollback. Sending a resize control frame (`0x00` with `type: 'resize'`) while replay data is being written causes garbled rendering (production Bugfix #625). The adapter must queue resize events and flush them only after the replay write completes.
270+
249271
**Backpressure:**
250272
- Chunk large `onDidWrite` calls (> 16KB) with `setTimeout(0)` between chunks
251273
- Prevents extension host CPU spikes and "Extension causes high CPU" warnings
@@ -269,17 +291,21 @@ CODEV AGENT FARM
269291
│ ├── #43 dashboard-polish [review] ● running
270292
│ └── #44 api-refactor [plan-approval] ⏸ blocked
271293
├── 📋 Pull Requests (2)
272-
│ ├── #187 feat: password hashing (ready)
273-
│ └── #188 fix: dashboard layout (draft)
294+
│ ├── #187 feat: password hashing @alice (ready)
295+
│ └── #188 fix: dashboard layout @bob (draft)
274296
├── 📥 Backlog (5)
275-
│ ├── #190 Add rate limiting
276-
│ ├── #191 Improve error messages
297+
│ ├── #190 Add rate limiting @alice
298+
│ ├── #191 Improve error messages @bob
277299
│ └── ... (2 more)
278300
└── ✓ Recently Closed (3)
279301
├── #185 feat: password reset (merged)
280302
└── ... (2 more)
281303
```
282304

305+
**Author attribution**: Backlog and PR items show `@username` when the forge provides author data (Spec 637). Gracefully omitted when the author field is absent (e.g., some non-GitHub forges).
306+
307+
**Forge-agnostic**: All issue/PR data comes through Tower's overview API, which dispatches to the configured forge provider (GitHub, GitLab, Gitea). The extension never calls `gh` or any forge CLI directly. "Open in Browser" actions use URLs from the API response, not hardcoded GitHub URLs.
308+
283309
**Data source**: `GET /api/overview` (same as browser dashboard Work View)
284310
**Refresh**: On SSE events + manual refresh button
285311
**Actions (context menu):**
@@ -305,7 +331,7 @@ Commands registered under `Codev:` prefix:
305331

306332
| Command | Action |
307333
|---------|--------|
308-
| `Codev: Spawn Builder` | Quick-pick for issue number + protocol → `af spawn` |
334+
| `Codev: Spawn Builder` | Quick-pick for issue number + protocol + optional branch `afx spawn` |
309335
| `Codev: Send Message` | Quick-pick builder → input box for message → `POST /api/send` |
310336
| `Codev: Open Architect Terminal` | Opens/focuses architect terminal |
311337
| `Codev: Open Builder Terminal` | Quick-pick builder → opens terminal |
@@ -314,7 +340,7 @@ Commands registered under `Codev:` prefix:
314340
| `Codev: Refresh Overview` | `POST /api/overview/refresh` |
315341
| `Codev: View Analytics` | Opens analytics Webview panel |
316342
| `Codev: View Team` | Opens team Webview panel (when teamEnabled) |
317-
| `Codev: Cleanup Builder` | Quick-pick builder → `af cleanup` |
343+
| `Codev: Cleanup Builder` | Quick-pick builder → `afx cleanup` |
318344
| `Codev: Builder Status` | Quick-pick builder → shows status in notification |
319345
| `Codev: Connect Tunnel` | Connect cloud tunnel → `POST /api/tunnel/connect` |
320346
| `Codev: Disconnect Tunnel` | Disconnect cloud tunnel → `POST /api/tunnel/disconnect` |
@@ -323,7 +349,7 @@ Commands registered under `Codev:` prefix:
323349

324350
### 6. File Link Handling
325351

326-
**Intercept `af open`**: Register a URI handler so `af open file.ts:42` triggers VS Code to open the file at line 42 using `vscode.workspace.openTextDocument` + `vscode.window.showTextDocument` with `vscode.Selection`.
352+
**Intercept `afx open`**: Register a URI handler so `afx open file.ts:42` triggers VS Code to open the file at line 42 using `vscode.workspace.openTextDocument` + `vscode.window.showTextDocument` with `vscode.Selection`.
327353

328354
**Terminal file path detection**: The browser dashboard uses `FilePathDecorationManager` to make file paths clickable in xterm.js. In VS Code, this is handled natively by the terminal's link provider. Register a `TerminalLinkProvider` that detects file paths and opens them in the editor on click.
329355

@@ -432,8 +458,10 @@ Without this package, the extension becomes a third independent copy of these ty
432458
Environment-agnostic Tower API client shared between dashboard and extension:
433459

434460
- REST client with authenticated fetch (local-key header)
435-
- SSE client with reconnection logic
461+
- SSE client with reconnection logic and heartbeat handling
436462
- WebSocket binary protocol adapter (encode/decode `0x00`/`0x01` frames)
463+
- EscapeBuffer (from `dashboard/src/lib/escapeBuffer.ts`) for incomplete ANSI sequence handling
464+
- Workspace path encoding (base64url) for workspace-scoped routes
437465
- Connection state machine (`DISCONNECTED → CONNECTING → CONNECTED → RECONNECTING`)
438466
- Must accept a custom HTTP agent — VS Code extensions run behind corporate proxies and need to integrate with `vscode.workspace.getConfiguration('http').get('proxy')`
439467

@@ -463,7 +491,7 @@ Environment-agnostic Tower API client shared between dashboard and extension:
463491
| State | Behavior |
464492
|-------|----------|
465493
| **Activation** | On `codev.*` command or workspace contains `codev/` directory. Lazy — no heavy init until needed. |
466-
| **Tower not running** | Status bar shows offline. Commands show "Tower is not running — start with `af tower start`". TreeView shows empty state. |
494+
| **Tower not running** | Status bar shows offline. Commands show "Tower is not running — start with `afx tower start`". TreeView shows empty state. |
467495
| **Tower starts** | Health check succeeds → SSE connects → TreeView populates → status bar updates |
468496
| **Tower restarts** | SSE drops → reconnection with backoff → terminals print reconnecting banner → WebSockets reattach → ring buffer replay |
469497
| **VS Code reload** | Extension re-activates → reconnects to Tower → re-creates terminal Pseudoterminals → reattaches to existing shellper sessions |
@@ -475,7 +503,7 @@ Environment-agnostic Tower API client shared between dashboard and extension:
475503
- [x] Should the extension be in this monorepo or a separate repo? **RESOLVED: Monorepo.** Extension lives in this repo (e.g., `packages/codev-vscode/`), sharing types and build infrastructure.
476504

477505
### Important (Affects Design)
478-
- [ ] Should `af open` use a VS Code URI scheme (`vscode://codev/open?file=...`) or a filesystem watcher approach?
506+
- [ ] Should `afx open` use a VS Code URI scheme (`vscode://codev/open?file=...`) or a filesystem watcher approach?
479507
- [ ] Should the extension auto-start Tower if it's not running, or always require manual start?
480508
- [ ] Terminal naming convention: `Architect` / `Builder #42 [implement]` or something else?
481509

@@ -503,7 +531,7 @@ Environment-agnostic Tower API client shared between dashboard and extension:
503531
### Functional Tests
504532
1. **Happy path**: Tower running → activate extension → TreeView shows builders → open architect terminal → type command → see output
505533
2. **Send message**: Command Palette → "Send Message" → pick builder #42 → type message → message appears in builder terminal
506-
3. **File opening**: `af open src/index.ts:42` → VS Code editor opens file at line 42
534+
3. **File opening**: `afx open src/index.ts:42` → VS Code editor opens file at line 42
507535
4. **Gate approval**: Status bar shows blocked gate → click → approve → builder resumes
508536
5. **Tower offline**: Stop Tower → status bar turns red → TreeView greys out → restart Tower → everything recovers
509537
6. **Shell terminal**: Command Palette → "New Shell" → shell opens in terminal panel → persists across Tower restart
@@ -526,12 +554,20 @@ Environment-agnostic Tower API client shared between dashboard and extension:
526554
- **Build**: `@vscode/vsce` for packaging and Marketplace publishing
527555

528556
## References
529-
- Spec 0066 (superseded): `codev/specs/0066-vscode-companion-extension.md`
530-
- Tower server architecture: `codev/specs/0105-tower-server-decomposition.md`
557+
- Spec 0066 (prior VS Code spec): `codev/specs/0066-vscode-companion-extension.md`
558+
- Spec 0105 (Tower decomposition): `codev/specs/0105-tower-server-decomposition.md`
559+
- Spec 0618 (v3.0.0 config overhaul): `codev/specs/0618-*`
560+
- Spec 0627 (ScrollController): `codev/specs/0627-*`
561+
- Spec 0637 (author in views): `codev/specs/0637-*`
562+
- Spec 0647 (CLI rename af → afx): `codev/specs/0647-*`
531563
- Architecture docs: `codev/resources/arch.md`
532564
- WebSocket protocol: `packages/codev/src/terminal/ws-protocol.ts`
565+
- EscapeBuffer: `packages/codev/dashboard/src/lib/escapeBuffer.ts`
566+
- ScrollController: `packages/codev/dashboard/src/lib/scrollController.ts`
533567
- Send buffer: `packages/codev/src/agent-farm/servers/send-buffer.ts`
534568
- Tower routes: `packages/codev/src/agent-farm/servers/tower-routes.ts`
569+
- Config loader: `packages/codev/src/lib/config.ts`
570+
- Forge system: `packages/codev/src/lib/forge.ts`, `packages/codev/src/lib/forge-contracts.ts`
535571

536572
## Risks and Mitigation
537573

0 commit comments

Comments
 (0)