Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
203 changes: 203 additions & 0 deletions specs/port/trios/crates/trios-bridge/src/server.t27
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
// WebSocket server for the Trinity Agent Bridge.
// Port of gHashTag/trios:crates/trios-bridge/src/server.rs
// (at 8b229e9489ee, 165 lines) — Roadmap Stage 6, goal #4548, target
// t27c gen-rust.
//
// The original runs the agent-bridge WebSocket server: a new server
// carries its agent router, a broadcast channel with room for 1024
// queued messages, the GitHub repo and the optional GitHub token; the
// getters read them back (the token optional, so an absent token
// answers none); subscribe joins the broadcast; broadcast_message
// serializes a bridge message and fans it to every subscriber; serve
// accepts connections, forwarding broadcasts out and dispatching
// incoming text through handle_message (a Close frame or a stream
// error ends the read loop, the handshake failure only logs); and
// handle_message routes by message type — a SendCommand for an agent
// that is not registered and not the broadcast target answers the
// agent_not_found error to everyone, otherwise the message forwards;
// ClaimIssue and UpdateStatus apply to the router and then broadcast
// the fresh board state; ListAgents answers the board state; anything
// else forwards unchanged.
//
// The WebSocket accept loop, the serialization and the router state
// are plumbing; the decisions — the 1024-deep broadcast capacity, the
// token's optionality, the dispatch table with its agent_not_found
// gate and the three board-refreshing variants — are ported here as
// pure functions and tested (issue #9121, "Port the decision, not
// the plumbing").

module port::trios_bridge_server;

// ---------------------------------------------------------------------------
// Original lines 25-33: `pub fn new(github_repo, github_token)` — the
// server carries the router, a 1024-slot broadcast channel, the repo
// and the optional token.
// ---------------------------------------------------------------------------

pub struct BridgeServerPlan {
broadcast_capacity: usize,
token_present: bool,
repo: str,
}

pub fn BridgeServer_new(github_repo: str, github_token_present: bool) -> BridgeServerPlan {
return BridgeServerPlan {
broadcast_capacity: 1024,
token_present: github_token_present,
repo: github_repo,
};
}

pub fn broadcast_channel_capacity() -> usize {
return 1024;
}

// ---------------------------------------------------------------------------
// Original lines 36-53: the getters — router, repo, and the optional
// token answering none when absent; subscribe joins the broadcast.
// ---------------------------------------------------------------------------

pub fn BridgeServer_router() -> bool {
return true;
}

pub fn BridgeServer_github_repo(plan: BridgeServerPlan) -> str {
return plan.repo;
}

pub fn BridgeServer_github_token(token_present: bool) -> bool {
return token_present;
}

pub fn BridgeServer_subscribe() -> bool {
return true;
}

// ---------------------------------------------------------------------------
// Original lines 56-60: `broadcast_message` — serialize then fan out;
// a failed send is tolerated (the `let _`), so the broadcast answers
// Ok regardless.
// ---------------------------------------------------------------------------

pub fn BridgeServer_broadcast_message(send_failed: bool) -> bool {
return true;
}

// ---------------------------------------------------------------------------
// Original lines 63-119: `serve` — the accept loop. The read loop
// continues on text frames, ends on a Close frame or a stream error,
// ignores other frame kinds, and a failed handshake only logs (the
// server keeps accepting).
// ---------------------------------------------------------------------------

pub fn BridgeServer_serve(handshake_failed: bool) -> bool {
return true;
}

pub fn read_loop_continues(frame_kind: str) -> bool {
if (frame_kind == "text") {
return true;
}
if (frame_kind == "close") {
return false;
}
if (frame_kind == "error") {
return false;
}
return true;
}

// ---------------------------------------------------------------------------
// Original lines 122-165: `handle_message` — the dispatch table. The
// SendCommand gate: a target that is neither the broadcast literal nor
// a registered agent answers the agent_not_found error broadcast;
// otherwise the message forwards. ClaimIssue and UpdateStatus apply
// and refresh the board state for everyone; ListAgents answers the
// board state; anything else forwards unchanged.
// ---------------------------------------------------------------------------

pub const BROADCAST_TARGET: str = "broadcast";

pub fn BridgeServer_handle_message(msg_kind: str, target: str, target_registered: bool) -> str {
if (msg_kind == "send_command") {
if (target != BROADCAST_TARGET and !target_registered) {
return "agent_not_found";
}
return "forward";
}
if (msg_kind == "claim_issue") {
return "apply_and_board";
}
if (msg_kind == "update_status") {
return "apply_and_board";
}
if (msg_kind == "list_agents") {
return "board_state";
}
return "forward";
}

pub fn agent_not_found_error_code() -> str {
return "agent_not_found";
}

// ---------------------------------------------------------------------------
// Tests: every decision above asserted against values read from the
// original.
// ---------------------------------------------------------------------------

test new_carries_capacity_repo_and_token {
const with_token = BridgeServer_new("gHashTag/trios", true);
assert(with_token.broadcast_capacity == 1024);
assert(with_token.token_present);
assert(with_token.repo == "gHashTag/trios");
const without_token = BridgeServer_new("gHashTag/t27", false);
assert(!without_token.token_present);
assert(broadcast_channel_capacity() == 1024);
}

test getters_read_back {
const plan = BridgeServer_new("gHashTag/trios", false);
assert(BridgeServer_router());
assert(BridgeServer_github_repo(plan) == "gHashTag/trios");
assert(!BridgeServer_github_token(false));
assert(BridgeServer_github_token(true));
assert(BridgeServer_subscribe());
}

test broadcast_tolerates_failed_sends {
assert(BridgeServer_broadcast_message(false));
assert(BridgeServer_broadcast_message(true));
}

test serve_accepts_past_failures {
assert(BridgeServer_serve(true));
assert(BridgeServer_serve(false));
}

test read_loop_frame_rules {
assert(read_loop_continues("text"));
assert(!read_loop_continues("close"));
assert(!read_loop_continues("error"));
assert(read_loop_continues("ping"));
}

test broadcast_target_literal {
assert(BROADCAST_TARGET == "broadcast");
}

test send_command_gate {
assert(BridgeServer_handle_message("send_command", "broadcast", false) == "forward");
assert(BridgeServer_handle_message("send_command", "agent-7", true) == "forward");
assert(BridgeServer_handle_message("send_command", "agent-7", false) == "agent_not_found");
assert(BridgeServer_handle_message("send_command", "broadcast", true) == "forward");
assert(agent_not_found_error_code() == "agent_not_found");
}

test dispatch_table_variants {
assert(BridgeServer_handle_message("claim_issue", "", true) == "apply_and_board");
assert(BridgeServer_handle_message("update_status", "", true) == "apply_and_board");
assert(BridgeServer_handle_message("list_agents", "", true) == "board_state");
assert(BridgeServer_handle_message("unknown_kind", "", true) == "forward");
assert(BridgeServer_handle_message("board_state", "", true) == "forward");
}
Loading