From c0d6cab0e9ace9c7db901572b73891bbe9173d5c Mon Sep 17 00:00:00 2001 From: wtclaude Date: Sat, 26 Sep 2026 16:20:42 -0500 Subject: [PATCH] =?UTF-8?q?docs(rust):=20plan=20fixes=20=E2=80=94=20the=20?= =?UTF-8?q?first=20player=20walk's=20findings,=20gating=20the=20cutover?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PLAN_FIXES.md plans what the Oxide pass of PLAYER_WALK.md found on 2026-09-26: fifteen fixes (F1-F15, F15 corrupts every non-ASCII site-to-game string including config saves; F9 rolls back valid config edits on a cold compile), the interface and walk-doc corrections, and the org lead's decisions D159-D168 (D160 reverses D31: the site owns every permission and group). Recommends the fixes land before Module-Rust's cutover (phase 19) and the redesigns follow it. PLAN.md gains a pointer in its status line and §35; the rust README lists the new file. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY --- modules/rust/PLAN.md | 15 ++ modules/rust/PLAN_FIXES.md | 338 +++++++++++++++++++++++++++++++++++++ modules/rust/README.md | 9 +- 3 files changed, 358 insertions(+), 4 deletions(-) create mode 100644 modules/rust/PLAN_FIXES.md diff --git a/modules/rust/PLAN.md b/modules/rust/PLAN.md index 1a89c18..9b49164 100644 --- a/modules/rust/PLAN.md +++ b/modules/rust/PLAN.md @@ -1,5 +1,8 @@ # `module-rust` — the plan +**Before the Module-Rust cutover (phase 19):** the first player walk's fixes and the org lead's +decisions from it (D159–D168) are planned in [`PLAN_FIXES.md`](PLAN_FIXES.md), 2026-09-26. + **Status:** phases 0 and 1 done, 2026-09-15. **Twenty-two decisions of record, no open questions.** Audited against the whole contract, not just the game-facing chapters (§7); the event and engagement catalogues are §9 and §10; §11 is a second pass over `MODULE_API.md` itself. **R19–R22 (2026-09-15) added a second modding framework, a Pterodactyl egg, moved the rigs off @@ -6982,6 +6985,18 @@ line, `confirms it connected.` (installer#34). [w206]: https://gitea.whitlocktech.com/RunicGateway/website/issues/206 [w207]: https://gitea.whitlocktech.com/RunicGateway/website/issues/207 +## 35. Before the cutover — the player walk's fixes (2026-09-26) + +The first walk of [`PLAYER_WALK.md`][pw] with a real player (the Oxide pass, org lead in game) found fifteen +places where the bridge or the site does something other than this plan and [`PROTOCOL.md`][protocol] say, and the +org lead took ten decisions on the spot (D159–D168) — among them **D160, which reverses D31**: the site now owns every +permission and group, not only those it authored. They are planned in their own document, +[`PLAN_FIXES.md`](PLAN_FIXES.md), so this one stays a record of the phases as built. + +**Its recommended gate:** the fixes (its §6 steps 1–2, protocol 13) land before Module-Rust's cutover in phase 19; +the redesigns it describes — the permission manager, the event step editor, zone options and domes — follow the +cutover as phases of their own. The Carbon pass is still to be walked and may add to it. + [kit]: https://gitea.whitlocktech.com/RunicGateway/Integration-kit [protocol]: ../../rust-link/PROTOCOL.md [pw]: ../../rust-link/PLAYER_WALK.md diff --git a/modules/rust/PLAN_FIXES.md b/modules/rust/PLAN_FIXES.md new file mode 100644 index 0000000..df4daa6 --- /dev/null +++ b/modules/rust/PLAN_FIXES.md @@ -0,0 +1,338 @@ +# `module-rust` — plan fixes + +**Status:** plan, awaiting the org lead's approval, 2026-09-26. **The gate before Module-Rust's cutover +(phase 19, [`PLAN.md`](PLAN.md) §34, D145).** Everything here comes from the first walk of +[`PLAYER_WALK.md`](../../rust-link/PLAYER_WALK.md) with a real player in the game — the Oxide pass, walked by +the org lead on the `rust-oxide` rig with every frame checked on the console, the sidecar and the site's +database. The Carbon pass has not been walked yet and may add to this document. + +This is a companion to [`PLAN.md`](PLAN.md), not a replacement: its decisions continue PLAN.md's numbering +(D159 onward), and where the two disagree this one is later and wins. It has three kinds of content, and +they gate the cutover differently (§6): + +- **Fixes** (§2) — the plugin, the sidecar or the site does something other than what the plan and the + protocol say. Fifteen, F1–F15. +- **Interface and walk-doc corrections** (§3) — what works but misleads, and where the walk doc itself is wrong. +- **Changes the org lead decided during the walk** (§4) — new behaviour, each with its decision of record. + +--- + +## 0. What the walk was + +| | | +|---|---| +| Date | 2026-09-26, 16:40–21:05 UTC | +| Rig | Pterodactyl server 17 `rust-oxide`, Oxide, world 3000 / seed 1234 | +| Plugin | Rust-Plugins **v0.1.1**, the released file, byte-identical | +| Sidecar | the phase-17 build, protocol 12 (pre-D155) | +| Site | the phase-18 walk core on :3270, Module-Rust at phase 17 | +| Extra plugins | Kits 4.4.9, ZoneManager 3.1.14, BetterChat 5.2.15, PopupNotifications 0.2.1, and — added during the walk — PermissionsManager 2.1.2 and ZoneDomes 2.0.2 | + +**Scorecard.** Player walk: 1–7, 9, 10 pass (4 and 9 with findings), 8 needs a second player. Identity: +1–5 pass, 6 behaves as written but that behaviour is F5. Permissions: all seven pass, three with findings. +Configuration: 1, 3, 4, 6, 7 pass; 2 failed as written (F9) and passed on a retry; 5 not applicable; 8 proven +at the site's guard only. Events: 1–6 pass in the game, 6's frame is F13; 7 needs several players. Rewards: +1, 3, 4, 6 pass (6's text is F15); 2 is covered by 3; 5 deferred. Chat titles: 1 authored, not earned; 2–5 +not walked. Map and phone walks: not walked (two accounts, the app). + +## 1. Decisions of record + +Taken by the org lead during and straight after the walk. + +| # | Decision | +|---|---| +| **D159** | **"Gathered" is everything a player harvests** — swings, the bonus on the final hit, and what is picked up off the ground. Rejected: swings only (the leaderboard would disagree with the inventory). F1. | +| **D160** | **The site is the source of truth for all permissions and groups** — those that were on the server before the site, those added in the game, and those added on the site. Everything flows back to the site. **This reverses D31**, under which a permission the site had not authored was "somebody else's business" and never enumerated. | +| **D161** | **Each server has a policy for a change made in the game: revoke, adopt, or auto-adopt. The default is auto-adopt.** | +| **D162** | **The permission screen follows uMod PermissionsManager's flow** (the reference is PermissionsManager 2.1.2 on the rig, screenshots in the workspace's `perms-screenshots/`): all players ⇄ all groups → a player or group → its plugins → that plugin's permissions with Granted / Revoked, Grant all / Revoke all → a player's groups, a group's players, Remove all. | +| **D163** | **Every subject on that screen shows the linked website account, if any, and the in-game name — or the Steam id when the server has no cached name.** | +| **D164** | **A "kit open for everyone" event is a lease paired with a use credit.** `core.lease` on the kit's permission plus `rust.kit.entitle` (one use per recipient, D103, withdrawn on revert), offered as a template — and the lease form warns when the kit's own limits mean players have already used it up. Rejected: a new lease that rewrites Kits' usage data, which D103 deliberately avoided. | +| **D165** | **The live map's monument markers get a per-type toggle, and the default shows the major monuments only.** Substations (18 on the rig's map), caves and similar small places are off unless staff turn them on. | +| **D166** | **The zone step offers ZoneManager's own flags and settings**, read from the installed ZoneManager, not a hard-coded list. | +| **D167** | **ZoneDomes is an optional dependency.** The zone step gains a "show a dome" option; the bridge adds the dome and removes it with the zone. | +| **D168** | **When a plugin we depend on does not expose what we need, we may write a small helper plugin for it.** It ships from Rust-Plugins beside the bridge, is optional, and is detected at hello like the other integrations. The bridge keeps calling only public APIs (R2); the helper is where a gap in someone else's plugin gets bridged. | + +**One more, leaning but not yet decided:** Steam as a first-class sign-in provider on the website (§4.8). + +## 2. Fixes + +Grouped by severity. Each says what the walk saw, why, and the fix. Line numbers are against +Rust-Plugins v0.1.1 and Module-Rust at phase 17. + +### 2.1 High + +**F15 — every non-ASCII character the site sends to the game is corrupted.** *(Rust-Plugins)* +An event announcement's em dash arrived as `â` in chat and in the console. `RunicGateway.cs:787` reads the +socket byte by byte and appends `(char)buffer[i]` — UTF-8 decoded as Latin-1. The write side (line 756) is +correct, so only site→game text breaks: announcements, news in chat and popups, chat titles and styles, +kit and zone names — **and configuration saves**: a file saved from the website that holds any accented +letter, dash or emoji is written back to disk mangled. Configuration step 3 passed only because the file +was ASCII. +*Fix:* collect bytes per line and decode the finished line with `Encoding.UTF8`; a multi-byte sequence split +across two reads must survive. *Test:* é, — and an emoji through an announcement and a config save, on both +frameworks. Add the same line to the walk. + +**F9 — a valid configuration edit is rolled back because the reload window is shorter than a cold compile.** +*(Rust-Plugins, Module-Rust)* +Kits' chat command changed from the site → "The plugin did not come back, so the old file was put back +automatically". Oxide's log: the compiler had idled out (it stops after 60 s, `oxide.config.json`), restarted, +compiled Kits in 2.85 s, and loaded it — just after `ConfigReloadWindowSeconds = 4f` (line 4087). The retry, +needing no compile, passed. The window races work whose length the plugin does not control: host speed, +plugin size, Carbon's own compiler. Operators must not have to tune Oxide to make the site work. +*Fix:* answer the save at once ("saved, reloading…") and deliver the outcome as a frame when it arrives, under a +long ceiling (30 s or more). Roll back on a real failure — Oxide logs "Failed to initialize plugin" the moment +it happens (configuration step 4 showed it) — not on a clock. Protocol-visible (§5). + +**F10 — the rollback can say "did not come back" about a plugin that did, and can race it.** *(Rust-Plugins)* +After F9's timeout the plugin restored the file and fired a second reload, but the log shows one compile, so +which file the running plugin read depends on timing. *Fix:* never restore while a compile of that plugin is +in flight, and report what actually loaded. F9's fix removes the cause. + +**F12 — a player already inside a zone when it is created or restored is not "in" it.** *(Rust-Plugins)* +After a restart the bridge re-created the event zone correctly (same id, same place, row still `confirmed`); +the org lead woke up 18 m inside it and ZoneManager's `IsPlayerInZone` said nobody. The same happened when a +zone opened around a player. ZoneManager counts a player only on trigger *enter*, and the participation tally +asks it every five seconds (§29.7) — **so after any restart mid-event, everybody already in the arena scores +nothing until they walk out and back in.** +*Fix:* after creating or re-creating a zone, have ZoneManager re-evaluate who is inside; if its API has no way +to, that is D168's case (a helper), or the tally measures distance itself for zones the bridge made. Walk it on +Carbon too. + +**F13 — the `world.expired` frame leaves with its kind overwritten.** *(Rust-Plugins, protocol)* +Three zones expired in the game exactly on time; all three frames were filed as `kind: "zone"`. Line ~7310 +builds `Frame("world.expired", "event")` and then sets `frame["kind"] = e.Kind`. [`PROTOCOL.md`](../../rust-link/PROTOCOL.md) +§15's example has no field for the entity's kind, so the collision never showed in the spec. The walk's own +check (`/events?kind=world.expired`) cannot pass. Present since protocol 9. +*Fix:* send the entity kind as `what`, the name the site's resource rows already use; amend §15. Protocol bump. + +### 2.2 Medium + +**F1 — `gathered` counts swings only.** *(Rust-Plugins)* 825 wood in the inventory, 587 tallied; 20 cloth from +hemp, none. The tally's shape was right (60-second deltas that sum). *Fix (D159):* also count +`OnDispenserBonus` and `OnCollectiblePickup`, and `OnGrowableGathered` for farmed plants; add all three to +`ExpectedHooks` so `rg.hooks` shows them. PROTOCOL.md §8.6 names the hooks that feed the counter. + +**F3 — destroying your own building counts toward the raid stat.** *(Rust-Plugins)* The org lead broke their own +wall and the next tally carried `structures: 1`. The `entity.destroyed` frame is right, and the site already +skips the raid alert for an attacker authorised on the cupboard (D59) — only the counter is wrong. +*Fix:* skip `Structures++` (~line 1700) when the attacker owns the piece or is authorised on its cupboard; +still emit the frame. + +**F5 — one dead server makes every wrong link code say "your code is still good".** *(Module-Rust)* With five of +seven enabled servers offline, a spent code and a made-up `ZZZZZZ` both answered "One of the servers could not +be reached… Your code is still good", and will until an admin disables the dead server. +*Fix:* ingest already sees every `account.link.requested` with its server and TTL. Ask only the servers that +issued a code in the last five minutes, and answer "unsure" only when one of *them* is unreachable +(`links.model.js:181`). + +**F6 — redeeming a code waits on every dead server in turn.** *(Module-Rust)* About 21 seconds, four per dead +server; the rig sorts last, so a successful link waited too. F5's fix usually leaves one server to ask; failing +that, ask in parallel with a short timeout. + +**F8 — loading a missing plugin does not re-sync permissions.** *(Rust-Plugins, Module-Rust)* PopupNotifications +came back at about 18:44; the grant that had been unresolved landed at 18:57, on the fifteen-minute audit. The +page promises "It will land by itself when the plugin is back" — true, thirteen minutes late. +*Fix:* emit a frame from `OnPluginLoaded` / `OnPluginUnloaded` when that plugin registers permissions; ingest +marks the server dirty the way `perm.drift` does. §4.1 needs the same signal. + +**F11 — the lease target picker was designed and never built.** *(website)* `core.lease`'s own comments say the +authoring form reads the chosen lease's target source; `EventEditor.jsx` only ever renders `param.source` +(line 299), so Module-Rust's `rust.options.grouppermissions` is never offered and the target is typed by hand. +The walk's first attempt put it in the wrong field. Built as part of §4.3. + +### 2.3 Low + +**F7 — the restart re-sync fires before the game has loaded.** *(Module-Rust)* With Oxide's permission files +deliberately wiped, the site restored its group, membership and grant — R2's central promise, proven. But its +restart sync ran 35 s before "Server startup complete", timed out with no warning in the log (the titles push +at the same moment did log one), and the retry 2.5 minutes later recorded "0 applied", so nothing says what the +restart restored. *Fix:* hold the restart sync until the hello says `worldReady: true`; log a failed sync. + +**F2 — NPC killers are named by prefab.** *(Rust-Plugins or Module-Rust)* "killed by `wolf2`". Send a display name, +or map prefab to label on the site — whichever the killfeed page already expects. + +**F4 — a death of a player made by another plugin carries `steamId: null`.** *(Rust-Plugins)* `UserIDString` is +empty for players other plugins spawn; the plugin already avoids it for `entity.destroyed`. Use +`((ulong)player.userID).ToString()` at all 13 sites. Real connected players are unaffected. + +### 2.4 By design — a question, not a fix + +**F14 — the site never learns that a zone expired.** Expired zones stayed `confirmed` on the run console until +the runs were cancelled, when teardown found them "already gone" and counted that a success. PROTOCOL.md §15 +says this is intended: "The website maps it to nothing. Core learns about it through `reconcile` and `revert`" +(D96). **The question:** should ingest mark the row `expired` once F13 makes the frame recognisable, so the +console stops showing a live zone that is gone? Recommended: yes. + +## 3. Interface and walk-doc corrections + +**U-1 — "unresolved" is a sentence in the server block, not on the grant.** The org lead granted a permission for +an unloaded plugin, looked, and saw nothing; the warning sits under the server's row next to "in sync". State +belongs on each toggle (§4.1). + +**U-2 — a configuration conflict offers nothing.** "That file changed on the server since you opened it", with no +way to load the server's version or compare. Offer the server's version beside the operator's edit. + +**U-3, U-4 — the event step editor is shaped for developers.** Type tags on every field (`LEASE * · STRING`), the +lease is a text box with a separate "Pick from…" menu, the examples are Ultima Online values shown to a Rust +admin, and a yes/no value is typed. The org lead: "it should be a dropdown of possible values created +dynamically". §4.3. + +**U-5 — Start runs the published version and says nothing about an unpublished edit.** The org lead fixed a step, +saved, pressed Start, and ran the broken version 1. Ask "Publish and start, or start version 1?". + +**U-6 — a lease's "10 minutes" lasted one second.** A phase with no advance rule ends as soon as its steps finish, +and teardown takes the lease back. Nothing in the editor says so. §4.3. + +**Walk-doc corrections** ([`PLAYER_WALK.md`](../../rust-link/PLAYER_WALK.md)): + +- **D-1** — permission step 4 names `zonemanager.admin`, which ZoneManager does not register, and never says the + permission must already be granted on the site to someone (true today; D160 retires the condition). +- **D-2** — configuration step 2 names a ZoneManager setting ("Auto Show Zones") that 3.1.14 does not have. Use + Kits' chat command, which changes something every player can see. +- Events step 6's check (`kind=world.expired`) cannot pass until F13. +- Configuration step 8 through the site is refused by the site first; the plugin's own guard needs a direct + sidecar request with the rig's token. +- Record that Kits hides a kit the player lacks permission for (`Show kits … without the permission: false`), + so "locked" in the events and rewards walks means "not listed". + +## 4. Changes decided during the walk + +### 4.1 The permission manager, rebuilt around the game's real state (D160–D163) + +What exists: a desired-state mirror. The site pushes the set it authored, the plugin reports `foreign` holders +only for names the site claims (`buildDesired` → `managed`), and `rust_perm_catalogue` holds just +`(server_id, permission, seen_at)` — no owner, no holders, no groups. + +What it becomes: + +- **An inventory read** — a new plugin verb returning every registered permission *with the plugin that + registered it*, every group with its title, rank, parent and members, and what each player holds directly + and through groups. The walk showed why the owner must come from the registration, not the name: + PermissionsManager files `zonemanager.ignoreflag.nokits` under Kits. +- **The first import** — on first contact with a server, everything the inventory reports becomes site-owned + (D160), so a server brought under the site keeps what it had. +- **In-game changes** — per D161: auto-adopt by default, or revoke, or adopt by hand. The existing + Revoke / Adopt answers stay for the two manual policies. +- **The screen** (D162, D163, U-1) — players ⇄ groups → subject → plugin → permissions with Granted / Revoked; + each toggle shows its state on each server (granted · waiting for a first connection · unresolved · not + landed); subjects named by website account and in-game name or Steam id. +- **The plugin-load signal** (F8), so the inventory and unresolved grants move when a plugin loads. +- **Open for the plan:** ZoneManager's `zonemanager.ignoreflag.*` permissions exempt players from zone flags + (§4.4). Under D160 the site owns those too — say how the screen presents them. + +### 4.2 The kit-weekend template (D164) + +`core.lease` on `/default/kits.` plus `rust.kit.entitle` to everyone, authored together. The lease +form reads the kit's `MaximumUses` and `Cooldown` and warns: "N linked players have already used this kit". + +### 4.3 The event step editor (U-3–U-6, F11) + +Pick an action or a lease by its label, and the step becomes that thing's own form: pickers for server, group, +permission, monument, kit; toggles for yes/no; the limit shown beside minutes. No type tags, no ids, examples +from the chosen lease. A phase that holds a timed step defaults to lasting that long, or says plainly that it +won't. Start warns about unpublished changes. `rust.announce` already takes `delivery: popup` (phase 17) — +show it as a Chat / Popup choice, and consider popups for the other player-facing event messages (a zone +entered, a reward earned). + +### 4.4 Zones: ZoneManager's options, and a dome (D166, D167) + +- **Options.** ZoneManager 3.1.14 takes key/value arguments to `CreateOrUpdateZone`: settings (name, radius, + size, rotation, enter and leave messages, radiation, comfort, temperature, safe zone, permission, parent, eject + spawns) and 64 flags — vehicles (`NoVehicleMounting`, `NoVehicleDismounting`, `KeepVehiclesIn`, + `KeepVehiclesOut`), combat (`PvpGod`, `PveGod`, `NoPve`, `NoFallDamage`), building (`NoBuild`, `NoDecay`, + `UnDestr`, `NoUpgrade`), loot, NPCs (`NoNPCSpawns`, `NpcFreeze`), comms (`NoChat`, `NoVoice`) and more. The + plugin reports the installed ZoneManager's flag list at hello; the form groups them and offers presets + ("Arena"). **There is no flying flag** — "allow flying in the zone" needs another plugin or a D168 helper. +- **Domes.** ZoneDomes exposes `AddNewDome(player, zoneId, type, stack)` and `RemoveExistingDome(player, zoneId)`. + Three things the walk established: its default sphere (type 0, one layer) is invisible at noon, and red at + three layers showed only a band near the ground — choose a default by looking at each type on the rig; its + domes are saved to its own data file and **outlive the zone** unless the bridge removes them at teardown and + at expiry; and after a restart it loads before the bridge re-creates the zone, so the bridge must re-add the + dome. Check that a null `player` is safe (it is used for messages only). +- F12 and F13 land in the same code. + +### 4.5 The live map (D165) + +A per-type marker setting for staff, defaulting to the major monuments. Decide in the plan whether the plugin's +monument sweep or the site does the filtering; the type (`monument_substation`, cave, …) is already in the +monument data. + +### 4.6 Chat titles: more conditions + +The org lead's list, as inspiration (the names are examples): animal, bow, melee, blade, revolver, NPC, APC and +helicopter kills; headshots; players killed; PvP and PvE kill distance; wood, ore and plants gathered; clothes +and weapons crafted; structures built and repaired; players healed; rockets fired; explosives thrown; quests +completed. Most need new counters in the plugin — the weapon class on a kill, headshots, crafting by category, +heals, building and repair, explosives — each a hook, a tally field and a leaderboard column. D159's gathering +counters feed the gathering titles. Quests need a quest plugin, and none is installed. The plan picks the first +set; the rest follow. + +### 4.7 NPCs + +Research before deciding: which free uMod NPC plugins are maintained and expose spawning with a loadout, a +name and a behaviour, against extending `rust.npc.place` to dress the stock scientist from a Kits kit and give it +a display name. The goal the org lead named is server customisation — NPCs with different kits and names. + +### 4.8 Steam sign-in (website, not this module) + +Steam as a provider beside Google and Discord. It is OpenID 2.0, not OAuth2/OIDC, so it needs its own adapter. +Leaning toward both halves: link-only sign-in (SSO never provisions an account), and linking Steam also creates +the Rust link, since it proves the Steam id more strongly than a code does. Belongs in `website/` with its own +plan; noted here because it changes R1's identity story. + +## 5. Protocol + +One bump, **protocol 13**, carries every wire change here, so the plugin, sidecar, module and PROTOCOL.md move +once: + +- `world.expired` carries `what` (F13). +- The configuration reload outcome as an asynchronous frame (F9). +- A plugin-loaded / unloaded frame (F8). +- The permission inventory verb and its reply (§4.1). +- ZoneManager's flags at hello, zone options on `world.zone`, the dome option (§4.4). +- New tally fields for D159 and the first title counters (§4.6). + +F15 changes no message shape and ships ahead of the bump. + +## 6. Order, and what gates the cutover + +1. **Before anything else:** F15 (data corruption), and F9 + F10 (it throws away edits). Small, no protocol change + for F15. +2. **Protocol 13 with the remaining fixes:** F13, F12, F1, F3, F8, F7, F2, F4, and F5/F6 in the module. +3. **The redesigns** (§4.1, §4.3, §4.4) — each its own phase, planned in detail before code. + +**Recommended gate:** steps 1 and 2 land before Module-Rust's cutover (phase 19); the redesigns follow it. +Nobody outside the org runs module-rust yet, so the cutover should not carry known corruption or silent +rollbacks, but it need not wait for the new screens. Open for the org lead. + +## 7. Helper plugins (D168) + +Where the walk already points at one: + +- **ZoneManager** — re-evaluating who is inside a zone the bridge just created or restored (F12), if it has no API + for it; and a flying permission per zone (§4.4). +- **ZoneDomes** — only if a null `player` turns out to be unsafe in its API. +- **Kits** — none needed: D164 uses the existing credit path. + +A helper is optional, versioned and released with the bridge, detected at hello, and does one job. The bridge +never depends on one to load. + +## 8. The re-walk + +After step 2 of §6, on both frameworks: + +- The whole player walk again, adding: a line with é, — and an emoji through announce and a config save (F15); a + configuration save that needs a cold compile (F9); wood from a felled tree and picked-up hemp (F1); your own + wall (F3); a code redeemed with a server down (F5, F6). +- A zone restarted with a player standing in it, and a zone opened around a player (F12). +- A zone left to expire, checked by `kind=world.expired` (F13). +- A plugin unloaded, a grant made, the plugin loaded again — the grant lands within a minute (F8). +- Then the steps still owed: the Carbon subset, player step 8 and the map walk with a second player, events step + 7 with several players, the phone walks. + +## 9. Open questions for the org lead + +1. The cutover gate in §6 — fixes before phase 19, redesigns after? +2. F14 — should ingest mark an expired zone `expired` (recommended), or keep D96's "maps it to nothing"? +3. Steam sign-in (§4.8) — both halves, and does it belong to this workstream or to `website/`? +4. Which title conditions form the first set (§4.6)? diff --git a/modules/rust/README.md b/modules/rust/README.md index 14ba167..c03301f 100644 --- a/modules/rust/README.md +++ b/modules/rust/README.md @@ -20,10 +20,11 @@ differ. | [`agent/`](agent/README.md) | The same facts in **machine shape** — TSV and JSONL, ~46% of the tokens. Generated in the same pass, so it cannot drift. | | [`CARBON.md`](CARBON.md) | **The other framework.** Where Carbon diverges from Oxide and nowhere else — file layout, the permission store, the `c.` commands, 30 Carbon-only hooks and 13 uMod names its catalogue omits. Sourced from Carbon's own metadata and source, and **proven on a live Carbon 2.0.259.0 server** — R19 at phase 0, and the whole read path at phase 3. | -**The one file here that is ours:** [`PLAN.md`](PLAN.md) — the schedule and the decisions of record -for actually building `module-rust`. Everything else in this directory is copied from uMod; that one -is written by this project and is where the phases, the settled decisions and the local test rig are -recorded. +**The two files here that are ours:** [`PLAN.md`](PLAN.md) — the schedule and the decisions of record +for actually building `module-rust`. Everything else in this directory is copied from uMod; those two +are written by this project and are where the phases, the settled decisions and the local test rig are +recorded. [`PLAN_FIXES.md`](PLAN_FIXES.md) is its companion: the first player walk's fixes and decisions +D159–D168, planned to land before the Module-Rust cutover. > **This is a mirror, not a specification we own.** uMod is upstream and wins any disagreement; the > point of copying it is availability and grep-ability, not authority. Nothing here may be cited as a