Merge pull request 'docs(rust): plan permission rewards — lend or give any registered permission (PLAN_REDESIGNS §12, D342–D346)' (#342) from docs/rust-perm-rewards-plan into main

Reviewed-on: #342
This commit is contained in:
2026-10-12 20:21:44 +00:00

View File

@@ -10,7 +10,8 @@ rows still to come (§3.4); §4, built 2026-09-29 (§4.1). **§6 was spiked on 2
chose neither route: Runic Gateway writes its own NPC plugin, RunicNPC, planned in
[`../../runicnpc/PLAN.md`](../../runicnpc/PLAN.md) (D214–D314), and the rest of this plan waits for it.** RunicNPC v1.0.0 shipped on 2026-10-09, and the plan
resumed with §2 the same day (D331–D334); §2 was built on 2026-10-12, and §11's Economics spike the same day
raised D335–D336 (§11.2.1). Economics was built on 2026-10-12 too (§11.2.4). The Backpacks spike followed and raised D337–D339 (§11.3.1), and Backpacks was built the same day (D340, D341, §11.3.4). **Every item left is built, tested and PR'd first; every walk —
raised D335–D336 (§11.2.1). Economics was built on 2026-10-12 too (§11.2.4). The Backpacks spike followed and raised D337–D339 (§11.3.1), and Backpacks was built the same day (D340, D341, §11.3.4). The org lead then added permission rewards (§12,
D342–D346), planned the same day. **Every item left is built, tested and PR'd first; every walk —
theirs and the ones still owed by §3 and §5 — happens together at the end (D331, §9).**
This is a companion to [`PLAN_FIXES.md`](PLAN_FIXES.md) and [`PLAN.md`](PLAN.md). Where they disagree, this
@@ -25,6 +26,7 @@ document is later and wins. Its decisions continue PLAN_FIXES' numbering at **D1
| 5 | Chat titles: twenty-three conditions | §4.6, D172–D175 | Rust-Plugins, Module-Rust |
| 6 | NPCs — now RunicNPC, its own plan ([`runicnpc/PLAN.md`](../../runicnpc/PLAN.md)) | §4.7 | **runicnpc-rust**, Rust-Plugins, Rust-Link, Module-Rust, installer |
| 11 | First-class optional plugins: Economics, Backpacks, RaidableBases (Kits stays required) | — (added 2026-09-28) | Rust-Plugins, Module-Rust, installer, Android-app |
| 12 | Permissions as event rewards: until the event ends, or for good | — (added 2026-10-12) | **core** (website `main`, MODULE_API 1.14.0), Module-Rust |
**Protocol.** §1, §3, §4 and §5 joined **protocol 13**, which shipped on 2026-10-09 with RunicNPC v1.0.0
(bundle 2026.10.09). The wire changes still to come, §2's and §11's, **open protocol 14** on `edge` and are
@@ -1015,6 +1017,8 @@ module does not have, and R9 keeps item icons out of the asset bridge.
| **D340** | **A backpack slot shows the item's name, not an icon.** The plugin sends each stack's English name from the game (`display`), so "Wood ×1000" and "Assault Rifle + Weapon Lasersight" need no item catalogue and nothing loads from outside the site. R9 stands. | Icons from a third-party host keyed by shortname; shortnames alone. | 11.3.4 |
| **D341** | **A blueprint names what it makes.** Backpacks stores a blueprint as `blueprintbase` with a target; the stack carries `blueprint` (the target's shortname), its `display` is the target's name, and the slot reads "Assault Rifle blueprint". | Every blueprint shown as the same anonymous "Blueprint". | 11.3.4 |
Permission rewards, added on 2026-10-12, were decided as D342–D346; they are tabled with their section, in §12.8.
## 11. First-class optional plugins (D199–D202)
The org lead named four plugins the module supports as first-class integrations. Kits already is one. The other
@@ -1646,3 +1650,167 @@ The org lead answered §11.4.4's three questions the same day:
These are new verbs, events and hello fields. **They join protocol 14 (D332)**, which §2 opens: protocol 13
shipped on 2026-10-09, before §9 reached item 7.
---
## 12. Permissions as event rewards (D342–D346)
The org lead asked for this on 2026-10-12, during the Backpacks build: an event reward that grants **any
permission the server's plugins registered**, so a perk nobody planned for (night vision, a chat colour, a
`/remove` command) is a reward without code written for it. The picker sorts by mod, then by permission
inside the mod. It is its own item, after Backpacks, and planned first. Five decisions, D342–D346, were answered
the same day (§12.8).
Two steps come out of it. **Permissions until the event ends** lends them for the run, as a kit reward does.
**Permissions for good** gives them for keeps, as a staff member's grant in the permission manager does. Both
take several permissions in one step, which needs one small addition to core's step editor (§12.3).
### 12.1 What it builds on
Nothing below is new machinery; both steps are rows in tables the permission manager already pushes.
- **The lease is a kit reward's** (PLAN.md §29, D84). `rust.kit.entitle` and `rust.backpack.size` (D337) write
one `rust_perm_run_grants` row per recipient, the permission mirror pushes it in the next `perm.sync`, and the
step's revert deletes the rows so the next sync revokes them. The push reads the **union** of these rows and
the standing grants, so a player who already held the permission keeps it after the revert.
- **A standing grant is the permission manager's own** (§1): a `rust_perm_grants` row for a website user,
scoped to a server, reaching every account the user has linked (D28). It is `UNIQUE` on (user, permission,
scope), so granting one twice leaves the first.
- **The catalogue** is `rust_perm_catalogue`: per server, every registered permission and the plugin that
registered it (`owner`, the plugin's `Name`). A permission no plugin owns, as Carbon's built-in modules
register theirs, has no owner, and the permission manager groups it by its prefix (§1).
- **The message is D333's notice**, which rides `perm.sync` and is told once, at once or on the player's
next wake.
- **So there is no plugin code, no sidecar code and no protocol change.** Protocol 14 is untouched.
### 12.2 The picker: `rust.options.permissions` (D342)
- **Every permission in the catalogue of every enabled server, once each.** Its `group` is the plugin that
registered it. A permission with no owner is grouped by its prefix, labelled *"<prefix> (not
registered by a plugin)"*, the same rule the permission manager draws.
- **Sorted by group, then by permission inside the group**, both case-insensitively. The editor keeps a
source's order.
- **A row says where it is missing.** A permission that is not in every enabled server's catalogue carries
a row `note` (MODULE_API 1.13.0): *"Only on Main, PvP"*.
- **Not `searchable`.** A server registers a few hundred permissions; the multi-select's own filter box
(§12.3) narrows them in the browser. If a deployment ever passes the 2,000-row bound, the source says so, as
every bounded source does.
- **No blocklist and no allowlist** (D343). The bridge's own `runicgateway.*` permissions are in the list like
any other.
- **The step checks the server it is given.** Every picked permission must be in that server's catalogue; one
that is not is refused by name: *"PvP has no permission nightvision.allowed — its plugins did not register
it"*. The check runs in the dry run too.
### 12.3 The core addition: a param that holds several values (D344, MODULE_API 1.14.0)
The step editor has six param types, all single values. A step that takes several permissions needs a
list, so core gains one declaration flag. **It is an addition only**: a module that does not use it is
unchanged, and Module-uo's `coreApi: "^1.10.0"` still resolves.
- **`multiple: true` on an action param** of type `string` or `int`, with an optional `max` (1 to 100, 20 if
left out). It is refused on any other type, on a `duration` param, and on a template's inputs, which this
version does not extend.
- **The value is a JSON array**: each element checked as the type's literal, duplicates dropped, order kept, at
most `max`. `required` means at least one. A bare value is read as a list of one, so a spec written by hand
or over the API does not trip on it. The module's `perform()` and `cost()` receive the array.
- **The catalog serves `multiple` and `max`** beside the rest of the declaration (`GET
/admin/events/catalog`).
- **The editor draws a multi-select.**
- With a `source`: the chosen values as chips above a grouped checklist, with a filter box over it. Ticking
past `max` is refused with the number.
- With a `searchable` source: the typeahead adds a chip per pick.
- With no source: a tag box, a value per Enter.
- **The definition view and the run console** show a list as its values joined by commas.
- **Docs:** `website/MODULE_API.md` gains 1.14.0's entry and the flag in the declaration reference;
`website/EVENTS.md` §F names the control.
### 12.4 Permissions until the event ends: `rust.perm.lend` (D345, D346)
*"Lends the permissions you pick to the people a mode picks from this run's tally — or every linked player of
the server — until the run ends. Waits for them if they are offline. Someone who already held one keeps it
when the event ends."*
- **Params:**
- the server;
- the permissions (`multiple`, `max` 10, from §12.2);
- the recipients and count (`rust.kit.entitle`'s modes, `linked` included);
- a message and its delivery;
- **`confirm`**, a required boolean: *"I am sure I want to give these permissions as a reward"* (D343).
- **One row per recipient per permission** in `rust_perm_run_grants`, with no kit and no credit; the notice
goes on one row per recipient, so each is told once.
- **Unticked, it refuses** in the dry run and at run time, naming what it would have given: *"Tick "I am sure"
to make nightvision.allowed, betterchat.color a reward"*. Being a required toggle (D334), it cannot be left
unset.
- **`risk: 'change'`, `reversible: 'ledger'`**, as a kit reward is. Its revert and reconcile are
`rust.kit.entitle`'s, and core reverts it at the end of every run, a normal finish included.
- **The budget is new: `rust.permissions`, *Permissions lent*.** One per recipient per permission; a mode
that is not a count is priced at the recipient bound times the permissions, as kit rewards are.
### 12.5 Permissions for good: `rust.perm.give` (D345, D346)
*"Gives the permissions you pick to the people a mode picks, for good. Ending or cancelling the event does not
take them back; staff remove them in the permission manager."*
- **The same params as §12.4**, the confirm included.
- **A standing grant per website user per permission**, scoped to the step's server, with source `event` and
the note *"event run <run>, step <step>"*. A grant already there is left alone and counted as
*already held*.
- **`risk: 'irreversible'`, `reversible: 'none'`**, as `rust.coins.pay` is (D335). Like every step that changes
the world it arrives off until an admin turns it on under Actions; unlike §12.4's, its failure aborts the run
by default instead of pausing it, and Actions lists it as irreversible, so an admin enables the two
separately.
- **The budget is new too: `rust.permissions.kept`, *Permissions given for good***, priced as §12.4's.
- **Taking one back is the permission manager's.** The grant shows there with its source and note, and staff
delete it like any other.
- **The message rides a notice-only row** in `rust_perm_run_grants`: no permission, no kit, no credit. The
push grants nothing for it and tells the notice once. Nothing reverts the step, so the row stays; it is a
sentence that has been said.
### 12.6 Settled in this plan, and overrulable
- **A blank message tells nobody**, as a kit reward's does (D333), rather than a default sentence. A
permission's name (`nightvision.allowed`) means nothing to a player, so only the author can say what it does:
*"You can use /nv until the event ends"*. `{permissions}` puts the names in.
- **At most 10 permissions in one step**: enough for a perk bundle, and the price stays readable.
- **A permission the server's catalogue lacks is refused, not skipped.** The plugin may be unloaded for the
wipe, and a reward that silently gives less than it says is the worse failure.
- **The for-good grant is scoped to the step's server**, not every server. An author who wants it everywhere
adds a step per server.
### 12.7 Where the work lands, and in what order
1. **Website (core), on `main`:** §12.3, MODULE_API **1.13.0 → 1.14.0**, with its tests (`registries`,
`spec`, the editor's multi-select) and Module-uo's frozen-manifest proof against the new core, as 1.11.0's and
1.13.0's were run.
2. **Module-Rust, on `edge`**, after the website PR merges: the option source, the two actions, the two
budgets; `coreApi` `^1.14.0`; `ci/core-ref.json` re-pinned to the merge; `routes.manifest` regenerated.
3. **docs:** this section's *Built* notes; `website/MODULE_API.md`; `website/EVENTS.md`.
**Not touched:** Rust-Plugins, Rust-Link, the protocol, the installer, Android-app (the app does not author
events) and Module-uo. The Integration Kit's pin is at 1.12.0 and moves only when someone re-reads its
chapters for a newer core; it skipped 1.13.0 the same way.
### 12.8 Decisions of record
Answered by the org lead on 2026-10-12.
| # | Decision | Rejected | § |
|---|---|---|---|
| **D342** | **An event reward may be any permission the server's plugins registered**, picked from the catalogue grouped by the plugin that registered it and sorted by plugin, then permission. `rust.backpack.size` stays beside it, with its sizes and its take-back warning. | A fixed list of perks; folding the backpack step into the generic one. | 12.2 |
| **D343** | **No blocklist and no allowlist. The safety is a required "I am sure" toggle in the step**; unticked, the dry run and the run refuse, naming the permissions. | A pop-up when the step is saved (a core change); a blocklist of dangerous permissions. | 12.2, 12.4 |
| **D344** | **One step gives several permissions, through a multi-select picker**: a `multiple` flag on an action param, added to core as MODULE_API 1.14.0. | One permission per step; five fixed picker slots; a comma-separated text box. | 12.3 |
| **D345** | **A permission reward lasts until the run ends, or for good**, the author's choice. | Until the run ends only. | 12.4, 12.5 |
| **D346** | **The two durations are two steps**, `rust.perm.lend` (`change`) and `rust.perm.give` (`irreversible`), each with its own budget, because core gives an action one risk class. | One step with a "keep it" switch, rated `irreversible` throughout. | 12.4, 12.5 |
### 12.9 The walk (deferred to the end, D331)
On both rigs, with a plugin that registers a perk (NightVision or similar):
- open the picker and see it grouped by plugin, sorted inside each, a permission missing from a server noted,
and the unowned ones grouped by prefix;
- run a *Permissions until the event ends* step with two permissions, unticked (refused) and ticked; see the
perks work online and offline at the grant, and gone after the run ends, except for a player who already held
one;
- run a *Permissions for good* step; see it stay after the run ends, show in the permission manager with its
note, and go when staff delete it;
- unload the plugin and see the step refuse the permission by name.