Files
buzz/desktop/fixtures/relay-url-normalization.json
b94c6e9ed3 fix(desktop): harden the relay-edit and repos-map concurrency edges
Resolve the review findings on the lazy multi-workspace agent stack
(review of f3baa96) — four hardening fixes, no behavior change on the
happy path:

- Serialize apply_workspace's .repos-dirs.json upsert on
  managed_agents_store_lock. A community relay edit fires
  rebind_agent_relay (which moves map entries under that lock) nearly
  simultaneously with the reinit apply, and both are read-modify-writes
  of the same file — unserialized, one side's update could be lost.
- Write .repos-dirs.json via temp file + rename. A crash mid-write left
  truncated JSON, which read_repos_dir_map degrades to an empty map —
  silently sending spawns back to the shared REPOS fallback, exactly
  the cross-workspace hazard the map exists to prevent.
- Pin the Rust/TS relay-URL normalizers together with a shared fixture
  (desktop/fixtures/relay-url-normalization.json) consumed by both
  relay tests and agentRelayScope.test.mjs, so an edit that lands on
  only one side fails the other side's tests instead of shipping a
  scoping skew. Both normalizers' doc comments now point at it.
- Sequence updateCommunity's state commit (and the reinit it triggers)
  after the rebindAgentRelay IPC settles. Fire-and-forget let
  apply_workspace(newUrl) race ahead of the rebind: activation ran
  while start-on-launch agents were still pinned to the old URL and
  marked the relay activated for the session, silently skipping them
  until app relaunch. A rebind failure still commits — pins stay
  recoverable by re-editing the community.

The fifth finding (stale "running in other communities" count) was
already resolved by the slow cross-community poll tier added in
0f8fa7e. Split relay.rs's inline test module into relay/tests.rs
(file-size guard, mirroring repos/tests.rs).

Tested: cargo test --manifest-path desktop/src-tauri/Cargo.toml (1435
passed, incl. the new temp-file and fixture-agreement tests), clippy
--all-targets -D warnings, rustfmt, desktop pnpm test (2955 passed,
incl. the new fixture-agreement test), tsc --noEmit, biome check,
pnpm check:file-sizes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
2026-07-17 21:06:51 -04:00

48 lines
1.3 KiB
JSON

[
{
"input": "wss://relay.example.com",
"canonical": "wss://relay.example.com",
"note": "already canonical"
},
{
"input": " wss://relay.example.com// ",
"canonical": "wss://relay.example.com",
"note": "surrounding whitespace and trailing slashes are cosmetic"
},
{
"input": "WSS://Relay.Example.COM:3000/Path",
"canonical": "wss://relay.example.com:3000/Path",
"note": "scheme and authority are case-insensitive (RFC 3986); the path is not"
},
{
"input": "WS://RELAY-A.example.com:3000/",
"canonical": "ws://relay-a.example.com:3000",
"note": "ws scheme and port survive; case and trailing slash fold away"
},
{
"input": "wss://Relay.Example.com/Path?Query=Value",
"canonical": "wss://relay.example.com/Path?Query=Value",
"note": "path and query are preserved case-sensitively"
},
{
"input": "wss://relay.example.com/path//",
"canonical": "wss://relay.example.com/path",
"note": "trailing slashes after a path are cosmetic too"
},
{
"input": "not-a-url/",
"canonical": "not-a-url",
"note": "schemeless values pass through trim/slash cleanup only"
},
{
"input": "",
"canonical": "",
"note": "empty stays empty"
},
{
"input": " ",
"canonical": "",
"note": "whitespace-only trims to empty"
}
]