PHASE-001: DispatchPhase model, registry phase, dispatcher queueing - #79
Merged
Conversation
Handlers registered with [FactoryEventHandler<T>] can now declare when they run relative to the factory operation that raised the event: - Immediate (default, unchanged): dispatched at Raise time, in the caller's transaction, observing staged state. - AfterFlush / AfterCommit: deferred into a per-scope queue and run when that phase drains, so read-only projections stop inheriting the in-transaction contract built for atomic write handlers. This plan lands the model, the registry phase, the queueing, and the drain primitive (IFactoryEventPhaseScheduler in the Internal namespace). The drain POINTS come next: entry-call tracking in PHASE-003, the consumer-facing coordinator in PHASE-004. Failure semantics key off the drain point rather than the phase: an in-transaction drain propagates handler exceptions so the caller can roll back; a post-completion drain logs (9003) and swallows, since a throw there can no longer roll anything back. A drain covers the requested phase and every earlier one, so nothing a handler enqueues mid-drain is silently dropped. FactoryEventHandlerAttribute<T> moved to its own file: FactoryAttributes.cs is linked into the netstandard2.0 generator, and compiling DispatchPhase there would duplicate a public runtime type. The generator matches the attribute by metadata name, so it never needed the type. Backward compatible: no phase argument means Immediate, the generator's existing two-argument RegisterHandler call is untouched, and no existing test was modified. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First plan of the PHASE arc (phased factory-event dispatch). Adds the phase model and the per-scope deferral queue; nothing drains yet — PHASE-003 (stacked behind this) owns the entry-call drain.
What lands
DispatchPhaseenum (Immediate/AfterFlush/AfterCommit),Immediatethe default and today's exact contract.FactoryEventHandlerAttribute<T>moved to its own file soDispatchPhaseis never linked into the netstandard2.0 generator (a duplicated public type broke every project referencing both — recorded as a Discovery Log entry).FactoryEventHandlerRegistrycarries a phase per handler; new 3-argRegisterHandleroverload alongside the generator-emitted 2-arg one, both preserving the[DynamicallyAccessedMembers(All)]trimming invariant.IFactoryEventPhaseScheduler(public inInternal, so generated code can reach it): enqueue + drain. The drain sweeps the requested phase and every earlier one, earliest first, until empty.Immediatehandlers; log events 9001–9004.Gates
Plan review (CONCERNS, 4 vetoes addressed pre-implementation), test review (found a real defect: the drain resolved only the requested phase's queue, silently dropping work enqueued into an already-passed phase — fixed, 3 tests verified red against the pre-fix code), code review. Suites at close: unit 653×2, integration 561×2, Design 86×2 — 0 failures.
Interim-behavior note: three acceptance bullets here pin behavior PHASE-003 is chartered to invert (queue-whenever-a-scheduler-exists). That restatement is pre-declared, not test-gutting — PHASE-003's evidence map lists each amended test with its intent preserved.
Docs:
docs/todos/PHASE-phased-event-dispatch/plans/001-phase-model-and-queueing.md, reviews in the siblingreviews/folder.🤖 Generated with Claude Code