chore(cutover): sync main into edge before the Asset Bridge cutover (Phase 9b, 0 of 5) #43

Merged
whitlocktech merged 5 commits from chore/cutover-sync-main into edge 2026-09-14 23:08:51 +00:00
Member

Step 0 of 5 of the Asset Bridge edge → main cutover (docs/link/v8.md §16 row 9b). Nothing
here is Asset Bridge work — this is the repair that has to land before edge → main, and it is
the only repo in the cutover that needs one.

edge was behind main, and the cutover would have reverted a released fix

Module-uo's edge was missing two commits that had gone to main during the Event System's own
cutover window:

  • #35fix(atlas): keep the UniqueId, and make a landmark value name one landmark. Released
    in v1.2.2.
  • ea63ad0 — the Event System's ci/core-ref.json re-pin from a website edge sha back to
    main.

Phase 9a's acceptance walk measured the consequence directly: 0 of 6,455 spawners carried a
UniqueId on an edge rig, even after the missing unique_id column had been added. Merging
edge into main in that state ships the regression.

The one conflict, and why the number had to move

Both sides bumped PARSER_VERSION 4 → 5, for different reasons, and main's 5 is released:

5 means
main (v1.2.2) a spawn point actually keeps its UniqueId — version 4 said it did and the aggregator still dropped it
edge (phase 7) source files are parsed in one canonical label order, so a tree read over the bridge and the same tree read off a disk cannot derive a different decoration graphic

Keeping 5 would have made phase 7's change unreachable on every install that already imported.
sameSources gates on the tree hash, currentParser on the stored number; a v1.2.2 install stores
5, so a phase-7 build also declaring 5 is called current and never re-reads. That is exactly the
trap this constant exists to defeat, so the merged file is PARSER_VERSION = 6 carrying both
notes — 5 as main's released meaning, 6 as phase 7's — with the renumbering explained in place
rather than silently collapsed.

Org lead's call, 2026-09-14.

Everything else

Merged clean. #35's files (server/config/uoEventActions.js, server/test/uoEventActions.test.js)
come across verbatim — verified by diffing the merge result against origin/main for those
paths, which is empty. ci/core-ref.json takes main's value (655fbf3f, a main sha carrying
MODULE_API 1.10.0) and stays there; website's main moves later in this cutover but its
MODULE_API does not, so there is nothing for the pin to chase.

Verification

  • npm test --prefix server723 pass, 0 fail
  • npm run build --prefix client then npm test --prefix client45 pass, 0 fail

AI disclosure

Authored with Claude Code (Claude Opus 5). Commits carry the Co-Authored-By trailer.

🤖 Generated with Claude Code

https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4

