feat(bridge): protocol 9 — the world verbs, and the phase-7 owner fix (phase 13a) #9

Merged
whitlocktech merged 3 commits from feat/phase-13a-world into edge 2026-09-24 05:08:47 -05:00
Member

Phase 13a of module-rust. The plan and the as-built record are in docs/modules/rust/PLAN.md §28; the protocol is in docs/rust-link/PROTOCOL.md §15. The docs PR goes alongside.

What

  • The phase-7 owner fix (D85), as its own commit. EnsureGrant and EnsureGroupPermission now grant with a null owner and read the store back. A grant that did not land is reported in the new notLanded list and is not counted.
  • world.monuments, world.zone, world.place, world.revert and world.owned.
    • Zones are ZoneManager temporary zones owned by the bridge, with a required duration that the plugin enforces itself (D96, world.expired). They are re-created from the registry on every load and on every ZoneManager reload, because ZoneManager erases a plugin's zones when that plugin unloads (D92).
    • Crates and NPCs come from a plugin-held allowlist (D88: crates and NPCs, never vehicles). They go at a monument, named by kind plus instance and resolved when the step runs (D93), or at raw coordinates. The limit is 25 crates or 20 NPCs per step (D95; EventsMaxCrates / EventsMaxNpcs may lower it).
  • The world.json registry is keyed by the website's idempotency key, because this bridge has no at-most-once store. A repeated key is answered with the first call's ids and places nothing.
  • world.owned looks for each thing by net id. Crates survive a restart with the same net id and NPCs do not, so a boot id proves nothing. A wipe drops the registry.
  • EventsEnabled now gates every world write (D94). Revert, owned and monuments are never gated.
  • A revert refuses only something that exists and is not this run's. It never trusts ZoneManager's true.
  • overlay.toml protocol → 9.

A defect the walk found here, fixed

The link starts in Init, before the save loads. A mid-run restart had the website ask world.owned into an empty world, and the plugin pruned three live crates from its registry. World commands now answer not-ready until OnServerInitialized, and server.hello carries worldReady (PLAN.md §28.7).

Walked

On Oxide and on Carbon, through real core: each verb observed in the game, the lost answer, reload, restart, wipe, the switch and bounds, and the owner fix before and after (§28.6). node scripts/checkPlugin.js and its tests pass.

  • AI-assisted: Claude Code (Claude Opus 5.5)

🤖 Generated with Claude Code

https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY

Phase 13a of module-rust. The plan and the as-built record are in `docs/modules/rust/PLAN.md` §28; the protocol is in `docs/rust-link/PROTOCOL.md` §15. The docs PR goes alongside. ## What - **The phase-7 owner fix (D85)**, as its own commit. `EnsureGrant` and `EnsureGroupPermission` now grant with a `null` owner and read the store back. A grant that did not land is reported in the new `notLanded` list and is not counted. - **`world.monuments`, `world.zone`, `world.place`, `world.revert` and `world.owned`.** - Zones are ZoneManager temporary zones owned by the bridge, with a **required duration that the plugin enforces itself** (D96, `world.expired`). They are **re-created from the registry** on every load and on every ZoneManager reload, because ZoneManager erases a plugin's zones when that plugin unloads (D92). - Crates and NPCs come from a plugin-held allowlist (D88: crates and NPCs, never vehicles). They go at a monument, named by kind plus instance and resolved when the step runs (D93), or at raw coordinates. The limit is 25 crates or 20 NPCs per step (D95; `EventsMaxCrates` / `EventsMaxNpcs` may lower it). - **The `world.json` registry is keyed by the website's idempotency key**, because this bridge has no at-most-once store. A repeated key is answered with the first call's ids and places nothing. - **`world.owned` looks for each thing by net id.** Crates survive a restart with the same net id and NPCs do not, so a boot id proves nothing. A wipe drops the registry. - **`EventsEnabled` now gates every world write** (D94). Revert, owned and monuments are never gated. - A revert refuses only something that exists and is not this run's. It never trusts ZoneManager's `true`. - `overlay.toml` protocol → 9. ## A defect the walk found here, fixed The link starts in `Init`, before the save loads. A mid-run restart had the website ask `world.owned` into an empty world, and the plugin **pruned three live crates** from its registry. World commands now answer `not-ready` until `OnServerInitialized`, and `server.hello` carries `worldReady` (PLAN.md §28.7). ## Walked On Oxide and on Carbon, through real core: each verb observed in the game, the lost answer, reload, restart, wipe, the switch and bounds, and the owner fix before and after (§28.6). `node scripts/checkPlugin.js` and its tests pass. - [x] AI-assisted: Claude Code (Claude Opus 5.5) 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
wtclaude added 3 commits 2026-09-24 03:16:07 -05:00
EnsureGrant and EnsureGroupPermission passed `this` as the owner. Given an
owner, Oxide first asks PermissionExists(perm, owner) — whether THAT plugin
registered the name — and returns silently when it did not, so every
site-authored grant of another plugin's permission was a no-op on Oxide that
the report counted as applied (PLAN.md §27.6).

Both now pass null and read the store back. A grant the store does not hold
afterwards is reported in the new `notLanded` list instead of counted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
world.monuments, world.zone, world.place, world.revert and world.owned
(PLAN.md §28, D86-D96):

- Zones through ZoneManager's temporary zones, owned by the bridge, with a
  required duration the plugin enforces itself (D96) and re-created from the
  bridge's own registry on every load and every ZoneManager reload (D92),
  because ZoneManager erases a plugin's zones when that plugin unloads.
- Crates and NPCs from a plugin-held allowlist (D88) at a monument by kind
  and instance, or at raw coordinates (D87, D93), bounded 25/20 per step (D95).
- A registry file keyed by the website's idempotency key, since this bridge
  has no at-most-once store: a repeat answers with the first call's ids.
- world.owned LOOKS by net id: crates survive a restart, NPCs do not, so a
  boot id proves nothing. A wipe drops the registry whole.
- EventsEnabled gates every world write; revert, owned and monuments never
  (D94). Revert refuses only a thing that exists and is not this run's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
fix(bridge): refuse world commands until the world has loaded
All checks were successful
PR Checks / plugin-checks (pull_request) Successful in -1m29s
0a7ad570f7
The link starts in Init, before the save loads. The phase 13a walk restarted
the rig mid-run: the website saw the new boot id at once, asked world.owned
into an empty world, and the plugin — finding no entity behind any net id —
pruned three live crates from its own registry. They stayed in the world,
owned by nothing, and a revert would have refused them.

world.owned, world.revert, world.monuments and every world write now answer
`not-ready` until OnServerInitialized, zones are not re-created before it,
and server.hello carries `worldReady` so the website can hold its reconcile
until the world it would be asking about exists.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
whitlocktech merged commit 81e0ab8c06 into edge 2026-09-24 05:08:47 -05:00
whitlocktech deleted branch feat/phase-13a-world 2026-09-24 05:08:48 -05:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: RunicGateway/Rust-Plugins#9
No description provided.