docs(link): roster members carry guild rank
Amends Protocol 4 in place rather than bumping it: the protocol has not reached `main`, and a bump is owed only once a protocol has been released. The roster shipped as the standard actor object, which carries no rank. Teams phase 2 found the consequence -- the website could learn leadership only from the board's single `leader` field, so it could name exactly one leader while a UO guild routinely has several, and TEAMS.md §2.5 treats multiple leaders as the normal case. Roster members now carry `rank` (0-4, 4 being Leader) plus `rankCliloc`, or `rankName` where a shard's custom rank definitions use literal names. Rank is on roster members only -- it is a property of a mobile's membership of THIS guild, not of the mobile, and every other actor the bridge writes is a bystander, a killer or a governor. Both files carry the trap this found, because it is the kind of thing a consumer gets wrong silently: **`PlayerMobile.GuildRank` returns Leader for anyone at GameMaster or above, whatever their real rank.** It is a gameplay convenience so staff can operate a guild stone, and the true value has no accessor -- so the bridge omits the rank entirely for a staff account rather than publish a leadership claim it knows is false. An absent rank therefore means "not known", and a consumer must read it as neither 0 (which silently demotes them) nor leadership (which republishes the lie). INTEGRATION.md also states the other half plainly for an outside integrator: `guild.update`'s single `leader` is the founder-leader, not the set of leaders, so "who leads this guild" is a read of the roster's ranks. Recorded too: the sidecar needs no change and no store migration, because it treats roster members as opaque values and never reads a field inside one. That is the forwarder design paying off, and it is worth having written down the next time someone adds a member field. Pairs with servuo-plugins (the emitter) and Module-uo (the ingest and the provider). Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
41
link/v4.md
41
link/v4.md
@@ -102,10 +102,11 @@ catch-up takes seconds instead of one sweep interval per batch.
|
||||
### 2.3 What a roster member carries
|
||||
|
||||
Each entry is the standard actor object — `serial`, `name`, `player`, plus `acct` when the mobile has
|
||||
an account and `webId` when that account is linked:
|
||||
an account and `webId` when that account is linked — **and the member's rank in this guild**:
|
||||
|
||||
```jsonc
|
||||
{"serial":"0x1F5","name":"Seed000A","acct":"seed_000","webId":"42","player":true}
|
||||
{"serial":"0x1F5","name":"Seed000A","acct":"seed_000","webId":"42","player":true,
|
||||
"rank":4,"rankCliloc":1062959}
|
||||
```
|
||||
|
||||
`acct` is **genuinely optional**: a `PlayerMobile` can have no `Account` at all, and the local test
|
||||
@@ -114,6 +115,42 @@ world contains such mobiles. Consumers must not assume it is present.
|
||||
These identity fields are emitted unconditionally, by design — the sidecar is a forwarder, and
|
||||
deciding who may see them is the website's job. See §4.
|
||||
|
||||
#### Rank
|
||||
|
||||
> **Added 2026-08-17, amending Protocol 4 in place.** The version is **not** bumped: Protocol 4 has
|
||||
> not reached `main`, and a protocol owes a bump only once it has been released. The roster shipped
|
||||
> without rank, and Teams phase 2 then found the consequence — the website could learn leadership
|
||||
> only from the board's single `leader` field, so it could name exactly one leader while a UO guild
|
||||
> routinely has several.
|
||||
|
||||
| Field | Shape | When |
|
||||
| --- | --- | --- |
|
||||
| `rank` | integer 0–4, 4 being Leader (`RankDefinition.Ranks`) | whenever the rank is known |
|
||||
| `rankCliloc` | integer — the cliloc the game names the rank with | when the rank's name is a cliloc, i.e. the five stock ranks |
|
||||
| `rankName` | string | when a shard's custom rank definition carries a literal name instead |
|
||||
|
||||
**Rank is on roster members only.** It is a property of a mobile's membership of *this* guild, not of
|
||||
the mobile, and every other actor the bridge writes is a bystander, a killer or a governor, where
|
||||
guild rank is meaningless.
|
||||
|
||||
**Only the raw rank is emitted, never a resolved label.** ServUO names the five stock ranks with
|
||||
clilocs (1062959–1062963) and ships no text for them, so the shard cannot produce "Warlord" without a
|
||||
client-file table it does not have. The website module does have one, and resolving a game term is
|
||||
its job in any case.
|
||||
|
||||
**An absent rank means "not known", and a consumer must not read it as 0 or as leadership.** It has
|
||||
one deliberate cause, which is the trap this amendment found: `PlayerMobile.GuildRank` returns
|
||||
`RankDefinition.Leader` for anyone at **GameMaster or above**, whatever their real rank. That is a
|
||||
gameplay convenience so staff can operate a guild stone, not a claim about who leads the guild — and
|
||||
the true value is in a private field with no accessor. So the bridge writes **no rank at all** for a
|
||||
staff account rather than publishing a leadership claim it knows to be false. A staff member who
|
||||
genuinely leads their guild therefore appears unranked, which is a visible gap rather than a lie on a
|
||||
public roster.
|
||||
|
||||
**The sidecar is unaffected.** It treats roster members as opaque values and never reads a field
|
||||
inside one, so a new member field needs no sidecar change and no store migration — which is the
|
||||
forwarder design (§3) doing its job.
|
||||
|
||||
---
|
||||
|
||||
## 3. The sidecar side
|
||||
|
||||
Reference in New Issue
Block a user