Wave Client for Emacs - Project Documentation
Table of Contents
- Overview
- Methodology
- Technical Stack
- Architecture
- Testing
- Progress Log
- Issues & Resolutions
- Build Times (FreeBSD 14.3)
- Completed Milestones
- Next Steps
- References
- Appendix: Beads Issue IDs
Overview
This document tracks the progress, approach, methodology, issues, concerns, and timing for the Wave Client for Emacs revival project.
Project Goal
Revive and modernize the original 2009-era Google Wave Protocol Emacs client to work with modern infrastructure:
- Python 3.11+ (was Python 2.x era)
- Emacs 30.1 (was Emacs 24.x)
- FreeBSD 14.3 development environment
- FastAPI/WebSocket backend simulator
Repository
- Main: https://github.com/aygp-dr/wave-client-for-emacs
- Original Source Archive: https://github.com/aygp-dr/wave-client-elisp-original (private)
Methodology
Multi-Agent Workflow
Using Gas Town (gt) and Beads (bd) for coordinated AI-assisted development:
- Beads: AI-native issue tracking stored in
.beads/directory- Epics for major initiatives
- Tasks for discrete work items
- Dependencies between issues
- Priority levels (P0-P4)
- Gas Town: Multi-agent workspace manager
- Rigs for agent configuration
- Polecats for ephemeral workers
- Sling for work distribution
- Stash for handoffs
Development Approach
- Understand First: Read original elisp before modifications
- Simulator-First: Build Python server adhering to Wave Protocol
- Contract Testing: Verify protocol compliance between elisp and server
- Incremental Progress: Small, testable changes with git commits
Technical Stack
| Component | Version | Purpose |
|---|---|---|
| Python | 3.11.13 | Server backend |
| Emacs | 30.1 | Client runtime |
| FreeBSD | 14.3 | Development environment |
| FastAPI | latest | REST/WebSocket server |
| SQLite | 3.x | Wave data persistence |
| uv | 0.6.4 | Python package management |
| pytest | latest | Test framework |
| websockets | 15.0.1 | WebSocket client for tests |
| httpx | latest | HTTP client for tests |
Architecture
┌─────────────────────┐ ┌─────────────────────┐
│ Emacs Client │ │ Wave Server │
│ │ │ (Python/FastAPI) │
│ wave-mode.el │◄────────│ │
│ wave-list.el │ WS │ /ws endpoint │
│ wave-edit.el │ + │ /api/inbox │
│ wave-client.el │ HTTP │ /api/waves/{id} │
└─────────────────────┘ │ /health │
└─────────────────────┘
│
▼
┌─────────────────────┐
│ SQLite Database │
│ wave_server.db │
└─────────────────────┘
Testing
Test Results Summary (2026-01-10)
| Test Suite | Tests | Passed | Status |
|---|---|---|---|
| REST API Extended | 18 | 18 | ✓ Pass |
| WebSocket Extended | 14 | 14 | ✓ Pass |
| Total | 32 | 32 | ✓ Pass |
Running Tests
Python Integration Tests
# Start the server first gmake server # In another terminal, run tests gmake test-python # Or run specific test files uv run pytest tests/integration/test_rest_extended.py -v uv run pytest tests/integration/test_ws_extended.py -v
Quick API Smoke Test
gmake test-api
Output:
Testing Wave Server API...
=== Health Check ===
{"status":"healthy","timestamp":"2026-01-10T21:12:13.817809"}
=== Inbox ===
[{"id":"indexwave!indexwave","digest":"Wave Server Index","unread":0,"creator":"system@localhost"},...]
=== Root ===
{"message":"Wave Server API","status":"running","version":"0.2.0"}
Emacs Batch Mode Testing
Emacs batch mode allows headless testing without a GUI. This is essential for CI/CD pipelines.
Available Make Targets
gmake elisp-version # Show Emacs version gmake elisp-load-test # Test elisp modules load without errors gmake elisp-check-syntax # Byte-compile check for warnings gmake elisp-http-inbox # Fetch inbox via HTTP (requires server)
Emacs Version Check
gmake elisp-version
Output:
Emacs version: GNU Emacs 30.1 (build 1, aarch64-unknown-freebsd14.3)
Load Test (No Server Required)
Tests that all elisp modules load without errors:
gmake elisp-load-test
Output:
Testing elisp module loading... Loading wave-client.el... done Loading wave-mode.el... done Loading wave-list.el... done Loading wave-edit.el... done All modules loaded successfully!
HTTP Inbox Fetch (Requires Server)
# Start server first gmake server & sleep 3 # Fetch inbox via Emacs batch mode gmake elisp-http-inbox
Output:
Fetching inbox via HTTP from localhost:9898... === INBOX (5 waves) === indexwave!indexwave: Wave Server Index localhost!conv+root: Discussion: Wave protocol features localhost!conv+root: Emacs client testing localhost!conv+root: Architecture discussion with diagram localhost!b+reply1: Charlie: Don't forget about the conflict resolution algorithms!
Batch Mode Elisp Code Example
The elisp-http-inbox target runs this code:
(require 'url) (require 'json) (defun fetch-inbox () (with-current-buffer (url-retrieve-synchronously "http://localhost:9898/api/inbox" t) (goto-char (point-min)) (re-search-forward "^$" nil t) (let* ((json-object-type 'plist) (inbox (json-read))) (message "=== INBOX (%d waves) ===" (length inbox)) (dolist (wave (append inbox nil)) (message " %s: %s" (plist-get wave :id) (plist-get wave :digest)))))) (fetch-inbox)
WebSocket Testing
Python WebSocket Test Script
gmake ws-test
Uses scripts/ws-test.py to verify WebSocket connectivity:
Connecting to ws://localhost:9898/ws...
Connected!
Sending ProtocolOpenRequest for indexwave!indexwave
Received: {"version":0,"sequenceNumber":1,"messageType":"ProtocolWaveletUpdate",...}
All tests passed!
Interactive WebSocket Client
gmake ws-test-interactive
Opens an interactive session for manual WebSocket testing.
REST API Test Coverage
| Endpoint | Test Cases |
|---|---|
GET /health |
Returns 200, contains status, contains timestamp |
GET /api/inbox |
Returns list, items have required fields |
GET /api/waves/{id} |
404 for nonexistent, returns wavelet data |
POST /api/waves/{id}/submit |
Add participant, remove participant, returns version |
GET / |
Returns 200, contains version info |
| Error handling | Invalid JSON returns 422, missing fields returns 422 |
| Content types | All endpoints return application/json |
WebSocket Test Coverage
| Category | Test Cases |
|---|---|
| Connection lifecycle | Connect success, clean disconnect, multiple connections |
ProtocolOpenRequest |
Valid wave, nonexistent wave, multiple requests |
| Message handling | Malformed JSON, empty message, invalid type, missing fields |
| Sequence numbers | Response includes sequence, large sequence numbers |
| Concurrency | Rapid messages (10 in sequence), interleaved connections |
Progress Log
2026-01-10: All Tests Passing
- Fixed
GET /api/waves/{id}500 error- Added
/healthendpoint towave_server.py - Fixed
doc_datatype handling (was list, expected dict)
- Added
- Updated REST tests to use correct
DeltaSubmissionschema - Updated WebSocket tests for
websockets15+ API - Final Results: 32/32 tests passing
2026-01-10: Extended Test Suite Created
- Created
tests/integration/test_rest_extended.py(18 tests) - Created
tests/integration/test_ws_extended.py(14 tests)
2026-01-08: WebSocket Tests Pass
- Completed
wave-client-for-emacs-x1z(WebSocket test task) - 8/8 WebSocket tests passing
- Database seeded with 4 waves, 5 wavelets, 3 updates
2026-01-07: Elisp Batch Mode
- Added Makefile targets for headless testing
- Fixed batch mode async issues using
url-retrieve-synchronously
Issues & Resolutions
Resolved Issues
GET /api/waves/{id} Returns 500 [FIXED]
Problem: The waves endpoint had two issues:
- Missing
/healthendpoint doc_datawas sometimes a list instead of dict
Solution: Added type checking in get_wave endpoint:
if isinstance(doc_data, dict): docs_dict[doc_id] = Document( doc_id=doc_id, contributors=doc_data.get("contributors", []), ... ) else: # doc_data is a primitive or list, wrap it as content docs_dict[doc_id] = Document( doc_id=doc_id, contributors=[], content=[doc_data] if not isinstance(doc_data, list) else doc_data )
WebSocket Tests Failing with websockets 15+ [FIXED]
Problem: The websockets library 15.0+ removed the .open attribute.
Solution: Use .state instead:
from websockets.protocol import State def is_connected(ws) -> bool: return ws.state == State.OPEN
Known Limitations
Elisp Batch Mode Async
WebSocket operations in batch mode hit recursion limits due to
accept-process-output loop depth. Use synchronous HTTP for batch testing.
FreeBSD Wheel Building
uv rebuilds C extensions on FreeBSD. Workaround: use polecat worktree venv
which has pre-built packages.
Build Times (FreeBSD 14.3)
| Operation | Time |
|---|---|
uv sync (cold) |
2-5 min |
uv sync (warm) |
<1 sec |
pytest integration |
10-30 sec |
| elisp byte-compile | 5-10 sec |
| Server startup | 2-3 sec |
Completed Milestones
[X]FixGET /api/waves/{id}500 error[X]Run extended REST test suite (18/18 passing)[X]Run extended WebSocket test suite (14/14 passing)[X]Emacs batch mode testing working[X]Database seeding with test data
Next Steps
[ ]Implement TUI dashboard demo[ ]Full E2E elisp integration test[ ]Document protocol compliance[ ]Add GitHub Actions CI workflow
References
- Original Google Code Archive (downloads unavailable)
- Original Elisp Source Archive (private backup)
- Gas Town Introduction by Steve Yegge
- Beads + Gas Town Workflow (private gist)
Appendix: Beads Issue IDs
| ID | Type | Priority | Status | Description |
|---|---|---|---|---|
| wave-client-for-emacs-4ez | epic | P1 | active | Full Integration Test Suite |
| wave-client-for-emacs-75v | epic | P1 | active | TUI Dashboard Demo |
| wave-client-for-emacs-vjm | epic | P1 | active | Wave Protocol Simulator |
| wave-client-for-emacs-802 | task | P2 | completed | Seed database |
| wave-client-for-emacs-x1z | task | P2 | completed | WebSocket tests |
| hq-jxx | bug | P2 | open | Google Code Archive missing downloads |