Wave Client for Emacs - Project Documentation

Table of Contents

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

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

  1. Understand First: Read original elisp before modifications
  2. Simulator-First: Build Python server adhering to Wave Protocol
  3. Contract Testing: Verify protocol compliance between elisp and server
  4. 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 /health endpoint to wave_server.py
    • Fixed doc_data type handling (was list, expected dict)
  • Updated REST tests to use correct DeltaSubmission schema
  • Updated WebSocket tests for websockets 15+ 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:

  1. Missing /health endpoint
  2. doc_data was 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] Fix GET /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

  1. [ ] Implement TUI dashboard demo
  2. [ ] Full E2E elisp integration test
  3. [ ] Document protocol compliance
  4. [ ] Add GitHub Actions CI workflow

References

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

Author: Jason Walsh

jwalsh@nexus

Last Updated: 2026-01-10 16:47:25

build: 2026-01-11 18:32 | sha: eb805a8