docs(link): the cutover, and the counter two branches bumped at once (Phase 9b)
Records the Asset Bridge `edge -> main` cutover: §16's 9b row, §17.15's four org-lead decisions, and the `PARSER_VERSION` correction those forced through §7, §10.4 and `SPAWN_ATLAS.md`. ## The number had to move, and the reason generalises §10.4 wrote phase 7's canonical-read-order bump as `PARSER_VERSION` 4 -> 5. `main` had meanwhile bumped 4 -> 5 for the `UniqueId` fix and RELEASED it as v1.2.2. Same number, different derivation. An install that imported under v1.2.2 stores 5, so a phase-7 build also declaring 5 is read as current by `currentParser` and never re-reads -- the exact failure the constant exists to prevent, reached through a merge instead of through forgetting to bump. Two long-lived branches bumping one counter for different reasons is a defect the counter cannot see; only the merge can. The docs now carry both meanings -- 5 as main's released one, 6 as phase 7's -- rather than collapsing them. ## The cutover is five repos `installer`'s `edge` carried 9a's `libgdiplus` `doctor` check, and `SHARD_PREREQS.md` on `main` had been describing that check as existing since phase 1. §16's 9b row named four repos and would have left an operator-facing doc naming a check in no released binary. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
This commit is contained in:
49
link/v8.md
49
link/v8.md
@@ -847,7 +847,7 @@ nothing.
|
||||
|
||||
## 7. The parser version applies here too
|
||||
|
||||
`spawnAtlasSource.js` carries `PARSER_VERSION` (currently 5) and the cliloc source carries its own,
|
||||
`spawnAtlasSource.js` carries `PARSER_VERSION` (currently 6) and the cliloc source carries its own,
|
||||
both counted as drift so that a corrected parse reaches an install whose files never change. The
|
||||
asset pipeline inherits the rule and needs it more, not less: a fixed hue application or a
|
||||
corrected frame offset changes the bytes we derive from files that are byte-identical.
|
||||
@@ -1185,8 +1185,11 @@ filesystem reader walks each decoration directory with `localeCompare` while the
|
||||
relative paths, and those diverge the moment a directory mixes cases.
|
||||
|
||||
So `buildFromFiles` sorts by label, ordinally, once, whatever order the files arrived in — and
|
||||
`PARSER_VERSION` goes **4 → 5**, because that is identical input producing a different answer for a
|
||||
handful of types, which is exactly what that number exists to push through the hash gate. The
|
||||
`PARSER_VERSION` bumps, because that is identical input producing a different answer for a
|
||||
handful of types, which is exactly what that number exists to push through the hash gate. It was
|
||||
written here as **4 → 5** and the cutover renumbered it to **6**: `main` released a *different* 5
|
||||
meanwhile (a spawn point keeping its `UniqueId`, v1.2.2), and an install already storing 5 would
|
||||
have read a phase-7 build declaring 5 as current and never re-read. See §17.15. The
|
||||
**source fingerprint also moved to raw bytes** at both ends: hashing decoded text hashes a UTF-8
|
||||
*re-encoding*, which is identical for valid UTF-8 and different for a file that is not, and one
|
||||
Latin-1 character in a creature name would have made the drift gate report a change on every import
|
||||
@@ -1676,7 +1679,7 @@ disagree, so a split bump means the next bundle silently fails to compose.
|
||||
|---|---|
|
||||
| `servuo-plugins/` | Extraction over ServUO's own `Ultima` (§4), the cliloc decompressor (§9), body resolution (§8), the request handlers, `overlay.toml` |
|
||||
| `link/` | Six command families forwarded, the REST surface, **the inbound line cap (§3.3)**, `PROTOCOL_VERSION`. **Nothing in phases 4 or 5** — `assets_call` forwards a request body verbatim and `respond_assets` returns the reply verbatim, so a new key family and a new reply field both pass through untouched |
|
||||
| `module-uo/` | Client calls, asset store, the atlas source backend (§10), cliloc ingest, admin surface. Phase 7 also bumped `PARSER_VERSION` 4 → 5 (§10.4) |
|
||||
| `module-uo/` | Client calls, asset store, the atlas source backend (§10), cliloc ingest, admin surface. Phase 7 also bumped `PARSER_VERSION`, written as 4 → 5 and renumbered to **6** at the cutover (§10.4, §17.15) |
|
||||
| `website/` | Almost none — `ctx.uploads` already suffices (§12). Phase 2 deleted `server/tools/cliloc-export/`, the converter this protocol retires |
|
||||
| `docs/` | This file; rewrite `CLILOCS.md` §Converting and `SPAWN_ATLAS.md` §Artwork + §Configuring; **delete `UOFIDDLER.md`**; add the libgdiplus prerequisite to `SHARD_PREREQS.md` (§4.4). Phase 9a added the rest: `INTEGRATION.md` to protocol 8, an operator entry in `UPGRADE_NOTES.md`, the index rows, and the prerequisite + `doctor` row in `installer/INSTALL.md` |
|
||||
| `installer/` | A `doctor` check for libgdiplus on Linux hosts (§4.4) — **built in phase 9a**, Linux-only (a Windows host has nothing to check, so there is no row rather than a row saying "not applicable") and a `⚠` rather than a `✗`, because names and the spawn atlas have no pixels in them. Bundle pairing already enforces §15 |
|
||||
@@ -1696,10 +1699,10 @@ disagree, so a split bump means the next bundle silently fails to compose.
|
||||
| 4 | **DONE 2026-09-11.** The UOP animation decoder (§4.3, §4.9): `BridgeUop` + a PNG encoder that never touches `System.Drawing`, wired in beneath the legacy reader. **Two of the eight player bodies turned out to exist** (gargoyles 666/667); the other six are in no client file, and ghost ids left the player-body set (§5.2, §17.9). The same fallback added **233 other bodies**: the catalogue is **1,022 rows, 1,409 ms cold**, and all six player bodies have art for the first time. `EXTRACTOR_VERSION` 1 → 2 | servuo-plugins |
|
||||
| 5 | **DONE 2026-09-11.** Item statics and land on demand (§11.1): the `static` and `land` families, hue applied on the shard from `tiledata.mul`, the byte-bounded art cache, `assets.fetch` made family-aware, `families` on `assets.sources`. Website side: the warm pass, per-row `catalog` staleness, and pictures on the marketplace and the character sheet. **39,189 statics and 4,244 land tiles served; the only refusals are the 9,963 + 12,140 empty index slots §4.5 predicted.** Two traps found — the library's bitmap cache poisons a hued sprite, and `PartialHue` decides the picture from a file only the shard has. Protocol stays 8; `EXTRACTOR_VERSION` stays 2 | servuo-plugins, module-uo |
|
||||
| 6 | **DONE 2026-09-11, and not what this row said.** The measurement came first and changed the phase: a complete one-direction animation set is **174,453 frames / 281.5 MB**, not the ~119,000 estimated, and **the site displays still pictures** — so the deep keys and the bulk-fill switch were **not built** (§11.2, org lead 2026-09-11). What shipped is what the still-picture site was missing: the **73 bodies with no art at action 0 and real art deeper** (a horse at `body/820/a23`), the catalogue key carrying its action, the atlas join that reads it, and §4.10's per-body **action ceiling** — without which the fallback walk itself would serve **452 validated pictures of the next body**. Catalogue **1,022 → 1,095**; `EXTRACTOR_VERSION` 2 → 3; protocol stays 8 | servuo-plugins, module-uo |
|
||||
| 7 | **DONE 2026-09-14.** The atlas over the sidecar (§10); shared-filesystem requirement retired. The measurement came first again and changed the shape: `tree/<label>` → bytes **cannot work** — `Spawns/trammel.xml` is 4.03 MB against a 1 MiB line cap — so a file crosses as **512 KiB chunks, each gzipped**, which is §5's depth scheme paying for itself a second time (§10.1). It is a `tree` **family** on `assets.fetch` rather than §14's separate commands, with `assets.manifest` generalised to match and its **own consent, `Bridge.TreeEnabled`** (§10.2) — so `link` needed nothing for the third phase running. **141 files / 11.9 MB / 158 chunks / 3 pages / 1.33 MB on the wire / 512 ms**, and the atlas built over the bridge is identical to the one built off the disk. Two defects, each found by a different harness: an empty `catalog` refusing every fetch, and `GZipStream` emitting **nothing** for the two empty files stock ServUO ships (§10.3). `PARSER_VERSION` 4 → 5 for one canonical read order (§10.4); protocol stays 8; `EXTRACTOR_VERSION` stays 3 | servuo-plugins, module-uo |
|
||||
| 7 | **DONE 2026-09-14.** The atlas over the sidecar (§10); shared-filesystem requirement retired. The measurement came first again and changed the shape: `tree/<label>` → bytes **cannot work** — `Spawns/trammel.xml` is 4.03 MB against a 1 MiB line cap — so a file crosses as **512 KiB chunks, each gzipped**, which is §5's depth scheme paying for itself a second time (§10.1). It is a `tree` **family** on `assets.fetch` rather than §14's separate commands, with `assets.manifest` generalised to match and its **own consent, `Bridge.TreeEnabled`** (§10.2) — so `link` needed nothing for the third phase running. **141 files / 11.9 MB / 158 chunks / 3 pages / 1.33 MB on the wire / 512 ms**, and the atlas built over the bridge is identical to the one built off the disk. Two defects, each found by a different harness: an empty `catalog` refusing every fetch, and `GZipStream` emitting **nothing** for the two empty files stock ServUO ships (§10.3). `PARSER_VERSION` bumped for one canonical read order — written as 4 → 5, renumbered to **6** at the cutover (§10.4, §17.15); protocol stays 8; `EXTRACTOR_VERSION` stays 3 | servuo-plugins, module-uo |
|
||||
| 8 | **DONE 2026-09-14.** The admin surface (§12.2): `Admin → Client Files` — one page over all three planes, because they come off one client and change on one event. The cliloc pair had had no UI since phase 2, which on a bridge install meant `curl` was the only way to import 67,496 names. §14's "activity view" is the **last import's own summary** rather than a filtered feed, which kept the phase to one repo (org lead, 2026-09-14). The walk imported **1,095 portraits in 3.5 s, warmed 313 item pictures in 0.6 s and reloaded 67,496 cliloc rows in 1.7 s** against a real shard — and found **two deletions nobody could see before a screen put the numbers together**: the body import diffing its manifest against *every* family's rows, which staged all 313 item pictures for deletion, and an approved vanish that unlinked the sprite and kept the row, so the key came back for review forever. Both fixed here; `EXTRACTOR_VERSION` and the protocol are untouched | module-uo |
|
||||
| 9a | **DONE 2026-09-14.** The docs pass and the acceptance walk (§17.13). `INTEGRATION.md` carried to protocol 8 — it had stopped at **6**, contradicted itself, and documented none of this plane; the operator-facing upgrade note; the index rows; the installer's `libgdiplus` `doctor` check and the kit's chapter note, both promised by §15 and built by no phase. The walk wiped every asset row and every imported sprite and went through it as a new operator: **1,095 portraits in 3.18 s, 67,496 names in 1.42 s, 313 item pictures in 1.38 s, the atlas over the bridge in ~2.0 s, and an Update with no drift answered in 0.99 s**. Two defects (§17.14), the larger of them **not ours and released** — Events phase 12b's `unique_id` has been failing every spawn-atlas import on every upgraded install since v1.2.0, which only a walk on a rig whose tables predate it could see | docs, module-uo, installer, integration-kit, servuo-plugins |
|
||||
| 9b | The `edge → main` cutover: `link`, `servuo-plugins`, `module-uo`, `website`, in dependency order. **`module-uo`'s `edge` is behind its `main`** — it is missing #35, the fix that keeps the atlas's `UniqueId`, so the walk measured 0 of 6,455 spawners carrying one. Sync `main` into `edge` first or the cutover ships a regression | link, servuo-plugins, module-uo, website |
|
||||
| 9b | **DONE 2026-09-14.** The `edge → main` cutover, and it is **five repos, not four** — `installer`'s `doctor` check was on `edge` too, and `SHARD_PREREQS.md` on `main` had been describing it as if it shipped (§17.15). Order: the `module-uo` sync first, then `link` + `servuo-plugins` **together** because `bundle.yml` Gate 1 refuses a protocol-mismatched pair, then `website`, `module-uo`, `installer`. The sync found the one real conflict in the whole cutover: both branches had bumped `PARSER_VERSION` **4 → 5** for different reasons and main's 5 was **released**, so keeping it would have made phase 7's re-read unreachable on every existing install — the merged value is **6** (§10.4). Core's entire share of eight phases is a **deletion**: `MODULE_API` never moved, because `ctx.uploads` was already exposed | link, servuo-plugins, module-uo, website, installer |
|
||||
| 9c | runicgateway.com: `platform.json`'s protocol 7 → 8 and the bundle triple, and any prose the bridge changed. Must follow 9b — `checkFacts.mjs` reads the protocol from `link`'s `main` | runicgateway.com |
|
||||
|
||||
Phase 0 exists because §4 chose to call code that can take the shard down if it is wrong, and the
|
||||
@@ -1913,3 +1916,37 @@ in the document.
|
||||
upgraded install has failed outright since **v1.2.0** with `Unknown column 'unique_id'`: no
|
||||
bestiary refresh, no spawn map, no champion altars. The cost of the decision is that operators
|
||||
on a released v1.2.x keep that until the Asset Bridge cuts over.
|
||||
|
||||
15. **The cutover's four questions — settled 2026-09-14.** Put to the org lead before anything
|
||||
merged, because the survey of `edge` against `main` turned up two things §16's row did not
|
||||
anticipate and two the row could not decide.
|
||||
|
||||
- **Five repos, not four.** `installer`'s `edge` carried phase 9a's `libgdiplus` `doctor`
|
||||
check, and `SHARD_PREREQS.md` on `main` had been describing that check as existing since
|
||||
phase 1. A four-repo cutover leaves an operator-facing doc on `main` naming a check that is in
|
||||
no released binary, so the installer goes over with the rest and publishes a release — which
|
||||
is the only way the check reaches a host.
|
||||
- **`PARSER_VERSION` resolves to 6, with both notes kept.** This is the one genuine conflict in
|
||||
the cutover and the most instructive thing in the phase. `edge` bumped 4 → 5 for phase 7's
|
||||
canonical read order; `main` bumped 4 → 5 for the `UniqueId` fix and **released it as
|
||||
v1.2.2**. The two numbers are the same and the two derivations are not. Keeping 5 would have
|
||||
left every install that imported under v1.2.2 storing 5, `currentParser` calling a phase-7
|
||||
build current, and phase 7's change reaching nobody already running — the exact failure the
|
||||
constant exists to prevent, arrived at through a merge rather than through forgetting to bump.
|
||||
**Two long-lived branches bumping one counter for different reasons is a defect the counter
|
||||
cannot see; only the merge can.** Both notes stay in the version log, 5 as main's released
|
||||
meaning and 6 as phase 7's, with the renumber explained in place rather than collapsed.
|
||||
- **The re-verify is the bundle, not a second walk** (departing from the Event System's
|
||||
cutover, which rebuilt the whole released-artefact rig). 9a had already walked the feature end
|
||||
to end on a real shard one day earlier, against the same commits; what a release path can
|
||||
break that a dev rig cannot is the sidecar/overlay **pairing**, so that is what gets checked.
|
||||
- **`module-uo`'s `ci/core-ref.json` stays where the sync puts it** (`655fbf3f`). `website`'s
|
||||
`main` moves in this cutover but `MODULE_API_VERSION` does not, and the pin asserts an API
|
||||
version, not a tip. Leaving it alone also keeps the frozen-manifest job — the one thing that
|
||||
can answer whether the module's claimed URLs are the URLs it serves — from going red in the
|
||||
middle of a five-repo window for a reason unrelated to any of it.
|
||||
|
||||
The ordering itself was not a question: `link` and `servuo-plugins` must land **together**,
|
||||
because `bundle.yml` Gate 1 refuses a sidecar/overlay pair that disagrees on protocol and
|
||||
whichever lands alone leaves a compose that cannot run. That is not a prediction — it is what
|
||||
happened at the Event System's cutover, compose run 102.
|
||||
|
||||
@@ -378,7 +378,7 @@ whatever an older build derived, indefinitely, and a deploy that corrects the
|
||||
parse would never reach the data. A version mismatch counts as drift, so the
|
||||
correction lands on the next boot without an operator having to know it happened.
|
||||
|
||||
It is **5** as of Protocol 8 phase 7: the source files are now parsed in one
|
||||
It is **6** as of Protocol 8 phase 7: the source files are now parsed in one
|
||||
canonical label order whichever end read them. That matters because the parse is
|
||||
order-sensitive in one place — the decoration index keeps the first item id it
|
||||
sees for a type, and the two readers sorted a nested directory differently, so the
|
||||
@@ -386,6 +386,15 @@ same tree could yield a different preview graphic depending on how it arrived.
|
||||
Identical files, a different answer for a handful of types: precisely what this
|
||||
number exists to push through the hash gate.
|
||||
|
||||
That change was written as **5** while 5 was being released from `main` meaning
|
||||
something else — a spawn point keeping its `UniqueId` (v1.2.2) — so the Asset
|
||||
Bridge cutover renumbered it to 6. The renumber is the mechanism working rather
|
||||
than bookkeeping: an install that imported under v1.2.2 already stores 5, so a
|
||||
build declaring 5 for a *different* derivation would have been called current and
|
||||
the correction would have reached nobody already running. Two branches bumping
|
||||
the same counter for different reasons is the one way this gate can be defeated,
|
||||
and a merge is where it has to be caught.
|
||||
|
||||
The **source fingerprint is taken over raw bytes** at both ends for the same
|
||||
reason. Hashing the decoded text would hash a UTF-8 *re-encoding* of the file —
|
||||
identical for valid UTF-8, and different for a file that is not, because an
|
||||
|
||||
Reference in New Issue
Block a user