Skip to content

feat(websocket): add WS foundational module and message type catalog - #96

Merged
yyy-router merged 4 commits into
1024XEngineer:mainfrom
LUPENGHAN:feature/websocket-foundation
Jul 29, 2026
Merged

feat(websocket): add WS foundational module and message type catalog#96
yyy-router merged 4 commits into
1024XEngineer:mainfrom
LUPENGHAN:feature/websocket-foundation

Conversation

@LUPENGHAN

Copy link
Copy Markdown
Contributor

Summary

  • Add infrastructure/websocket/: connection lifecycle (session.hellosession.ready/session.error), a type-based MessageRouter with unified *.error mapping (unknown type, invalid message, handler exceptions all caught), and a device-keyed ConnectionManager for server-initiated push (send/broadcast, best-effort — no Outbox yet, see Out of Scope below).
  • Add infrastructure/websocket/messages/: Pydantic models for every message type in the architecture doc's WS protocol — session, voice (stream start/end/error, parse result), schedule (upsert/list), location (report/ack), reminder (control/ack, system refs check, system schedule/alarm delete, schedule confirmed). Each type is validated in tests against the doc's example JSON, field-by-field.
  • Wire /ws into main.py's composition root, alongside the existing /api/v1/health route.
  • Extend tests/test_architecture.py to also forbid infrastructure/websocket from importing data/gateway/intelligence — this closes a gap the boundary doc calls out in its own §9.3 ("尚未检查 infrastructure/websocket 对 data/gateway/intelligence 的禁止依赖").

Out of scope (intentional, see issue)

  • No business message handlers are implemented (schedule.upsert, voice.stream.*, etc.) — router.register() is left for each feature's owner to wire in.
  • No Outbox/reliable delivery — schedules has no Outbox table yet; push is best-effort for now.
  • No auth/token validation — device_id is only checked for presence and consistency between the connection query param and session.hello.
  • No heartbeat, protocol version negotiation, or reconnect replay.

Issue

Closes #95

Test plan

  • ruff check . — clean
  • mypy (strict) — clean, 24 source files
  • pytest — 49 passed: unit tests for ConnectionManager/MessageRouter/handle_session_hello, an end-to-end integration test over a real WebSocket via TestClient (connect → hello → route → disconnect cleanup), one success+failure test per message type validated against the architecture doc's example JSON, and the new architecture boundary test
  • Manually verified against a real running uvicorn server (not just TestClient) using a real TCP WebSocket client: missing device_idINVALID_DEVICE_ID; full handshake → session.ready → unregistered type → UNKNOWN_MESSAGE_TYPE; message before hello → SESSION_HELLO_REQUIRED

Add infrastructure/websocket/: connection lifecycle, session.hello
handshake, type-based message routing with unified error mapping, and
a device-keyed connection manager for server-initiated push
(best-effort, no Outbox yet). No business message handlers are
implemented; router.register() is left for each feature owner.

Add infrastructure/websocket/messages/: Pydantic models for every
message type in the architecture doc's WS protocol (session, voice
streaming, schedule upsert/list, location report, reminder control,
system refs check/delete, schedule confirmed), each validated against
the doc's example JSON in tests.

Wire /ws into main.py's composition root alongside the existing
/api/v1/health route.

Extend test_architecture.py to also forbid infrastructure/websocket
from importing data/gateway/intelligence, closing the gap the
boundary doc calls out in its own §9.3.

Issue 1024XEngineer#95

@fennoai fennoai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found four correctness and resilience issues in the WebSocket foundation. Inline comments cover reconnect cleanup, handshake validation, malformed/non-object frames, and best-effort broadcast behavior.

Verification: reviewed the complete current diff and ran git diff --check successfully. The automated suite was not rerun because uv is unavailable in this review environment.

View job run

Comment thread backend/src/timeflow/infrastructure/websocket/endpoint.py Outdated
Comment thread backend/src/timeflow/infrastructure/websocket/endpoint.py
Comment thread backend/src/timeflow/infrastructure/websocket/endpoint.py Outdated
Comment thread backend/src/timeflow/infrastructure/websocket/connection_manager.py Outdated
- Drop build_result_envelope and ConnectionManager.broadcast(): both
  had zero callers and no documented message in the architecture doc
  needs a broadcast-to-all-devices pattern.
- Reuse ErrorDetail inside build_error_envelope instead of hand-rolling
  the same error shape twice.
- Extract session.invalid_device_id_error() so endpoint.py and
  session.py stop constructing the identical INVALID_DEVICE_ID error
  independently.

Issue 1024XEngineer#95
session.py had exactly one caller (endpoint.py) and no concrete future
second consumer, unlike connection_manager.py and router.py which are
each awaiting a near-term second caller (a future workers dispatcher,
and main.py registering business handlers). Merge
handle_session_hello()/invalid_device_id_error() directly into
endpoint.py and fold the matching tests into test_ws_endpoint.py.

Issue 1024XEngineer#95
- ConnectionManager.unregister() now requires the connection instance
  and only clears the entry if it still refers to that connection, so
  a late-exiting old session can't remove a newer reconnected one.
- handle_session_hello() now validates against SessionHello via
  model_validate() instead of only conditionally checking device_id,
  so missing device_id/app_version is rejected as the type requires.
- Add _receive_message() to catch JSON decode failures and non-object
  frames (list/null/etc.) and return a unified MALFORMED_MESSAGE error
  instead of letting the exception crash the session; malformed frames
  before the handshake still close the connection, malformed frames
  after it do not.

Issue 1024XEngineer#95
@yyy-router
yyy-router merged commit 53d7585 into 1024XEngineer:main Jul 29, 2026
1 check passed
LUPENGHAN pushed a commit that referenced this pull request Jul 30, 2026
* Revert "feat(backend): add voice parsing pipeline (#101)"

This reverts commit a720cd0.

* Revert "feat(websocket): add WS foundational module and message type catalog (#96)"

This reverts commit 53d7585.
@fennoai fennoai Bot mentioned this pull request Jul 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Proposal:WS 基础模块(连接、握手、路由、推送、消息类型定义)

2 participants