Skip to content

feat: the kernel declares every event it dispatches to the dispatcher - #43

Merged
rodrigoteamx merged 1 commit into
mainfrom
feat/events-declare-themselves
Sep 8, 2026
Merged

feat: the kernel declares every event it dispatches to the dispatcher#43
rodrigoteamx merged 1 commit into
mainfrom
feat/events-declare-themselves

Conversation

@rodrigoteamx

Copy link
Copy Markdown
Contributor

What

milpa/runtime now declares the five events it dispatches — architecture.resolved, capability.resolved, plugin.booting, plugin.booted, kernel.booted — to the dispatcher, at the moment Kernel::boot() holds it and before any dispatch.

  • Milpa\Runtime\Event\RuntimeEvents — one declarations holder for the package. It owns the event names as constants and returns one EventDeclaration per event from declarations(): the dispatching class, one sentence on the moment it fires, the payload key (event) and the class of the subject, and interceptable: true on plugin.booting alone — the only payload that carries an InterceptionSlot.
  • The dispatch sites use the same constants. Kernel::boot() and InlinePluginBootStrategy no longer hand literals to dispatch(); a declaration and its dispatch cannot drift apart through a retyped string.
  • Declaring is a silent no-op for a dispatcher that cannot take it (instanceof DeclaredEvents), and dispatching an undeclared name keeps working: declaring is not enforced, it is counted.
  • milpa/core floor raised to >=0.11 <1.0 (where DeclaredEvents / EventDeclaration live). The locked siblings capped core at ^0.8, so milpa/container 0.2.0→0.2.1, milpa/events 0.3.0→0.4.0, milpa/resolver 0.7.0→0.7.1 and (dev) milpa/plugin 0.12.2→0.13.1 moved with it.

Why

Greenhouse decisions/0228, «los eventos se declaran al despachador y la casa los cuenta»: the authority on what events exist is the emitter, and the one place every dispatch passes through is the dispatcher. So each package declares its events in the same package, next to its dispatch() sites, built from the same constants those dispatches use — and the house asks the dispatcher what exists and what was dispatched undeclared, instead of grepping source.

Falsifier

tests/TheKernelDeclaresEveryEventItDispatchesTest.php drives the real boot path with a spy dispatcher implementing both MilpaEventDispatcherInterface and DeclaredEvents, and asserts:

  • every name the spy saw dispatched was declared, and declare() ran before the first dispatch();
  • the declared set is exactly the five expected names, in boot order;
  • each declaration's subjectKey/subjectType/interceptable match the payload that dispatch really carried;
  • end to end on milpa/events' own dispatcher: dispatched() minus declared() is empty;
  • control: the same boot on a plain dispatcher that does not implement DeclaredEvents runs unchanged and dispatches the same five names.

Mutation proof: deleting the kernel.booted declaration from declarations() turned 3 of the 5 tests red; restored, green again.

Gates

composer validate --strict valid · php-cs-fixer 0 of 80 · PHPStan [OK] · docblock gate OK · attribution OK · PHPUnit 104 tests, 308 assertions green · line coverage 92.66% (floor 89).

The authority on «what events exist» is the emitter, and the one place every
dispatch passes through is the dispatcher — so this package now declares the
five events it dispatches to the dispatcher the moment Kernel::boot() holds it,
before any dispatch (greenhouse decisions/0228).

- Milpa\Runtime\Event\RuntimeEvents holds the names as constants and
  declarations(): one EventDeclaration per event (architecture.resolved,
  capability.resolved, plugin.booting, plugin.booted, kernel.booted) with the
  dispatching class, the moment it fires, the payload key and class of its
  subject, and plugin.booting alone marked interceptable — its payload carries
  the InterceptionSlot.
- Kernel::boot() and InlinePluginBootStrategy dispatch from those same
  constants: a declaration and its dispatch can no longer drift apart through a
  retyped string.
- Declaring is guarded by instanceof DeclaredEvents: a dispatcher that cannot
  take declarations is told nothing and every dispatch keeps working.
- Falsifier: TheKernelDeclaresEveryEventItDispatchesTest drives the real boot
  with a spy dispatcher and holds the dispatched set against the declared set,
  the declared set against the exact expected list, each declaration against
  the payload really carried, plus the family dispatcher end to end and a
  control with a dispatcher that takes no declarations. Deleting one
  declaration turns three of its five tests red.
- milpa/core floor raised to >=0.11 <1.0 (the DeclaredEvents contract). With
  it, milpa/container 0.2.0 -> 0.2.1, milpa/events 0.3.0 -> 0.4.0,
  milpa/resolver 0.7.0 -> 0.7.1 and, in require-dev, milpa/plugin
  0.12.2 -> 0.13.1 moved, because the locked versions capped core at ^0.8.
@rodrigoteamx
rodrigoteamx merged commit 22524b1 into main Sep 8, 2026
2 checks passed
@rodrigoteamx
rodrigoteamx deleted the feat/events-declare-themselves branch September 8, 2026 10:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant