From 7adda343fc2ea5974df7ff6b2a834dc7cffdbeb7 Mon Sep 17 00:00:00 2001 From: Patrick Buckley Date: Wed, 4 Mar 2026 06:12:31 -0800 Subject: [PATCH] Update docs and diagrams for cluster-scale schema changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - StorageBackend protocol: document 5 new workstream methods (26 total) - Session ID: 12-char hex → 32-char full UUID in API reference - /health endpoint: add node_id field to response docs - sessions table: document node_id and ws_id columns - Bridge node_id: document server-owned identity with /health retrieval - Regenerate storage architecture PNG from updated PlantUML --- docs/api-reference.md | 8 ++++++-- docs/architecture.md | 20 ++++++++++++++----- docs/diagrams/14-storage-architecture.puml | 14 ++++++++++--- docs/diagrams/png/14-storage-architecture.png | 4 ++-- 4 files changed, 34 insertions(+), 12 deletions(-) diff --git a/docs/api-reference.md b/docs/api-reference.md index e174083f..6d2e246f 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -394,12 +394,14 @@ Each session object: | Field | Type | Description | |-----------------|-------------|--------------------------------------------| -| `session_id` | string | Unique 12-char hex session identifier | +| `session_id` | string | Unique 32-char hex UUID session identifier | | `alias` | string/null | User-assigned short name | | `title` | string/null | LLM-generated title | | `created` | string | ISO timestamp of session creation | | `updated` | string | ISO timestamp of last message | | `message_count` | int | Number of messages in the session | +| `node_id` | string/null | Server node that created the session | +| `ws_id` | string/null | Workstream the session belongs to | --- @@ -692,7 +694,8 @@ liveness probes. ```json { "status": "ok", - "version": "0.3.0", + "version": "0.3.5", + "node_id": "worker-01_a3f2", "uptime_seconds": 3614.72, "model": "llama-3.1-70b-instruct", "workstreams": { @@ -714,6 +717,7 @@ liveness probes. |-------|------|-------------| | `status` | string | `"ok"` or `"degraded"` (degraded when backend unreachable) | | `version` | string | turnstone server version | +| `node_id` | string | Server-generated node identity (`{hostname}_{4hex}`) | | `uptime_seconds` | number | Seconds since the server process started | | `model` | string | Model name detected or configured at startup | | `workstreams.total` | integer | Total active workstreams | diff --git a/docs/architecture.md b/docs/architecture.md index e09b362f..0945a481 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -713,7 +713,7 @@ and are the single source of truth for both backends and Alembic migrations. | Method | Purpose | |--------|---------| -| `register_session(session_id, title)` | Create a sessions row (no-op if exists) | +| `register_session(session_id, title, node_id, ws_id)` | Create a sessions row (no-op if exists) | | `save_message(session_id, role, content, ...)` | Log a message to conversations | | `load_session_messages(session_id)` | Reconstruct OpenAI message format from DB rows | | `list_sessions(limit)` | List sessions with >=1 message, ordered by updated DESC | @@ -725,6 +725,11 @@ and are the single source of truth for both backends and Alembic migrations. | `set_session_alias(session_id, alias)` | Set user-friendly alias (returns False if taken) | | `get_session_name(session_id)` | Return alias if set, else title, else None | | `update_session_title(session_id, title)` | Set/update LLM-generated title | +| `register_workstream(ws_id, node_id, name, state)` | Create a workstreams row (no-op if exists) | +| `update_workstream_state(ws_id, state)` | Update workstream state and bump timestamp | +| `update_workstream_name(ws_id, name)` | Update workstream display name | +| `delete_workstream(ws_id)` | Delete a workstream row | +| `list_workstreams(node_id, limit)` | List workstreams, optionally by node | | `kv_get(key)` / `kv_set(key, value)` / `kv_delete(key)` | Generic key-value store (backs memories table) | | `kv_list()` / `kv_search(query)` | List or search key-value pairs | | `search_history(query, limit)` | Full-text search (FTS5 on SQLite, tsvector on PostgreSQL) | @@ -745,9 +750,11 @@ Environment variables: `TURNSTONE_DB_BACKEND`, `TURNSTONE_DB_URL`, `TURNSTONE_DB ### Session Persistence and Resume -Each `ChatSession` generates a 12-char hex `_session_id` on creation and -registers it in the `sessions` table. Messages are saved to `conversations` -as they happen via `save_message()`. +Each `ChatSession` generates a full 32-char hex UUID `_session_id` on creation +and registers it in the `sessions` table with the server's `node_id` and the +owning `ws_id`. Messages are saved to `conversations` as they happen via +`save_message()`. Workstreams are persisted to the `workstreams` table on +creation, with state changes tracked via `update_workstream_state()`. **Auto-titling:** After the first complete exchange (user message + assistant response), a background thread calls the LLM with a title-generation prompt @@ -1037,7 +1044,10 @@ a response or the approval timeout (default 3600s / 1 hour) expires. `ws_id` for active sends. When the global SSE reports `ws_state → idle` for a tracked workstream, the bridge emits a synthetic `TurnCompleteEvent` with the correlation ID. -**Multi-node routing:** Each bridge has a `node_id` (defaults to hostname) and BLPOPs +**Multi-node routing:** Each bridge retrieves its `node_id` from the server's +`/health` endpoint on startup (with exponential backoff retry). The server +generates the `node_id` (`{hostname}_{4hex}`) and is the sole authority for +node identity. The bridge BLPOPs from both `turnstone:inbound:{node_id}` (directed, priority) and `turnstone:inbound` (shared). Messages with `target_node` set are pushed to the target's per-node queue. Messages for existing workstreams are auto-routed via `turnstone:ws:{ws_id}` ownership keys in Redis. diff --git a/docs/diagrams/14-storage-architecture.puml b/docs/diagrams/14-storage-architecture.puml index 366a25e7..1795c935 100644 --- a/docs/diagrams/14-storage-architecture.puml +++ b/docs/diagrams/14-storage-architecture.puml @@ -13,7 +13,7 @@ skinparam class { ' -- Protocol -- interface "StorageBackend" as SB <> { - +register_session(session_id, title) + +register_session(session_id, title, node_id, ws_id) +save_message(session_id, role, content, ...) +load_session_messages(session_id) → list[dict] +list_sessions(limit) → list @@ -25,6 +25,11 @@ interface "StorageBackend" as SB <> { +set_session_alias(session_id, alias) → bool +get_session_name(session_id) → str | None +update_session_title(session_id, title) + +register_workstream(ws_id, node_id, name, state) + +update_workstream_state(ws_id, state) + +update_workstream_name(ws_id, name) + +delete_workstream(ws_id) → bool + +list_workstreams(node_id, limit) → list +kv_get(key) → str | None +kv_set(key, value) → str | None +kv_delete(key) → bool @@ -58,7 +63,8 @@ class "_schema.py" as Schema <> { +metadata: MetaData +memories: Table +conversations: Table - +sessions: Table + +sessions: Table (node_id, ws_id) + +workstreams: Table (node_id, state) +session_config: Table -- SQLAlchemy Core @@ -94,9 +100,11 @@ class "memory.py" as Facade <> { +register_session() +save_message() +load_session_messages() + +register_workstream() + +update_workstream_state() +save_memory() / delete_memory() +search_memories() - +... (all 18 functions) + +... (all 22 functions) -- Thin delegation to get_storage() diff --git a/docs/diagrams/png/14-storage-architecture.png b/docs/diagrams/png/14-storage-architecture.png index 1bc7d2cb..8f3ae227 100644 --- a/docs/diagrams/png/14-storage-architecture.png +++ b/docs/diagrams/png/14-storage-architecture.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0c615984373b4893b6cc5755604f137541e9d391862122746a4fcbae63543563 -size 201041 +oid sha256:bd776a34b50d3fe194e2a34d3b4d04ec71af31e1154b5fc98223636b1844e6b8 +size 226686