docs(events): Phase 0 — the eleven decisions, and the two documents they reach into

Closes the second half of EVENTS_PLAN.md Phase 0. All of EVENTS.md §N1–N11 were
answered by the org lead on 2026-09-01, before any code, which lifts the ⚠ gate
from P11 and P12 and unblocks P1.

Nine went the way §N recommended. Two did not, and each changes a phase:

- N2 — publishing a version and starting a run are `admin` only; a moderator
  keeps live control of a run already in flight (cancel, abort) and nothing
  more. Start and stop are deliberately not the same gate: starting commits the
  deployment to everything a definition contains, unattended, while cancelling
  is incident response. §K's table and the API surface table are updated, and
  P3 and P6 carry it.
- N4 — this deployment is single-instance and not planned to change, so P2 does
  not build the `--scale app=2` test. Every claim path in §E is built exactly as
  specified regardless — the unique index and the CAS equally protect a tick
  that overruns into the next one, and the lease and its reclaim recover a step
  whose process died mid-dispatch. §E records what to build first if this
  deployment is ever scaled.

N1 was taken in full, item grants included, which is a reversal of two separate
lines in ADMIN_CONTROLS.md §8 rather than one. §8's cut list names "item/gold
grants" explicitly and separately from world building, so the new §8a amendment
says both out loud rather than letting the grant ride along on the creation
reopening. §8's bullet is left standing verbatim — it is the decision that was
actually taken — with a pointer to the amendment, and §1's thesis paragraph gets
the same pointer. Loot-table changes and arbitrary [set / [get / [add stay
permanently excluded, along with the rest of the Tier-H moderation verbs.

N3 gives ENGAGEMENT.md §7.1 Q6's "no evaluator yet" an owner: the Event System
runner becomes the `kind: 'scheduled'` evaluator, built in that workstream's P2.
Nothing in the engagement contract changes.

One consequence was derived rather than decided and is flagged for review:
POST /admin/events/:id/verify moves to `admin, editor` rather than staying with
`start`. A dry run dispatches nothing, and the author who wrote the definition
is who should price it against the caps before asking an admin to publish.

Verified: every relative link and anchor in the four touched files resolves,
including the renamed §N anchor (#n--decisions) and the new #8a. The docs repo
has no CI workflow, so the plan's "docs CI link check" was run by hand.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-09-01 20:52:37 -05:00
parent cda5325ab4
commit cc6775e20b
4 changed files with 274 additions and 32 deletions

View File

@@ -21,6 +21,12 @@ None of these are *moderation*. A staff member who wants to kick a cheater, ban
**The thesis up front:** a small, well-guarded set of account/session-moderation verbs plus the help-page queue covers the overwhelming majority of "why do I have to log in to the game for this" moments. World-building and object manipulation (`[add`, `[set`, `[dupe`, decorate, spawners) should stay in the game client — they are target-driven, high-blast-radius, and gain nothing from a web form.
> ⚠ **Amended 2026-09-01 — see [§8a](#8a-amendment--2026-09-01).** That thesis holds for *ad-hoc staff
> action*, and the Event System does not contest it: a GM still does the target-driven parts in-client.
> What was reopened is narrower — declared, cap-bounded, ledgered, idempotent creation and **leased**
> mutation, both owned by an event run and both undone at teardown. `[set` / `[get` / `[add` and
> loot-table changes stay excluded exactly as this paragraph argues.
---
## 2. How ServUO admin controls actually work
@@ -284,6 +290,11 @@ All resolved by source inspection (ServUO checkout at `<servuo>`). No live-shard
## 8. Decisions — locked 2026-07-12
> ⚠ **Partly amended on 2026-09-01. Read [§8a](#8a-amendment--2026-09-01) with this section.** Two of
> the exclusions in the first bullet below — world creation, and item/gold grants — were reopened for
> the Event System under conditions that did not exist when this was locked. The bullet is left
> standing verbatim because it is the decision that was actually taken; §8a is what changed and why.
- **Scope:** Phase 1 (kick / ban / unban / broadcast) + Phase 2 (help-page queue) + Phase 3 second-wave. **The former Tier-H verbs (firewall, kill/res, jail, item/gold grants, set-access-level) are cut entirely** — not now, not later.
- **Authorization:** enforced on the **website** (admin-only + moderator roles). Inbound sidecar commands are applied on the shard as **CoOwner-level** authority, with a hard floor that refuses any target at `AccessLevel >= CoOwner` (Owner-only shield). Write plane defaults **off** in `Bridge.cfg`.
- **Attribution:** `web:<actor>` in `CommandLogging` and the `BanDealer` tag; no core edits.
@@ -292,6 +303,70 @@ All resolved by source inspection (ServUO checkout at `<servuo>`). No live-shard
---
## 8a. Amendment — 2026-09-01
**What changed:** §8's blanket exclusion of world writes is narrowed, in **two** places, and only for
the Event System. Everything §8 decided about the moderation write plane — its verbs, its
CoOwner-level authority, its Owner-only shield, its `Bridge.cfg` default-off switch, its attribution
and its logging — stands unchanged.
**Decided by:** the org lead, 2026-09-01, answering
[`../website/EVENTS.md`](../website/EVENTS.md) §N1. The design of record is `EVENTS.md`; this section
records the reversal against the decision it reverses, so §8 is never read alone.
### What is now permitted
| | Permitted | Under what conditions |
|---|---|---|
| **1** | **Event-owned creation** — creatures, "simple" boss variants, oracle NPCs, temporary gates, temporary decoration | Created by an event run, counted against a module-declared cap dimension, recorded in the resource ledger with the run, step and actor that produced it, and **deleted by serial at teardown** on every terminal path — completion, cancellation and abort alike |
| **2** | **Leased mutation** of a plugin-declared allowlist of config values and object properties | Baseline recorded before the write, drift-checked on read-back, and **deadline-enforced by the game itself** — the shard restores baseline when the lease expires whether or not the website ever comes back |
| **3** | **Item and gold grants** | As (1): cap-bounded, ledgered and attributable — but `reversible: 'none'`. A granted item is not clawed back |
### What stays excluded, permanently
Unchanged by this amendment and not reopened: **loot-table changes**, and **arbitrary `[set` / `[get`
/ `[add`**. Both are the "change something with no baseline recorded" case, which is the thing §8 was
actually objecting to; no cap makes them safe. `firewall`, `kill`/`res`, `jail` and `set-access-level`
also stay cut — this amendment is about what an *event* may create and borrow, not about restoring the
Tier-H moderation verbs.
### Why the reversal, and why item grants are called out separately
**§8's objection was to an unrecorded change with a large blast radius, made from a web form.** It was
locked when the bridge was a read plane with three inbound verbs, none of which were moderation, and
when the only way to express "change the world" would have been a passthrough command string. In that
setting the objection was correct and it would still be correct today for anything shaped that way —
which is exactly why `[set` / `[get` / `[add` remain excluded.
**The Event System proposes something with four properties that shape did not have**, all of which are
required before P12 ships and none of which are optional:
1. **Declared, not typed.** A module declares a verb with its params and their types; the browser posts
an action id and a params object. There is no passthrough field and no place a request body can name
a sidecar route.
2. **Cap-bounded.** Every dimension is enforced in a conditional `UPDATE` with the cap in the `WHERE`
clause — a control a stolen admin session cannot talk its way past, because a role check is exactly
what a stolen session has already passed.
3. **Ledgered.** Every created object and every borrowed value is recorded in `event_run_resources`
before it is confirmed, with its run, step and actor, and with a declared revert.
4. **Idempotent on the wire.** A key minted once per step rides core → module → sidecar → plugin, and
the plugin answers a repeat with the original result rather than acting twice — so a lost
acknowledgement cannot double a spawn or a grant.
**Item grants are named as their own row because they are a second reversal, not a consequence of the
first.** §8's cut list names "item/gold grants" explicitly and separately from world building; reading
the creation reopening as carrying grants along with it would be the silent reversal this amendment
exists to avoid. They are permitted on the same four conditions as creation — with the difference that
they are **not reversible**, which is why the cap and the ledger carry the entire weight for this row
and why the action ships declared as `reversible: 'none'` rather than pretending at a revert.
**What has not changed:** the module boundary is still not a security boundary, and none of the above
defends against a hostile module. It defends against a compromised session and an operator mistake —
both of which *scheduling* makes larger, because a scheduled change happens while nobody is watching.
That is the reason the caps and the leases matter more here than the role check does.
---
## 9. Where the code goes
| File | Responsibility |