Layer 4 · Runtime context

When a Rule Needs the Room

Rage could explain its own +2. Dueling needed both hands. Sneak Attack needed an ally beside the target. Protection needed the room, a shield, and a reaction. The event could not carry the whole world.

Starteach rule asks for a seam
Breakthroughruntime context already exists
Overstepreading becomes mutation
request-scoped knowledgeresponsibility expands
The pattern starts repeating

One real rule asks for the next seam

Rage proved damage could compose. The next features revealed adjacent needs. Second Wind first published a healing event; HealChain followed five days later. Unarmored Defense exposed armor-class calculation; ACChain followed, although that condition would not join it until later.

Real rules reveal reusable calculation seams RULEREUSABLE SEAM BARBARIANRage proves DAMAGECHAINindependent damage contributions FIGHTERSecond Wind exposes HEALING FLOWHealingReceivedTopic → HealChain follows BARBARIANUnarmored Defense exposes ARMOR-CLASS CALCULATIONneed identified → ACChain follows IMPLEMENT THE RULE · DISCOVER THE REUSABLE MECHANISM
The architecture was becoming repeatable: use the next rule to discover the next shared seam rather than predicting every seam upfront.
The rhythm: implement the rule, then make the missing mechanism reusable.
Calculation is no longer enough

Conditions work until they need the room

Dueling needed to inspect both hands. Sneak Attack needed to know whether an ally stood beside the target. Protection combined distance, a shield, and an available reaction. These conditions needed facts beyond their own state and the event in front of them.

Real conditions need progressively wider runtime context THE KNOWLEDGE RADIUS EXPANDS CONDITIONwhat do I need? DUELING · SELFWhat is in my hands?main hand · off handa shield is allowed SNEAK ATTACK · WORLDWho is beside my target?Room · Teamsposition + relationship PROTECTION · COMPOSED VIEWCan I protect that ally?Room · shield · reaction MY STATE → MY EQUIPMENT → ANOTHER PLAYER → THE ROOM
The event identifies the attack. It does not contain every fact each independent rule may need to interpret that attack.
The pressure: Conditions work great until they need another player or the room.
Put everything on the event?

The event is not the world

Adding the missing facts to DamageChainEvent solved each example locally. It also made every publisher responsible for equipment, armor, positions, teams, levels, and action economy whether the listening rule needed them or not.

The focused damage event grows into a description of the world FOCUSED EVENTDamageChainEventAttackerIDDamageComponentswhat happened here JUST ADD FRANKENSTEIN EVENTDamageChainEvent AttackerWeapons AttackerArmor NearbyAllies Positions Teams AttackerLevel ActionEconomy · and whatever comes next EVERY CONDITION MAKES EVERY PUBLISHER KNOW MORE
The event started as a message about damage. The convenient additions turned it into a partial, ever-growing copy of game state.
The boundary: Every new condition cannot make every publisher describe more of the world.
Move the knowledge somewhere else

Four wrong homes for the same question

The event was only the first candidate. Resolver functions made the chain depend on an absent game server. Conditions caching equipment could go stale. Registries on the bus made event routing responsible for state storage.

Four plausible homes fail for different ownership reasons WHERE DOES RUNTIME KNOWLEDGE LIVE? One publish needs an answerhands · room · teams · reaction 01 · EVENTCarry everythingbecomes a kitchen sink 02 · CHAINCarry resolverswho provides the server? 03 · CONDITIONCache the answercopied state becomes stale 04 · BUSStore registriesrouting becomes storage The question was not how to pass more data. IT WAS WHERE REQUEST-SCOPED KNOWLEDGE BELONGED
Each option solved the immediate lookup by giving a second responsibility to something that already had one.
The real question: Where does knowledge for this one runtime call belong?
The thing Go already had

This is runtime context

The game server had already loaded the characters and room for this call. It could expose narrow registries through Go's context.Context. The rulebook defined the questions; each condition asked only what it needed; core events remained generic.

Request-scoped gamectx lets each rule query a narrow view of loaded state ONE CALL · ONE RUNTIME VIEW RPG-API · HOSTLoad the characters and roomprovide narrow registries CONTEXT.CONTEXTrequest-scoped runtime knowledgetravels through this publish · disappears with the call D&D RULEBOOKDefine the questions conditions are allowed to ask DUELINGCharacterRegistrymain hand · off hand SNEAK ATTACKRoom + Teamsposition · nearby ally PROTECTIONRoom + CharacterRegistrydistance · shield · reaction EACH RULE ASKS ONLY THE QUESTION IT OWNS
The event stayed small. The host supplied the loaded world, and each condition received a temporary, purpose-shaped way to read it.
The breakthrough: Events describe what happened. Context answers what is true right now.
The read model needs a write path

Reading worked. How do we write this back?

gamectx gave conditions clean read access. It did not give them an owner-controlled write path. When damage needed to change HP, the design handed out live Combatants, called ApplyDamage, then marked changed objects dirty for the host to save later.

Runtime context solves reads but leaves no owner-controlled write-back path THE READ BREAKTHROUGH RPG-APIload live Combatants RUNTIME CONTEXTprovide a view of what is true CONDITIONask a narrow question READ ACCESS BECOMES DIRECT MUTATION TOOLKITresolve damage LIVE OBJECTApplyDamage MUTATIONHP changes PERSISTENCE SIGNALIsDirty = true MISSING OWNER-CONTROLLED WRITE PATH condition decides?Character owns state how does the requested change get home? READ ACCESS SOLVED · WRITE-BACK PATH MISSING
ApplyDamage and dirty tracking shipped. The generic save sweep remained intended, and conditions still lacked a clean request path back to the state owner.

A condition knew what should change. How could it ask the Character that owned the state to change it?