Layer 3 · Actions and active state

Rage Is Not Raging

D&D used one word for both the ability and the state it created. The compiler made me admit they were different responsibilities. Splitting them turned one overloaded feature into a composable handoff, then made the hidden +2 explain itself.

Startone feature owns everything
Turnthe compiler refuses
Proofthe +2 explains itself
responsibility finds an ownerarchitecture pushes back
Before the small API felt inevitable

One object doing everything

The last field note ended with the confidence of .On(bus). Rewind one day. The first real Rage Feature was roughly 170 lines because activating Rage and remaining enraged were still treated as one responsibility.

It checked and spent uses, remembered its owner, subscribed to attacks and incoming damage, added damage and resistance, tracked subscription IDs, ended itself, and announced that it had started and stopped.

The early Rage Feature owns every responsibility COMMIT C578C911 · ROUGHLY 170 LINES ONE OBJECT Rage Feature active · owner · subscriptions every reason to change RESOURCEcheck + spend uses ACTIVATIONstart Rage SUBSCRIBEwatch attacks MODIFYadd damage MODIFYapply resistance OBSERVEtrack activity CLEAN UPunsubscribe itself LIFECYCLEdecide when to end EIGHT RESPONSIBILITIES · ONE FEATURE
The implementation was not large because Rage was unusually complicated. It was large because the capability and its ongoing state had not yet become separate owners.
The pressure: one D&D word was hiding two different kinds of behavior.
One word, two owners

Rage is not Raging

Rage is something a Barbarian can do. Raging is the temporary state that follows. The feature should decide whether activation is allowed and spend the use. The condition should own everything that remains true afterward.

Rage and Raging become separate responsibilities ONE D&D WORD Rage SPLIT BY RESPONSIBILITY PERMANENT CAPABILITY Rage Feature CanActivate spend one use begin the handoff TEMPORARY STATE Raging Condition change calculations watch combat activity own how the state ends THE VERB WHAT REMAINS TRUE
D&D could keep one familiar name. The code needed to distinguish the capability somebody owns from the state currently affecting them.
The split: Features activate. Conditions persist.
The package graph tells the truth

The compiler agrees

The concepts were separate. Their dependencies were not. Rage needed to create Raging. Conditions needed the D&D event topics. The root D&D package defined those events while also trying to import its child implementations.

Go refused the circular dependency. I could move the names around, but I would not continue until the arrows expressed one honest direction of ownership.

Go rejects the circular package dependency THE ATTEMPTED HANDOFF DND5E / FEATURES Rage Feature needs to create Raging DND5E / CONDITIONS RagingCondition needs D&D event topics ROOT DND5E PACKAGE foundation + coordinator defines events · imports children GO BUILD import cycle not allowed THE ARROWS CANNOT ALL BE TRUE
The compiler error was not an inconvenience to route around. It was evidence that a package was claiming two incompatible architectural roles.
The refusal: a circular dependency is architecture arguing with itself.
The smaller contract was waiting

Give the job to the thing that can own it

Action[T] had landed before both the overloaded Rage and the compiler wall. Its first example was Rage. The contract asked only whether an action could activate, then let it activate. The cycle made the value of that restraint visible.

The answer was not another object above everything. It was giving each responsibility to the thing that could do it well and own the result.

Each responsibility moves to the thing that can own it RESPONSIBILITY LEDGER RAGE · ACTION[T] CanActivate + Activate check availability · spend the use · begin the handoff RAGING · CONDITIONBEHAVIOR Own the temporary state modify calculations · observe activity · end cleanly CHARACTER Own active conditions verify target · apply behavior · store the condition DND5E / EVENTS Own the handoff contract topics · event types · small shared interfaces ONE JOB EACH · ONE DIRECTION BETWEEN THEM
The generic Action contract is supporting evidence, not a new subsystem tour. Its importance is how little it asks Rage to own.
The rule: Put each responsibility with the thing that can do it well and own the result.
A foundation that imports no children

Give the handoff neutral ground

The final correction extracted dnd5e/events. It owned the topics, event types, and small ConditionBehavior interface without importing Feature, Condition, or Character implementations. Every domain package could point toward the same foundation.

Rage could create Raging and announce the handoff. Character could receive it, apply it, and store it. Raging could subscribe to the moments and chains it owned from there.

A neutral events package enables the typed Rage to Raging handoff 01 · ONE-WAY PACKAGE FOUNDATION FEATURES Rage CONDITIONS RagingCondition CHARACTER active conditions DND5E / EVENTS · FOUNDATION topics · event types · ConditionBehavior imports no D&D child implementation 02 · THE TYPED HANDOFF RAGE.ACTIVATE spend one use create RagingCondition CONDITIONAPPLIEDEVENT Target: barbarian Type: raging Condition: ConditionBehavior CHARACTER RECEIVES Condition.Apply(bus) append(activeConditions) Raging owns what happens next
The event bus carried a typed handoff across the boundary. Character remained the owner of its collection, and Raging remained the owner of the active behavior.
The handoff: the event bus was the route. It did not own the result.
The insides become observable

Why is this 13 damage?

The proof did not need the whole combat system. A Barbarian attacked a Monster. The active RagingCondition joined DamageChain and contributed its bonus. The result kept enough provenance to explain itself.

The Rage contribution becomes visible in a 13 damage breakdown ONE COMPLETE PATH BARBARIAN Raging active DAMAGECHAIN gather → execute MONSTER takes final damage DAMAGE BREAKDOWN · SOURCE-BACKED TEST 1d12 weapon damage 8 Strength modifier +3 Rage bonus +2 total damage 13 THE AUTONOMOUS RULE CAN NAME ITS CONTRIBUTION
The archived integration test rolled 8, added a +3 Strength modifier, and received +2 from Raging. Without the active condition, the Rage line disappears.

Eight from the die. Three from Strength. Two from Rage. Composable rules still need to explain themselves.

Next field note When a Rule Needs the Room →