feat(shard): Protocol 2.0 cross-links — titles, guild, governor, houses

Phase 3: surface the new board data on existing character/user pages.

- Character sheet: render the char.profile titles block (fame/karma + skill +
  selected reward title; numeric clilocs skipped since the site has no cliloc
  table yet), plus "Guildmaster" and "Governor of <city>" chips.
- Char profile enrichment (player/admin /shard/char/:serial, one shared path):
  attach guild + governorOf from our own boards. Guild is LEADERSHIP-ONLY — it's
  verifiable from current board state, whereas guessing membership from stale
  guild.join events risks showing a wrong guild, so we return null instead.
- Admin user detail (/admin/users/:id): new "Standing" section (governorships
  held + guilds led) via GET /users/:id/shard/standing; Houses rows now show the
  registry fields (decay level, placement price, co-owner/friend counts) already
  returned by listHousesForAccounts.

Server 179/179, client build clean, swagger regenerated.

Refs .plans/protocol2-integration.md (Phase 3).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-17 12:45:15 -05:00
parent e9aa19a83d
commit 2957708bab
9 changed files with 229 additions and 3 deletions

View File

@@ -1255,6 +1255,18 @@ adminRouter.get(
validate,
usersShard.getOnline,
)
adminRouter.get(
'/users/:id/shard/standing',
// #swagger.tags = ['Admin · Users']
// #swagger.summary = 'A users shard standing — governorships held and guilds led (admin only)'
// #swagger.security = [{ "cookieAuth": [] }, { "bearerAuth": [] }]
// #swagger.parameters['id'] = { in: 'path', required: true, schema: { type: 'integer' }, description: 'User id.' }
/* #swagger.responses[200] = { description: 'Standing { governorOf, guildsLed }', content: { "application/json": { schema: { type: "object", additionalProperties: true } } } } */
/* #swagger.responses[404] = { description: 'Not found', content: { "application/json": { schema: { $ref: "#/components/schemas/Error" } } } } */
param('id').isInt(),
validate,
usersShard.getStanding,
)
// ── uo-link sidecar control (admin only) ──────────────────────────────────
// Connection config (base/ws URL + token + protocol + enabled) and the town