fix(uo-link): pin protocol 4, the version this build actually speaks
All checks were successful
PR Checks / client-build (pull_request) Successful in 24s
PR Checks / server-tests (pull_request) Successful in 28s
PR Checks / frozen-manifest (pull_request) Successful in 40s

The protocol-4 cutover moved `link`'s PROTOCOL_VERSION, the overlay's
`overlay.toml` and this module's ingest — `guild.roster` and `guild.leave`
landed with the Teams cutover — but left both of this module's pin sites at 3.

A fresh install therefore came up speaking 3 to a protocol-4 sidecar, and a
sidecar answers a stale client with `409 protocol version mismatch` rather than
mis-parsing it. The failure is total and silent: every REST read fails, the WS
closes on ws.hello, and the operator sees an empty marketplace, an empty guild
board and no shard status, with the cause only in the server log. It cleared
only when an admin edited the number by hand in Admin → Shard.

Found while standing up a demo deployment for the marketing site's screenshots.

- `DEFAULT_PROTOCOL` → 4 (the constant used before an admin has saved anything)
- the `uo_link_config.protocol` column default → 4, at both declaration sites
- a protocol-4 one-shot mirroring the protocol-3 one, guarded by its own marker
  so an operator who deliberately pins an older sidecar stays pinned, and
  written `protocol < 4` so an install that never took the protocol-3 migration
  is carried the whole way rather than one step
- three regression tests: the column default, the marker ordering, and the
  `< 4` predicate

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-24 19:17:51 -05:00
parent 6fca1cebf4
commit 7f7d4578ce
3 changed files with 94 additions and 3 deletions

View File

@@ -10,7 +10,14 @@ const { secretBox } = require('../../core')
// The wire protocol this build speaks (link/sidecar/src/main.rs PROTOCOL_VERSION).
// Only used before an admin has saved anything — the stored row wins once it exists,
// and UOLINK_PROTOCOL still overrides for an operator running an older sidecar.
const DEFAULT_PROTOCOL = Number(process.env.UOLINK_PROTOCOL) || 3
//
// This says 4 because this build handles protocol 4's frames: `guild.roster` and
// `guild.leave` ingest landed with the Teams cutover. It said 3 for a while after
// that, which is the bug this constant is now the fix for — a FRESH install pinned
// 3, the sidecar answered `409 protocol version mismatch` to every REST call, and a
// new deployment read nothing from its shard until an admin edited the number by
// hand in Admin → Shard. See the matching cutover in db/schema.sql.
const DEFAULT_PROTOCOL = Number(process.env.UOLINK_PROTOCOL) || 4
function toSafe(row) {
if (!row) {