Step **0 of 5** of the Asset Bridge `edge → main` cutover (`docs/link/v8.md` §16 row 9b). Nothing here is Asset Bridge work — this is the repair that has to land **before** `edge → main`, and it is the only repo in the cutover that needs one. ## `edge` was behind `main`, and the cutover would have reverted a released fix `Module-uo`'s `edge` was missing two commits that had gone to `main` during the Event System's own cutover window: - **#35** — `fix(atlas): keep the UniqueId, and make a landmark value name one landmark`. Released in **v1.2.2**. - **ea63ad0** — the Event System's `ci/core-ref.json` re-pin from a website `edge` sha back to `main`. Phase 9a's acceptance walk measured the consequence directly: **0 of 6,455** spawners carried a `UniqueId` on an `edge` rig, even after the missing `unique_id` column had been added. Merging `edge` into `main` in that state ships the regression. ## The one conflict, and why the number had to move Both sides bumped `PARSER_VERSION` **4 → 5**, for different reasons, and main's 5 is **released**: | | 5 means | |---|---| | `main` (v1.2.2) | a spawn point actually keeps its `UniqueId` — version 4 said it did and the aggregator still dropped it | | `edge` (phase 7) | source files are parsed in one canonical label order, so a tree read over the bridge and the same tree read off a disk cannot derive a different decoration graphic | Keeping **5** would have made phase 7's change unreachable on every install that already imported. `sameSources` gates on the tree hash, `currentParser` on the stored number; a v1.2.2 install stores 5, so a phase-7 build *also* declaring 5 is called current and never re-reads. That is exactly the trap this constant exists to defeat, so the merged file is **`PARSER_VERSION = 6`** carrying both notes — 5 as main's released meaning, 6 as phase 7's — with the renumbering explained in place rather than silently collapsed. Org lead's call, 2026-09-14. ## Everything else Merged clean. #35's files (`server/config/uoEventActions.js`, `server/test/uoEventActions.test.js`) come across **verbatim** — verified by diffing the merge result against `origin/main` for those paths, which is empty. `ci/core-ref.json` takes main's value (`655fbf3f`, a `main` sha carrying MODULE_API 1.10.0) and stays there; website's `main` moves later in this cutover but its MODULE_API does not, so there is nothing for the pin to chase. ## Verification - `npm test --prefix server` — **723 pass, 0 fail** - `npm run build --prefix client` then `npm test --prefix client` — **45 pass, 0 fail** ## AI disclosure Authored with Claude Code (Claude Opus 5). Commits carry the `Co-Authored-By` trailer. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
wtclaude added 5 commits 2026-09-14 22:35:00 +00:00
chore(ci): the core pin comes home to main (Phase 16b cutover)
All checks were successful
PR Checks / client-build (pull_request) Successful in 18s
PR Checks / server-tests (pull_request) Successful in 23s
PR Checks / frozen-manifest (pull_request) Successful in 49s
ea63ad019c
`ci/core-ref.json` pointed at a website `edge` sha for the length of the Event
System window (org lead, 2026-09-04), because `api.registerEventActions` exists
only from MODULE_API 1.10.0: under the old `main` pin the frozen-manifest job's
`register()` threw and this module did not load at all, so the job would have
been red by construction for eight phases while a real regression hid behind it.

