Skip to content

feat(frontend): add session transport and contracts - #130

Merged
gac0812 merged 3 commits into
1024XEngineer:MVPfrom
gac0812:codex/frontend-session-transport
Aug 1, 2026
Merged

feat(frontend): add session transport and contracts#130
gac0812 merged 3 commits into
1024XEngineer:MVPfrom
gac0812:codex/frontend-session-transport

Conversation

@gac0812

@gac0812 gac0812 commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Summary

  • define the frontend WebSocket contracts for session, schedule, voice, reminder, and transport envelopes
  • add a real WebSocket client with request correlation, binary frames, connection state, and pending-request cleanup
  • persist device_id, build the backend-compatible session endpoint, and complete the session.hello handshake
  • keep the in-process fake transport restricted to development builds and cover its schedule behavior

Why

The frontend previously had no durable session identity or production transport boundary. This establishes the backend contract and real connection lifecycle before feature UI is wired to it.

Impact

This is an infrastructure layer and does not replace the existing screen by itself. It is the transport dependency for the schedule and assistant PRs that follow.

Dependency

Validation

  • npm ci
  • npm run check (10 suites, 43 tests)
  • npx expo export --platform android --output-dir ../../export-session

@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 three behavioral issues in the new transport layer. The local validation command npm run check passes, but its fake/contracts currently mask production incompatibilities.

client.close();
setConnectionStatus('error');
setConnectionError(error instanceof Error ? error.message : 'WebSocket 连接失败');
scheduleReconnect();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[P2] Avoid scheduling the same reconnect twice. client.close() synchronously emits closed, and the status listener already calls scheduleReconnect(). Calling it again here clears that timer and increments reconnectAttempt a second time; the timeout path has the same pattern. As a result, retries jump 2s → 8s → 30s instead of using the configured 1s → 2s → 4s backoff. Schedule from one place only (and add a provider lifecycle test).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 5943819. Reconnect scheduling now happens only from the closed-status listener; the timeout and connect-failure paths close the client without incrementing the backoff again. Added a SessionProvider lifecycle test proving the first retry occurs after 1 second. All 45 tests pass.

Comment thread frontend/src/contracts/session.ts Outdated
timestamp: string;
};

export type LocationReport = WsRequest<'location.report', LocationReportPayload>;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[P1] This contract does not match the production backend. LocationReport is currently modeled as {request_id, payload}, but the backend validates schedule_scope, coordinates, accuracy, and timestamp at the top level. Its successful location.report.ack also has neither request_id nor payload. Code using this type with WsClient.request() will send an invalid report and cannot correlate a successful ack; the fake's correlated envelope hides the problem. Align both message shapes (or change the backend in the same dependency chain) before consumers rely on this contract.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 5943819. LocationReport now matches the backend top-level fields, and LocationReportAck no longer invents request_id or payload. The fake and an integration test now use the production shapes. All 45 tests pass.

Comment thread frontend/src/dev/fakes/FakeWsServer.ts Outdated
setTimeout(() => this.reply(parseResult), 0);
}

private handleVoiceCancel(message: VoiceStreamCancelCommand): void {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[P2] The fake reports voice.stream.cancel as supported, but the production router only registers voice.stream.start and voice.stream.end; remote sessions return UNKNOWN_MESSAGE_TYPE instead of voice.stream.cancelled. This makes cancellation work in development/tests and fail after switching transports. Either implement the backend route or make the fake/contracts reflect the current unsupported behavior.

@gac0812
gac0812 merged commit 13d0226 into 1024XEngineer:MVP Aug 1, 2026
1 check passed
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.

1 participant