docs!: Protocol 3.0 cutover — the 3.0 documentation set #73
Reference in New Issue
Block a user
No description provided.
Delete Branch "edge"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What & why
Order 6 of the Protocol 3.0 plan (
v3.md§4) — theedge→maincutover, docs side. One of four PRs that merge together; that merge is thePROTOCOL_VERSION2 → 3 bump.maincurrently documents a v2 world. Everything 3.0 built has been documented onedgealongside the code, and this brings that set across in one go — 25 commits, 12 files:link/v3.md— the canonical 3.0 design: the audience ladder and the two locked visibility rules, the three new kinds, the spawn atlas, the cliloc finding, the §9 sequencing table and §11's verification procedure. It also carries the record of what the live system disagreed with the plan about, which is the part that does not survive in code: the four REST leaks the smoke test found, thecharacterName/ownerAcctclass of inert field rules,(long)on adoublebeing unchecked in C#,%/_being LIKE metacharacters that parameterization does not neutralize.link/INTEGRATION.md— catalog entries and consumer sections forworld.ruleset,points.boardandvendor.listing, and §2 rewritten for v3: header,/health,ws.hello, the 409 example, and what a v2 integration has to do to upgrade.website/SHARD_VISIBILITY.md,website/SPAWN_ATLAS.md,website/CLILOCS.md,website/MARKETPLACE.md— the admin/operator-facing guides.website/BACKEND_DESIGN.md— the new tables and routes, and the visibility framework written up as a security contract rather than a feature.link/PLAN.mdandlink/PROTOCOL_2.md— 3.0 phasing, and §10.4'sworld.systemsmarked superseded byworld.ruleset(its deferred VvV question is answered:VvV.cfg Enabled=True, Factions off), so no orphan kind is left behind.How it was tested
Prose. Each file landed with the code it describes and was corrected against what the live system actually did — several sections exist because the running shard contradicted the plan (six facets not thirteen, facet names disagreeing across sources, XmlSpawner directives inside type tokens, compressed cliloc files). The §2 version numbers and the 409 body were re-checked against the v3 sidecar binary, not against the plan.
Merge order
edge(the cutover docs), link #20 →edge, website #117 →edge.This PR's diff picks up #72 automatically once that merges.
Checklist
AI-assisted contributions (required)
Claude Code. I have reviewed and understandevery change, and take responsibility for it. AI-authored commits are
marked with a
Co-Authored-By/Assisted-Bytrailer.License
(GNU GPL v3.0 or later), and I have the right to contribute it.
The live five-rung smoke test of the visibility framework found that Part A enforced it on the SSE path and on /guilds + /governors, but not on the remaining public REST reads - so one event was projected live and served verbatim from stored history. link/v3.md gains 3.6.1 with the full list (the anonymous acct/webId leak on /feed, the flattened ownerAcct on /idoc, the dead `houses` field rules, /feed ignoring live config, the empty-allowlist fall-through, and the Date-to-{} projection bug), plus the rule it leaves behind: a read path that returns shard data and does not project is a bug, and every new Part B/C surface must gate its kind set on live config rather than on PUBLIC_KINDS. 3.5 also corrected: the table is NOT seeded on boot. An absent row means "use the compiled default", which keeps the defaults in one place instead of duplicating them into a seeder that could drift. BACKEND_DESIGN.md 6.5 records the same as a security contract: rule 1 locks a field by meaning rather than spelling; PUBLIC_KINDS is a module-load constant and must not answer per-caller questions; projectFeature walks arrays and plain objects only. SHARD_VISIBILITY.md gets the admin-facing version - that stored history answers the same way the live stream does, and that turning live updates off stops the push, not the reading. Co-Authored-By: Claude <noreply@anthropic.com>Protocol 3.0 order 2 (v3.md §5) is built across all four repos; this is its documentation half, plus the running progress record the plan was missing. v3.md - A progress table at the top and a State column on §9's sequencing table, so "what has landed" is answerable without reading four git logs. Part A (order 1) and world.ruleset (order 2) are marked done; the spawn atlas is next. - §5 gains the implementation notes worth keeping, chiefly: where a system's on/off state is DERIVED rather than configured, read the system's own static instead of inventing a .cfg key (Shadowguard has no Enabled key — it's the TOL expansion gate; Factions is `!ViceVsVirtueSystem.Enabled` by construction in stock ServUO). Also that the plugin CAN be compile-verified despite the "no standalone build" caveat, and how. INTEGRATION.md - The world.ruleset catalog entry and GET /ruleset, with the two things consumers get wrong: caps are in TENTHS (1000 = 100.0), and `connect` exists only if the operator set Bridge.PublicConnectAddress — the shard's real listen address is never published. - §2 now says plainly that v3 has NOT been bumped yet and what that means: sidecars on `edge` report 2 while already carrying some v3 kinds, so do not infer feature availability from the version during this window. PROTOCOL_2.md §10.4 - The deferred "which PvP system does this shard run?" is answered (VvV on, Factions off — and mutually exclusive by construction), and world.systems is marked superseded by world.ruleset, which carries the systems block it asked for. No orphan kind is left behind. BACKEND_DESIGN.md — the shard_ruleset table (why it is stored whole rather than normalized, and why no row means null rather than {}) and the public route. PROJECT_TREE.md is deliberately untouched: sync-project-tree regenerates it on push to main, so it updates itself at the v3 cutover. Co-Authored-By: Claude <noreply@anthropic.com>Protocol 3.0 §7 lands across servuo-plugins, link and website; this is the matching documentation. INTEGRATION.md - points.board in the event catalog: one frame per system, never a delta, no points.remove (the shard's system set is fixed at startup). Four gotchas called out, all of them things a consumer gets wrong by default: 1. maxPoints: 0 means UNCAPPED, not "zero allowed" — and on a real shard it is the COMMON case, so anything rendering points/maxPoints must special-case it or divide by zero on the happy path. 2. nameString is usually null, with a cliloc in nameNumber — so humanising the system key is the primary display path, not a fallback. 3. players counts players actually holding points, not table size: ten of the ~25 systems keep a zero-point row per character ever created, so the raw count would report the shard's whole census. 4. Entries carry serial + name only, never acct/webId. - The char.profile `points` block, and why `rank` is absent by default. - GET /points and /points/:system, including why 404 (never published) and 200-with-empty-top (published, nobody scored) are different answers. v3.md - B/2 marked done in both the progress table and §9. - NEW §7.5, "what the run against a real shard changed" — the same record §6.1 and §6.2 keep. Four corrections the plan could not have anticipated from reading PointsSystem.cs, the sharpest being that (long)double.MaxValue is an unchecked conversion yielding long.MinValue, which published "maxPoints": -9223372036854775808 on the first live sweep. Also records that GetEntry/GetPoints mutate the world on AutoAdd systems and so cannot be used in a read model, and the one deliberate deviation from §7.4: the visibility field rule must key on the wire's `name`, not the descriptive `characterName`, or it is silently inert. BACKEND_DESIGN.md — shard_points_boards (including why the top-N list stays in the payload and why listing orders by COALESCE(name, system)), plus the two new public routes. PLAN.md — 3.0 phasing brought current: the spawn atlas and points.board added to what has shipped, and the Points* keys noted in the config-key paragraph. PROJECT_TREE.md files are deliberately untouched — they are CI-generated and say so. Co-Authored-By: Claude <noreply@anthropic.com>Protocol 3.0 §8.6 resolved as its own website-only change, landing ahead of §8 so the marketplace ships with real item names. Matching documentation for website #TBD. NEW website/CLILOCS.md — operator-facing: why the conversion step exists, how to convert, how to configure the path, the refresh contract, what gets stored and how names are applied. link/v3.md §8.6 rewritten. Two things in the original recommendation turned out to be wrong, and both are recorded because the reasoning generalises: 1. The committed db/data/clilocs.json artifact predates the Part C corrections (§6.1) and violates both — no committed snapshot of derived content, and nothing EA-derived ever shipped. UO's strings are EA's, exactly as the creature sprites are. 2. "scripts/buildClilocs.js reads the UO client's Cliloc.enu" is not possible. EVERY current client ships its cliloc files compressed (first DWORD's high byte 0x8E, the Mythic container); the plain layout is what those files looked like before that change, and parsing one as the other does not fail cleanly — it yields ~19k records with negative ids, 1,722 distinct keys out of 19,508, one 62 KB "string", and a truncation somewhere in the middle. ServUO's own Ultima.StringList cannot read it either, so VendorSearch.GetItemName is already inert on such a shard and the work could not be pushed to the plugin. That second point also retires an open question in §8.2: the warning never to call GetItemName in the market sweep costs us nothing we could otherwise have had, because the in-game Vendor Search gump has the same gap. Three traps found by building it are recorded: StringList.SaveStringList RE-COMPRESSES on save (its output is byte-identical to its compressed input, because its purpose is round-tripping a file back into the client); trimming a text line before splitting silently drops the ~half of a table that is empty strings; and Number('') is 0, not NaN. Also updated: - Progress and §9 sequencing tables: order 5 split into 5a (this, website only) and 5b (the four-repo wire change). - website/BACKEND_DESIGN.md — shard_clilocs / shard_cliloc_meta, the three admin routes, and why there is no staged-approval flow and no public route. - link/INTEGRATION.md — the char.profile field note now says explicitly not to expect the shard to resolve clilocs, and points at CLILOCS.md. - §10 documentation obligations list CLILOCS.md. Documentation only. Every claim was written after the corresponding behaviour was observed running: the compressed-format finding and the parse failures come from the real client files on this machine, and the counts (123,490 parsed → 67,496 stored) and timings from importing them into the live MariaDB. PROJECT_TREE.md files are deliberately untouched — they are CI-generated by the sync-project-tree workflow and say so in their header. Co-Authored-By: Claude <noreply@anthropic.com>The v3 work added four shard features and an admin-configurable visibility framework the Android client knows nothing about. v3.md §10 deferred the app side as a follow-up; re-examining it before the cutover found the gap is wider than nav hiding: - no consumer for any of ruleset / leaderboards / market / atlas, - no `points` block on the character sheet (§7.3), - no cliloc-resolved item names (§8.6), and - shard nav gated on session role alone, so an admin who disables a feature or raises its audience leaves the app rendering entries that 404/403 into a generic error where the web client hides them. Scoped as PLAN.md §9 M11 in two PRs (the visibility rules + read-model adds, then the four screens), with the traps a real shard exposes recorded inline: uncapped `maxPoints: 0`, cliloc-named boards with a null `nameString`, skill caps in tenths, the required market staleness banner, the market stream being off by default, atlas delays in seconds, and `points`-count vs `spawners`-list. edge → main is held until both land so web and app surface the same shard on the same day. Neither PR is coupled to the merge order — on a pre-v3 website every new route and /public/shard/features 404s and the app falls back to today's behavior — so holding the cutover is a schedule decision, not a technical dependency. Also records two things verified as already correct, so they are not re-derived: the app's SSE request rides the authenticated client (same audience rung as the same account on web), and every shard DTO is nullable-with-defaults (field projection cannot cause a decode failure). Co-Authored-By: Claude <noreply@anthropic.com>Records what the live Protocol 3.0 smoke test (ServUO + sidecar + website + AVD) turned up, so none of it has to be rediscovered. v3.md §5.3 — the ruleset `shard` field now falls back to the instance's own name when the shard publishes ServUO's stock "My Shard", why that is done at ingest rather than on read (the frame is also broadcast live), and why the backfill snapshot must go through the dispatcher instead of writing state directly: a direct call made it a second writer that skipped the normalization. v3.md §7.4 — an unscored board renders a placeholder row rather than a blank card, and why it is deliberately not shaped like a real entry. PLAN.md §9 M11 — `places` is a list of {facet,label,spawners,maxAlive} OBJECTS, not of place-name strings, and typing it `List<String>` makes the whole detail route fail to decode while the request itself returns 200. Adds the rule that came out of it: decode tests must feed real captured JSON, because the fakes build DTOs in Kotlin and can never catch a wire mismatch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U7CBg11prhLimL9iHSX1bPplacesshape, and two traps' (#76) from docs/protocol-3-smoke-findings into edge eab0a83f26