feat: web SPA bind — add 'combined' (:8392) both-plane option as default

The bind dropdown offered only memory/affect single-plane binds; #18's
composite endpoint (:8392, both planes in one session) was never reachable
from the SPA. Add 'combined' as the default-selected option, keeping
memory-only / affect-only for single-plane isolation diagnostics.

- endpoint_for_plane: combined -> :8392 (sessions.py)
- web server: accept bifrost_plane="combined" (server.py)
- dropdown: combined (:8392) default-selected, single-plane retained (index.html)
- #17 contract: endpoint_for_plane FN + plane-selector spec updated to combined
- tests: endpoint_for_plane combined, server combined bind -> :8392, dropdown default

Suite 506 green. Live-verified on :8765 (current code).
This commit is contained in:
vh
2026-06-20 16:47:03 -07:00
parent c5c8ecf9d5
commit 719e4d605b
8 changed files with 74 additions and 20 deletions
+44
View File
@@ -837,6 +837,50 @@ class TestWebBifrostBind:
}
assert upstream.headers["Authorization"] == "Bearer server-ck"
@respx.mock
def test_combined_plane_binds_to_8392(self) -> None:
"""combined [#18 composite]: a 'combined' plane from the browser → the server
binds the :8392 both-plane endpoint; bound-state echoes plane='combined'."""
import json as _json
from ratatoskr.web.server import create_app
route = respx.post("https://w.example/sessions").mock(
return_value=httpx.Response(201, json=_CREATE_OK)
)
app = create_app(
_mock_client_factory(),
bifrost_consumer_key="server-ck",
bifrost_visible_host="10.100.10.50",
)
resp = TestClient(app).post(
"/api/sessions",
json={"agent_id": "ratatoskr:sindra", "bifrost_plane": "combined"},
)
assert resp.status_code == 201
assert resp.json()["bifrost"] == {
"plane": "combined",
"endpoint": "http://10.100.10.50:8392",
"status": "bound",
}
upstream = route.calls[0].request
body = _json.loads(upstream.content)
assert body["bifrost"] == {
"endpoint_url": "http://10.100.10.50:8392",
"scope": None,
}
def test_dropdown_offers_combined_as_default(self) -> None:
"""(a)+default: the SPA plane dropdown offers a 'combined' (:8392) option,
it is the DEFAULT-selected one, and single-plane memory/affect remain."""
from pathlib import Path
import ratatoskr.web as web_pkg
html = (Path(web_pkg.__file__).parent / "static" / "index.html").read_text()
assert '<option value="combined" selected>' in html
assert 'value="memory"' in html and 'value="affect"' in html
@respx.mock
def test_plane_without_server_config_is_400(self) -> None:
"""A plane requested but no server-held key/host → bifrost_not_configured."""