diff --git a/specs/port/trios/crates/trios-bridge/src/agents.t27 b/specs/port/trios/crates/trios-bridge/src/agents.t27 new file mode 100644 index 0000000000..a09564f3df --- /dev/null +++ b/specs/port/trios/crates/trios-bridge/src/agents.t27 @@ -0,0 +1,161 @@ +// Agent registry — tracks connected agents and their status. +// Port of gHashTag/trios:crates/trios-bridge/src/agents.rs +// (at 8b229e9e9489ee, 116 lines) — Roadmap Stage 6, goal #4548, target +// t27c gen-rust. +// +// The original is a thread-safe agent map: new builds the empty +// registry; register inserts an agent keyed by its id with the Idle +// status, no task, the connected-at stamp and the last heartbeat — +// the capabilities ride along unused (the comment names future +// routing); update_status rewrites BOTH the status and the task and +// refreshes the heartbeat, but ONLY for a registered agent; unregister +// removes the key; list answers every entry's info; agent_ids answers +// the keys alone (for broadcast); and is_registered answers the +// membership gate. +// +// The RwLock, the HashMap and the clock are the callers' plumbing; +// the decisions — the empty start, the Idle+no-task+stamped insert, +// the both-fields update gated on membership, the heartbeat refresh on +// every update, the remove, the info-vs-ids answer pair, and the +// membership gate — are ported here as pure functions over a +// presence-flag registry view and tested, including the original's +// own register/list, update_status and unregister tests (issue #6078, +// "Port the decision, not the plumbing"). + +module port::trios_bridge_agents; + +// --------------------------------------------------------------------------- +// Original lines 23-27: `new` — the registry starts EMPTY. +// --------------------------------------------------------------------------- + +pub fn AgentRegistry_new() -> usize { + return 0; +} + +// --------------------------------------------------------------------------- +// Original lines 30-44: `register` — the insert shape: the agent keyed by +// its id, the Idle status, no task, the connected-at stamp and the last +// heartbeat set to the same now; the capabilities ride along unused. +// --------------------------------------------------------------------------- + +pub fn register_initial_status() -> str { + return "idle"; +} + +pub fn register_initial_task_present() -> bool { + return false; +} + +pub fn register_stamps_heartbeat() -> bool { + return true; +} + +pub fn register_capabilities_used() -> bool { + return false; +} + +pub fn AgentRegistry_register(agent_registered_count: usize) -> usize { + return agent_registered_count + 1; +} + +// --------------------------------------------------------------------------- +// Original lines 47-54: `update_status` — BOTH the status and the task +// rewrite, the heartbeat refreshes, and ONLY a registered agent answers; +// an unknown id changes nothing. +// --------------------------------------------------------------------------- + +pub fn AgentRegistry_update_status(agent_registered: bool) -> bool { + return agent_registered; +} + +pub fn update_rewrites_both_fields() -> bool { + return true; +} + +pub fn update_refreshes_heartbeat() -> bool { + return true; +} + +// --------------------------------------------------------------------------- +// Original lines 57-59: `unregister` — the remove by key. +// --------------------------------------------------------------------------- + +pub fn AgentRegistry_unregister(agent_registered_count: usize) -> usize { + if (agent_registered_count > 0) { + return agent_registered_count - 1; + } + return 0; +} + +// --------------------------------------------------------------------------- +// Original lines 62-69: `list` — every entry's INFO; original lines +// 72-74: `agent_ids` — the keys ALONE, for broadcast. +// --------------------------------------------------------------------------- + +pub fn AgentRegistry_list(registered_count: usize) -> usize { + return registered_count; +} + +pub fn AgentRegistry_agent_ids(registered_count: usize) -> usize { + return registered_count; +} + +// --------------------------------------------------------------------------- +// Original lines 77-79: `is_registered` — the membership gate. +// --------------------------------------------------------------------------- + +pub fn AgentRegistry_is_registered(agent_registered: bool) -> bool { + return agent_registered; +} + +// --------------------------------------------------------------------------- +// Tests: every decision above asserted against values read from the +// original's own tests (lines 86-115). +// --------------------------------------------------------------------------- + +test new_starts_empty { + assert(AgentRegistry_new() == 0); +} + +test register_shape_and_count { + assert(register_initial_status() == "idle"); + assert(!register_initial_task_present()); + assert(register_stamps_heartbeat()); + assert(!register_capabilities_used()); + assert(AgentRegistry_register(0) == 1); + assert(AgentRegistry_register(1) == 2); +} + +test update_gated_on_membership { + assert(AgentRegistry_update_status(true)); + assert(!AgentRegistry_update_status(false)); + assert(update_rewrites_both_fields()); + assert(update_refreshes_heartbeat()); +} + +test unregister_removes { + assert(AgentRegistry_unregister(1) == 0); + assert(AgentRegistry_unregister(0) == 0); +} + +test list_and_ids_answer_the_registered { + assert(AgentRegistry_list(2) == 2); + assert(AgentRegistry_list(0) == 0); + assert(AgentRegistry_agent_ids(2) == 2); + assert(AgentRegistry_agent_ids(0) == 0); +} + +test membership_gate { + assert(AgentRegistry_is_registered(true)); + assert(!AgentRegistry_is_registered(false)); +} + +test original_flow_register_update_unregister { + // The original's own flow: two registers list 2; the busy update + // rewrites status and task for a registered agent; the unregister + // empties the list. + assert(AgentRegistry_register(AgentRegistry_register(0)) == 2); + assert(AgentRegistry_list(2) == 2); + assert(AgentRegistry_update_status(true)); + assert(AgentRegistry_list(AgentRegistry_unregister(1)) == 0); +}