Files
whetstone_DSL/HANDOFF-2026-03-09-sprint291.md

158 lines
5.3 KiB
Markdown
Raw Normal View History

# Whetstone Handoff — Sprint 291: HTTP/SSE Daemon Mode
**Date:** 2026-03-09
**Steps:** 19831987
**Tests:** 5/5 passing
---
## What Was Built
Eliminates the dual-process bug where each Claude Code session spawned its own
`whetstone_mcp` stdio process with independent in-memory state. `start_recording`
in one session and `get_metrics` in another would hit different processes and lose
data. Recording state, workspace, buffers, and AST were all split.
**Fix:** HTTP+SSE daemon mode. One shared daemon process, all sessions connect via
MCP HTTP+SSE transport. Single in-memory state for everything.
### New files
- `editor/src/MCPHttpServer.h` — HTTP+SSE server (Step 1983)
- Uses cpp-httplib (fetched via FetchContent, header-only)
- `GET /sse` — opens SSE stream, sends `event: endpoint` with session URL
- `POST /message?sessionId=X` — routes JSON-RPC to handler, pushes response to SSE
- `GET /health` — status JSON `{status, sessions, port}`
- Per-connection `Session` struct: mutex + condition_variable + queue<string>
- 25s keepalive timeout (sends SSE comment to prevent proxy drops)
- Writes `{workspace}/.whetstone/daemon.pid` on start, removes on exit
- `editor/src/Sprint291IntegrationSummary.h` — sprint metadata (Step 1986)
- `editor/tests/step1987_test.cpp` — integration tests (Step 1987)
- `tools/start-whetstone-daemon.sh` — idempotent startup script (Step 1986)
### Modified files
- `editor/src/MCPBridge.h` — added `runHttp(int port, const std::string& workspace)`
- `editor/src/mcp_main.cpp` — added `--daemon` and `--port N` flags (default 7600),
PID lockfile, duplicate daemon guard via `kill(pid, 0)` check
- `editor/CMakeLists.txt` — FetchContent for cpp-httplib, step1983-1987 targets
- `Documents/.mcp.json` (not in this repo) — switched from `command:` (stdio) to
`"type": "sse", "url": "http://127.0.0.1:7600/sse"`
---
## How to Use It
### Starting the daemon (do this once before any Claude Code sessions)
```bash
cd /home/bill/Documents/CLionProjects/whetstone_DSL
tools/start-whetstone-daemon.sh
# prints PID when ready; idempotent — safe to run again if already up
```
Verify it's running:
```bash
curl http://127.0.0.1:7600/health
# → {"status":"ok","sessions":0,"port":7600}
```
### Autostart (so you don't have to do it manually)
Add to `~/.bashrc` or `~/.profile`:
```bash
# Auto-start whetstone daemon if not running
if ! curl -sf http://127.0.0.1:7600/health >/dev/null 2>&1; then
/home/bill/Documents/CLionProjects/whetstone_DSL/tools/start-whetstone-daemon.sh >/dev/null 2>&1 &
fi
```
Or as a systemd user service:
```bash
cat > ~/.config/systemd/user/whetstone-daemon.service << 'EOF'
[Unit]
Description=Whetstone MCP Daemon
After=network.target
[Service]
Type=simple
ExecStart=/home/bill/Documents/CLionProjects/whetstone_DSL/editor/build-native/whetstone_mcp \
--daemon \
--port 7600 \
--workspace /home/bill/Documents/CLionProjects/whetstone_DSL \
--language cpp
Restart=on-failure
RestartSec=5
[Install]
WantedBy=default.target
EOF
systemctl --user enable --now whetstone-daemon
```
### Stopping the daemon
```bash
kill $(cat /home/bill/Documents/CLionProjects/whetstone_DSL/.whetstone/daemon.pid)
# or
pkill -f "whetstone_mcp --daemon"
```
### Falling back to stdio mode
Change `.mcp.json` back to:
```json
{
"mcpServers": {
"whetstone": {
"command": "/home/bill/Documents/CLionProjects/whetstone_DSL/editor/build-native/whetstone_mcp",
"args": ["--workspace", "/home/bill/Documents/CLionProjects/whetstone_DSL", "--language", "cpp"]
}
}
}
```
---
## Architecture Notes
### MCP HTTP+SSE transport (2024-11-05 spec)
1. Client opens `GET /sse` — receives `event: endpoint\ndata: /message?sessionId=<uuid>`
2. Client sends requests via `POST /message?sessionId=<uuid>`
3. Server pushes responses back over the SSE stream as `event: message\ndata: <json>`
### Thread safety
- `sessions_` map protected by `sessionsMtx_`
- Each session has its own mutex + cv + queue — POST handler and SSE provider communicate via cv.notify_one()
- cpp-httplib handles one thread per connection automatically
### Session lifecycle
- Session created on `GET /sse`, stored in map
- Session removed via the httplib `ContentProviderResourceReleaser` callback when SSE connection closes
- PID lockfile prevents two daemons on the same workspace
---
## Build
```bash
cd /home/bill/Documents/CLionProjects/whetstone_DSL/editor
cmake -S . -B build-native -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_PREFIX_PATH="$(pwd)/build-native/vcpkg_installed/x64-linux"
cmake --build build-native --target whetstone_mcp step1987_test --parallel
./build-native/step1987_test # 5/5 should pass
```
cpp-httplib is fetched via FetchContent (v0.18.1) — no vcpkg required.
---
## What Comes Next
- **Phase 7 (TBD):** Plan in a new session
- **Daemon autostart:** Systemd user service is the cleanest long-term solution
- **Language selection:** Current daemon starts with `--language cpp`. For Python projects,
the daemon needs `--language python` or a language-switching mechanism. As a workaround,
run separate daemons on different ports with different languages and point project-specific
`.mcp.json` files at the appropriate port.
- **Multi-language daemon:** Future improvement — a single daemon that accepts
`language` as a per-request parameter rather than a startup flag.