The cutover put 1.10.0 on `main` (website#199, 655fbf3f), so the pin returns to
a `main` sha -- and this is the same move that turns the Integration kit green,
since `checkCoreApi` asserts equality against whatever core this pin names.

`routes.manifest.json` needed NO regeneration. The frozen-manifest job's own
steps were run against this exact ref -- core's manifest alone, the module
installed, core's manifest again, then `frozenManifest.js --check` -- and it
answered `routes.manifest.json is current, 73 routes, all documented`. So the
file's own "commit both together" instruction had nothing to pair with this
time. website's `main` and `edge` are the identical tree (930422ff), which is
why the measurement taken on the branch holds for the merge.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
Reviewed-on: #34
Reviewed-by: Colby Whitlock <whitlocktech@gmail.com>
fix(atlas): keep the UniqueId, and make a landmark value name one landmark
All checks were successful
PR Checks / client-build (pull_request) Successful in 23s
PR Checks / server-tests (pull_request) Successful in 28s
PR Checks / frozen-manifest (pull_request) Successful in -21s
d6346996d3
Two defects the Phase 16b re-verify found in the released v1.2.1 bundle, both
of which make a shipped feature unusable and neither of which any test saw.

## The aggregator discarded the UniqueId

`shard_spawn_points.unique_id` was NULL on all 6,455 rows of a stock 57.4 tree.
`listSpawners` filters `unique_id IS NOT NULL`, so `uo.options.spawners` was an
empty dropdown -- and it is the ONLY option source for the Phase 12b
object-property leases, so no `Spawner.MaxCount` / `MinDelay` / `MaxDelay` lease
could be authored at all, with nothing on the form to say why.

Every part of the path was already right except one line. The spawn files carry
`<UniqueId>` (~6,374 of them), `parsePoints` returns it, the column exists and
the insert passes `p.uniqueId || null`. `buildAtlas` rebuilds each point from an
explicit field list and `uniqueId` was not on it -- the word appears nowhere in
that file. `PARSER_VERSION = 4`'s own note says "a spawn point keeps its
UniqueId, which is what a property lease targets", so the intent shipped as a
comment while the code dropped the field one function later.

`PARSER_VERSION` goes to 5 because the bump is the only thing that re-reads an
already-imported tree: `sameSources` compares the tree's hashes, which have not
changed -- only what is kept from them. Confirmed on the rig, where the boot
after the fix logged `spawn atlas refreshed` on an unchanged tree and the manual
import then correctly answered `unchanged`.

## A landmark option value named 23 places at once

A stock tree has 558 landmarks under 320 distinct `facet/name` pairs.
`Trammel/Entrance` is 23 different dungeons -- Blighted Grove, Covetous, Deceit,
Despise, Destard and so on -- and `landmarkPoint` resolved with `.find()`, so 22
of the 23 were unreachable. An author who picked "Entrance - Destard" got
Blighted Grove, and the run succeeded with no warning. The group was already the
disambiguator: it was shown in the dropdown and left out of the value.

The value is now `facet/group/name`, which is distinct across all 558.
`landmarkPoint` tries that form first and keeps the two-part read as a fallback,
because every event published before this fix stores `facet/name` and a
published version is immutable -- refusing to parse those would break runs
rather than correct them. The fallback keeps the old first-match behaviour
deliberately: it is imprecise in exactly the way it always was, and silently
relocating a live event's spawn point is worse than repeating a known
imprecision. A three-part value whose group is gone REFUSES rather than falling
back to the name, because it asked for one particular place.

## Verification

On the released-artefact rig (installer -> bundle 2026.09.10 -> stock 57.4 tree
-> protocol-7 sidecar -> core at main with this module):

  spawn points     6455 rows, 6364 with a unique_id   (was 0)
  uo.options.spawners   100 options, and `?q=orc` searches them   (was 0)
  uo.options.landmarks  558 options, 558 distinct values          (was 320)
  suite            625 pass, 0 fail

Each new test was confirmed to FAIL without its fix. The atlas one asserts the
field on the AGGREGATOR's output rather than the parser's, which is the whole
point of it -- and the test fixture had no `<UniqueId>` at all until now, which
is exactly why a green suite said nothing. The landmark one asserts an
INEQUALITY between two resolved points rather than a literal value string, so it
survives another change of format as long as two options still address two
places.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
Reviewed-on: #35
chore(cutover): sync main into edge before the Asset Bridge cutover
All checks were successful
PR Checks / client-build (pull_request) Successful in 21s
PR Checks / server-tests (pull_request) Successful in 27s
PR Checks / frozen-manifest (pull_request) Successful in -31s
f9bbc7a90d
`edge` was BEHIND `main` by two commits — Module-uo#35 (the atlas keeps its
`UniqueId`, and a landmark option value names one landmark) and the Event
System's core re-pin — so merging `edge` into `main` as the Asset Bridge
cutover would have REVERTED a released fix. Phase 9a's walk measured it:
0 of 6,455 spawners carried a `UniqueId` on an `edge` rig even after the
column existed.

## The one conflict, and why the number had to move

Both sides bumped `PARSER_VERSION` 4 -> 5, for different reasons, and main's
5 is RELEASED in v1.2.2: "a point keeps its `UniqueId`". `edge`'s 5 was
phase 7's canonical label order.

Keeping 5 would have made phase 7's change unreachable. `sameSources` gates
on the tree hash and `currentParser` on the stored number; an install that
imported under v1.2.2 already stores 5, so a phase-7 build declaring 5 would
be called current and would never re-read. That is precisely the trap this
constant exists to defeat, so the merged file carries BOTH notes: 5 is main's
released meaning, 6 is phase 7's, with the renumbering explained in place.

Everything else merged clean and keeps #35's files verbatim.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
whitlocktech merged commit 76c224fff1 into edge 2026-09-14 23:08:51 +00:00
whitlocktech deleted branch chore/cutover-sync-main 2026-09-14 23:08:52 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: RunicGateway/Module-uo#43
No description provided.