Round 48: SRD items/chargen/plumbing — the last engine wave of #505 (#512/#514-#519/#548)
Eight primitives closing the SRD engine register. Two stale claims corrected
(chargen array/roll "not implemented"; the Wearer "affix sums" only).
- Pack-Entity-Reference: chargen array_assign + roll step kinds now implemented
(roll is server-authoritative, forged client values ignored); wearable gains
static `modifiers` (#514) and `equip_affects` (#515, on-equip magic + OnHit
procs, item-keyed derived state).
- Abilities-and-Effects: worn static modifiers compose through the Wearer
modSource (mulMod was hardwired to identity); teleport/push same-zone
relocation ops (#516, forced-move harm-gated, same-zone structural, instance-
entrance refused); the polymorphic dice_num + one-of conflict rejection (#517)
and the `fixed` op flag (#515).
- Combat-System: item procs = an equip-affect subscribing OnHit, with the
OnHit self-loop guard + `fixed` rider; the prevents:move WALK fix (#548, a
confirmed bug — attemptMove never consulted prevents:move, voiding the whole
immobilize system).
- Builder-Commands + Player-Reference: carrying capacity (carry_capacity derived
attribute, enforced at acquisition, get-all-takes-what-fits); the `credits`
verb (#519); rest short/long.
- Pack-MUD-Settings: manifest license/attribution (accumulate per pack; a later
pack can't erase an earlier notice).
- Pack-Lua-Hooks: OnRest now live + OnShortRest/OnLongRest (distinct kinds, NOT
the mag argument which would double a handler's heal).
- Pack-Lua-Scripting: h:move now harm-gates forcing another player (was an
ungated grief hole) — the safe co-move primitive for mounts/grapple-drag.
Store field-drop trap again: #519 pack license needed BOTH fold loops
(mergePacks file->Pack AND Merge Pack->LoadedContent) plus the pack_meta
round-trip; #514/#515 ride existing wholesale-marshaled bodies.
Round 47: SRD engine primitives batch 2 (#536/#539/#541-#547)
Nine composable SRD primitives, threaded into the combat/effect/schema pages.
- Combat-System: #547 OnEnterCombat (the initiative checkpoint, seeded-rng, per
entry); #544 crit dice-doubling (crit_dice doubles the dice term vs crit_mult
the whole roll; reaches swing + check-band crits; set-not-multiply, no leak);
#543 heterogeneous multiattack routine (replaces attacks, shares one to_hit,
same dealDamage path per swing, capped); #536 absorb buffer in the funnel
(after the OnDamageTaken reaction, before the vital; instance-set capacity, no
max_attr, set_resource op).
- Abilities-and-Effects: #541 highest-wins stacking + graded rungs, and the HIGH
harm-classifier fix (affectIsDetrimental / respawn-strip now fold in RUNG
modifiers/prevents — the 6th blind spot; decrement_rung gated cross-player);
#545 duration_kind: indefinite + level/dispel-by-level (indefinite room-field
lease kept finite); #546 prevents_source source-relative CC (a targeting gate,
not a can't-harm firewall; fail-open across load); #539 concentration single
slot (the cross-zone single-writer fix: expireConcentration gated on
holder.zone==z + break-before-detach on transfer/quit); #542 richer if
predicate (comparators + formula RHS; indeterminate operand fails to FALSE, not
0; unknown comparator rejected at parse); the set_resource / increment_rung /
decrement_rung ops; the absorb resource fields.
- Pack-Entity-Reference: resource absorb/fronts; affect level/concentration/
duration_kind/rungs/prevents_source + stacking: highest; combat_profile
multiattack.
- Pack-Lua-Hooks: OnEnterCombat + OnAffectBlocked added to the live event enum.
Store field-drop trap hit TWICE this round (absorb/fronts on resource_defs;
multiattack on combat_profiles) — both round-tripped through Postgres by the
reflection test; noted as JSONB-body fields needing no migration.
Round 46: SRD engine primitives (#511/#513/#537/#538/#540/#535 + #557/#562)
The capstone SRD-primitive round. Six composable primitives plus a fold-clamp
fix and the check subject selector, threaded into the effect/combat pages.
- Check pipeline (Combat-System + the check op in Abilities-and-Effects):
* #511 boon/bane — a net-sign channel selecting among boon_dice/bane_dice/
neutral dice (NOT keep-high synthesis, which inverts the demo's roll-UNDER
ladder); 5e cancellation falls out of sign-netting; composes through
ordinary attribute modifiers, no new state/DTO.
* #513 band `when` — a forcing fifth band axis (auto-crit / auto-fail-save)
with no engine crit/fail vocabulary; truthiness is non-zero AND finite
(NaN != 0 in Go); replaces an affine sentinel hack that broke on stacking.
* #562 subject selector — subject: target is the saving-throw idiom (target
rolls, narrates to the saver, OnCheck subject = saver, caster rides as
other); does NOT rebind band-op target vocabulary (authoring trap noted).
- Damage/death (Combat-System):
* #537 per-target damage_taken_mult — an affect-granted map, product-composed
like prevents (absence = identity-1 structurally), consulted after soak;
per-FACTOR normalization at composition closes the "two {fire:-3} buffs
compose to a 9x amplifier" cross-player harm-gate bypass.
* #535 downed/dying — a third onPoolDepleted disposition: an active
suspends_death affect holds the victim at 0 (no die/corpse/respawn); regen
pauses, auto-swings stop (swingGatesPass now refuses a prevents:[act]
attacker); the resolution is a content affect. die() keeps its one call site.
- Reactions/affects (Combat-System + Abilities-and-Effects):
* #540 incapacitation gate — canReact = canAct && !preventsTag("react") on
every reaction checkpoint; gates reactions only, never the plain bus
(passive on_event still fires while stunned).
* #538 incoming-affect trait immunity — grants_immunity vetoes an incoming
affect BEFORE attach on {ref}u{category}u tags; a separate multiset from
prevents; the veto sits after the reattach branch so a load is never vetoed.
* #557 attribute-fold clamp — the (base+flat)*mul fold saturates (+/-Inf ->
+/-1e12, NaN -> pre-mod base); screen runs BEFORE min/max since NaN passes
every range; the reachable consumers read attr() directly (resourceMax ->
an unkillable bottomless pool, the OPPOSITE of the panel's claimed one-shot).
- Pack-Entity-Reference affect schema: tags, grants_immunity, suspends_death,
damage_taken_mult rows + the conventional act/react/cast prevents tags.
Round 43: the three review-bundle follow-ups (#397/#399/#401)
Refinements to systems already documented (spawn-protection, comms history,
the audit trail); each thread lands in its existing home.
- Abilities-and-Effects (#397): the sourceless-ambient exemption. A room field
with no applier ticks at actor==target, which guardHarmful treats as an exempt
self-effect, so it kept damaging a just-respawned occupant. A sourcelessAmbient
flag fires ENFORCEMENT even at actor==target while CANCELLATION stays
actor!=target-gated, so the field never drops the occupant's own shield;
threaded into all four such harm sites.
- Combat-System (#397): a swing at a spawn-protected target is short-circuited
BEFORE the pipeline, not zeroed inside it. Narration ran before dealDamage, so
the target saw a phantom hit each round; the pre-gate also drops a still-
protected attacker's own shield.
- Builder-Commands + Pack-Entity-Reference (#401): a sent tell is recorded at the
CONFIRMED-echo point, so no phantom entry on a resolve-miss or publish-failure;
history channel appends a shard-local partial-view footer on a multi-shard
fleet; the channel history ring is reaped on channel removal, and the schema
now warns that loosening hear_access retroactively exposes buffered lines.
- Persistence-and-Durability (#399): the async auditor now coalesces up to 64
events into one pgx.Batch round-trip, with per-row fallback under the same
bounded ctx; newest-first reads order by at DESC, seq DESC for true insertion
order incl. within a shared-now batch; and staff audit-by-name now folds in the
target account tier_changed rows, scoped to the owning account.
Round 42: depletion hooks on any pool, damage-type pool routing, the depletion arithmetic
Updates the builder-facing reference for #405/#406/#407:
- resources: `vital` now means only "emptying this causes death"; every pool runs
its own on_depleted. Documents the level-trigger and the two authoring rules it
forces (make the hook idempotent; never reward from one), and the
$depletion.overflow/.applied/.amount scalars with the carry-over idiom.
- damage_types: target_resource, the three-tier routing precedence, why a
capacity-less target is immune by construction (and how to use a 0-base capping
attribute as an opt-in lever), and the never-retrofit rule.
- Combat System: the same two corrections where it described the seam as
vital-only and the routing as op-level-only.
Round 35 milestone: multi-vital support (#71)
Corrects a now-stale claim: Abilities-and-Effects asserted "multi-vital is
unsupported (only the lowest-ref pool damages and kills)".
- Abilities-and-Effects: multiple independently-lethal vital pools, each with
its own on_depleted; deal_damage routes via `resource` (empty = the new
`primary` vital, so existing content is unchanged); `primary` replaces the
implicit lowest-ref rule; max<=0 is natural immunity; non-vital pools are
damageable but never lethal.
- Combat-System: damage now subtracts from the ROUTED pool; documents the
per-pool vitalDepleted predicate (def.vital && max>0 && cur<=0) and why the
max>0 term is load-bearing (a zero-max pool would read as already-depleted
and instantly kill); the death-generation guard is ordered BEFORE the
pool-local cancel re-check so a cross-pool cascade yields exactly one die().
- Pack-Entity-Reference: vital row updated, new `primary` row (rides the
resource JSONB body, no migration), per-pool on_depleted, and the
LintVitalResources / LintDealDamageResources lints.
- Pack-Lua-Scripting: h:damage gains `resource=` for the routed pool.
Round 34 milestone: spawn-protection window, audit trail, comms history
- Combat-System + Abilities-and-Effects: the actor-agnostic post-respawn
spawn-protection window shipped (#394) — updates the #318 "deferred" note.
respawnPlayer opens a pulse-deadline protectedUntil; guardHarmful refuses
harmful ops aimed at a protected player AHEAD of the !isPlayer no-op so a MOB
attacker is covered (safe-room cannot be); drops when the player acts.
- Persistence-and-Durability (new section) + Accounts-and-Auth-Internals: the
append-only character_audit trail (#350) — world emits died/attr/track via an
async per-shard auditor (saver twin, drains on shutdown), account emits
create/tier_changed in-transaction; idempotent on (subject,kind,dedup_key)
with a DURABLY-UNIQUE key (died = per-death UUID, not the transient deaths
counter that collided across relogs); audit self-view scoped by stable pid,
audit <name> staff-only.
- Builder-Commands + Player-Reference: tells/replay session tell history (#349,
structural pair-privacy, skips ignored authors, resets on relog) and
history <channel> scrollback (#348, fetch-time canHear gate, shard-local
partial view); Pack-Entity-Reference channel_defs.history row enriched.
Round 33 milestone: staff visibility pair + respawn hostile-affect strip
- Pack-Entity-Reference: help_defs gains an optional per-topic min_rank; a
gated topic is invisible in both the index and a direct lookup (falls
through to "no help on ..." so existence never leaks), see_also is filtered
per-viewer, and the gate fails open (a dropped field un-gates text, never a
capability) (#351).
- Combat-System: document the "no hostile effect survives respawn" invariant
at the respawnPlayer chokepoint — whitelist inversion (provably-benign is
closed/checkable, unknown ops fail toward stripping), hard removal with no
on_expire hooks, and the tickOnce re-entrancy guard for a lethal DoT (#318).
- Builder-Commands + Accounts-and-Auth-Internals + RPC-and-Protobuf +
Trust-Tier-Model: promote/demote now have the wiz-command posture for
non-staff (plain Huh?, no refusal, no account-service round-trip) via a
manage_tiers visibility bit on IssueSessionAssertion; the service stays
authoritative, the bit is defense-in-depth visibility only (#369).
Round 25: durable-zone login fix + engine-side notes for #69/#262/#266
The round self-documented #69 (Abilities), #262 and #266 (Sysadmin). Closing the
engine-page gaps and the one undocumented item:
- Persistence-and-Durability: NEW 'Where a returning player lands' — characters.zone_ref
is the login routing key (#320). Documents the data-loss bug it fixed (an intra-shard
walk + logout silently reset you to the home zone's start room), the link-dead
reconnect side-fix, the room_ref warn, the checkpoint drift guard, and the remaining
shard-not-zone placement gap.
- Combat-System: the death generation (Living.deaths) + die() entry re-entrancy latch
(an item dupe) + why position/location can't carry the 'did it die' signal (#69).
- Scoped-Event-Bus: paced durable NAK redelivery + the tri-state ack decision (#266).
- RPC-and-Protobuf: AdoptZone is signed on keyed shards (#262).
- Edge-and-Protocol: the connect diagram binds the durable zone.
Engine Dev: abilities/effects, combat, loot/spawns/crafting
- Abilities-and-Effects: substrate, effect-op vocabulary, 10-step lifecycle,
the PvP gate + guardHarmful, tag-CC, affect runtime (stale-comment corrections)
- Combat-System: round loop, swing pipeline, death checkpoint, reactions;
honest gaps (lag logged-only, auto-swings ungated by prevents, dead formula seam)
- Loot-Spawns-and-Crafting: resolver, quality/affixes, scheduled spawns,
tracks/bundles/four modes, crafting + tier-bound salvage
Scaffold wiki + author About and Getting Started
- 48-page skeleton across all audiences (flat files + grouped _Sidebar)
- Every page carries an Audience + Status banner (Ready/Draft/Placeholder)
- About: Overview, History of MUDs (Ready)
- Getting Started: Getting-Started, Running-Locally, First-Admin-Setup,
Content-Packs-Intro (Ready) — dev bypass, local GitHub OAuth, bootstrap
admin, promote/demote, content-pack model, all grounded in code
- SRD5/WoW/D6 pack pages are honest placeholders (packs not yet built)