feat(events): targeted leases, value sets and searchable sources (Phase 12b) #194
Reference in New Issue
Block a user
No description provided.
Delete Branch "feature/events-p12b-borrowed-and-oneshots"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Events Phase 12b, core half — and the half 12a did not need. One of five:
website,servuo-plugins(#—),link(#—),Module-uo(#—),docs(docs#222). Spec of record:docs/link/v7.md§11.Five repos and no
MODULE_API_VERSIONbump (org lead, 2026-09-07): 1.10.0 is amended in place, the shape every phase since P10 has used while this workstream sits onedge.A targeted lease is a shape
core.leasedid not haveEvery lease before this named a single value, so the lease id was the target and none of the four callables took one.
Spawner.MaxCountis not that shape: it is one capability over thousands of spawners, and a reservation on the id alone would let one run turning up one spawner refuse every other run every other spawner.So a lease may declare a
target, the callables are handed it, and the ledger ref becomes<lease id>#<target>— which puts the two-events-one-target refusal at the granularity the world actually has while leaving it coming from the same unique index it always did.Extending core rather than giving the module a lease verb of its own is what §F decided in Phase 8 ("the verb is core's"): a lease verb per module would re-implement
maxDurationMsand the conflict check once per module, advisory everywhere and wrong in the first one that forgot. Half that objection no longer holds — the target check comes free from the index whichever verb reserves the row — and the other half still does.Three readers of a lease ref, not one
cleanup.restoreLeaseandledger.normaliseboth looked a lease up by the wholerow.ref, and both were correct for exactly as long as a ref was a bare id. Left alone, a targeted row would have missed in both — cleanup reporting "no module registers the lease" and refusing to restore a world that really was changed, which is the worst failure this table has. All three now go througheventLeaseForRef.values, and searchable sourcesvaluescloses astringlease's set.min/maxbound the numeric types and nothing boundedstring, so the only check on a string lease's value was the game side's — a refusal arriving unattended, mid-run, from a step nobody is watching.Option sources become searchable, and the first one that needed it forced this phase's shape.
resolveOptionSource(id)took no argument and every source answered a flat list bounded at 2,000; module-uo's spawner target is 6,707 spawn points, so a flat list would have dropped two thirds of the world and said nothing about which two thirds — the failure 12a named for decoration, arriving for real.resolve({ q })is additive: every source is passed a term, none is required to read one, andsearchablesays which do, because inferring it from a truncated answer reads correctly right up until a small deployment's list happens to fit.One incidental change in the regenerated spec
The committed
swagger-output.jsonsaid the session cookie isrg_rig, which is neither the documented default nor what this repo's ownserver/.envsets — it was generated somewhere with that env var set. The regeneration corrects it torg_token. Flagging it because it is a fix I did not go looking for.routes.manifest.jsonis unchanged: this phase adds no route.Checks
npm test: 2010 pass, 0 fail (89 DB-skipped), withmodules/uoparked as the core suite requires. Six new tests cover the targeted-lease shape, both refusal directions, the value set and the search term.Exercised end to end against the real local ServUO 57.4 world — see the
servuo-pluginsPR for the walk. Not driven by that rig: core's ref composition through a live run; it is covered by the unit tests above rather than by the walk.🤖 Generated with Claude Code
https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
The core half of Phase 12b, and the half Phase 12a did not need. A targeted lease is a shape `core.lease` did not have. Every lease before this named a SINGLE value, so the lease id WAS the target and none of the four callables took one. `Spawner.MaxCount` is not that shape: it is one capability over thousands of spawners, and a reservation on the id alone would let one run turning up one spawner refuse every other run every other spawner. So a lease may declare a `target`, the callables are handed it, and the ledger ref becomes `<lease id>#<target>` -- which puts the two-events-one-target refusal at the granularity the world actually has while leaving it coming from the same unique index it always did. Extending core rather than giving the module a lease verb of its own is what §F decided in Phase 8 ("the verb is core's"): a lease verb per module would re-implement `maxDurationMs` and the conflict check once per module, advisory everywhere and wrong in the first one that forgot. Half that objection no longer holds -- the target check comes free from the index whichever verb reserves the row -- and the other half still does. Three readers of a lease ref, not one. `cleanup.restoreLease` and `ledger.normalise` both looked a lease up by the whole `row.ref`, and both were correct for exactly as long as a ref was a bare id. Left alone, a targeted row would have missed in both -- cleanup reporting "no module registers the lease" and refusing to restore a world that really was changed, which is the worst failure this table has. All three now go through `eventLeaseForRef`. `values` closes a `string` lease's set. `min`/`max` bound the numeric types and nothing bounded `string`, so the only check on a string lease's value was the game side's -- a refusal arriving unattended, mid-run, from a step nobody is watching. Refused on any other type: a set beside `min`/`max` would be a second bound with no rule about which wins. Option sources become searchable, and the first one that needed it forced this phase's shape. `resolveOptionSource(id)` took no argument and every source answered a flat list bounded at 2,000; module-uo's spawner target is 6,707 spawn points, so a flat list would have dropped two thirds of the world and said nothing about which two thirds -- the failure 12a named for decoration, arriving for real. `resolve({ q })` is additive: every source is passed a term, none is required to read one, and a `searchable` flag says which do, because inferring it from a truncated answer reads correctly right up until a small deployment's list happens to fit. `MODULE_API_VERSION` stays 1.10.0, amended IN PLACE (org lead, 2026-09-07) -- the shape every phase since P10 has used while this workstream sits on `edge`. The swagger regeneration carries one incidental change: the committed spec said the session cookie is `rg_rig`, which is neither the documented default nor what this repo's own `server/.env` sets. It was generated somewhere with that env var set. The regeneration corrects it to `rg_token`. 2010 pass, 0 fail (89 DB-skipped), with `modules/uo` parked as the core suite requires. Six new tests cover the targeted-lease shape, both refusal directions, the value set, and the search term. Refs: docs/link/v7.md §11, docs/website/MODULE_API.md, EVENTS_PLAN.md Phase 12b Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4