# Event Bus And Automation Foundation Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Establish a safe lifecycle EventBus foundation with documented contracts, selected after-commit emitters, listener boundaries, idempotency guardrails, and architecture checks without broadly refactoring legacy workflows.

**Architecture:** Domain lifecycle events live under their owning modules and expose scalar automation-safe payloads. Phase 3 wires only the Phase 2 Actions and one tracking listener seam through a dedicated provider, while the event catalog documents planned emitters for future gradual wiring.

**Tech Stack:** Laravel 12, PHP 8.2 readonly classes, Laravel events/queues, Livewire feature regression tests, PostgreSQL/MySQL-compatible runtime.

---

## File Map

- Create `tests/Feature/Architecture/EventBusTest.php` for event contracts, after-commit wiring, queue registration, and idempotent tracking listener behavior.
- Create `tests/Feature/Architecture/AutomationBoundaryTest.php` for listener boundary scans and side-effect architecture checks.
- Modify Phase 2 event classes and Actions under `app/Domain/*` to use scalar payloads and `DB::afterCommit`.
- Create missing lifecycle contract classes under `app/Domain/{Enrollments,Finance,Attendance,Corporate,WhatsApp,Events,Tracking}/Events`.
- Create `app/Domain/Common/Automation/ListenerIdempotency.php`.
- Create `app/Domain/Tracking/Listeners/TrackB2bLeadConverted.php`.
- Create `app/Providers/DomainEventBusServiceProvider.php` and register it in `bootstrap/providers.php`.
- Extend `config/architecture-check.php` and `app/Console/Commands/ArchitectureCheckCommand.php`.
- Create `docs/event_catalog.md` and `docs/event_driven_architecture.md`.

### Task 1: Lock The EventBus Contract In Tests

**Files:**
- Create: `tests/Feature/Architecture/EventBusTest.php`
- Create: `tests/Feature/Architecture/AutomationBoundaryTest.php`

- [ ] **Step 1: Write failing EventBus tests**

```php
public function test_required_lifecycle_event_classes_exist_and_keep_scalar_payloads(): void
{
    foreach ($this->requiredEvents() as $eventClass) {
        $this->assertTrue(class_exists($eventClass), "Missing event {$eventClass}");
        foreach ((new ReflectionClass($eventClass))->getProperties(ReflectionProperty::IS_PUBLIC) as $property) {
            $type = (string) $property->getType();
            $this->assertStringNotContainsString('App\\Models\\', $type);
        }
    }
}

public function test_b2b_conversion_event_waits_for_outer_commit(): void
{
    Event::fake([B2bLeadConverted::class]);
    DB::beginTransaction();
    app(ConvertB2bLeadToOrganizationAction::class)->handle($lead, $data);
    DB::rollBack();
    Event::assertNotDispatched(B2bLeadConverted::class);
}
```

- [ ] **Step 2: Write failing automation boundary tests**

```php
public function test_domain_listeners_do_not_write_enrollment_or_payment_models_directly(): void
{
    foreach ($this->domainListenerFiles() as $file) {
        $source = File::get($file->getRealPath());
        $this->assertStringNotContainsString('Enrollment::create(', $source);
        $this->assertStringNotContainsString('Payment::create(', $source);
    }
}
```

- [ ] **Step 3: Run RED tests**

Run:

```bash
php artisan test tests/Feature/Architecture/EventBusTest.php tests/Feature/Architecture/AutomationBoundaryTest.php
```

Expected: failure because Phase 3 event contracts/provider/listener guardrails are missing.

### Task 2: Add Scalar Lifecycle Event Contracts And Safe Emitters

**Files:**
- Modify: `app/Domain/Corporate/B2B/Events/B2bLeadConverted.php`
- Modify: `app/Domain/WhatsApp/Events/WhatsAppLeadCreated.php`
- Modify: `app/Domain/Scheduling/Events/AttendanceSessionCreated.php`
- Modify: `app/Domain/Attendance/Events/AttendanceMarked.php`
- Modify: `app/Domain/Enrollments/Events/EnrollmentCreated.php`
- Modify: `app/Domain/Enrollments/Events/DepositPaymentApproved.php`
- Modify: `app/Domain/Corporate/Finance/Events/CorporateInvoiceIssued.php`
- Modify: `app/Domain/Corporate/Finance/Events/CorporatePaymentAllocated.php`
- Modify: `app/Domain/Corporate/B2B/Actions/ConvertB2bLeadToOrganizationAction.php`
- Modify: `app/Domain/WhatsApp/Actions/CreateB2bLeadFromConversationAction.php`
- Modify: `app/Domain/WhatsApp/Actions/SendWhatsAppReplyAction.php`
- Modify: `app/Domain/Scheduling/Actions/CreateAttendanceSessionAction.php`
- Create: remaining Event contract files requested by Phase 3.

- [ ] **Step 1: Convert Phase 2 Events to immutable scalar contracts**

```php
final readonly class B2bLeadConverted implements ShouldDispatchAfterCommit
{
    public function __construct(
        public int $leadId,
        public string $organizationId,
        public ?int $actorUserId,
        public string $occurredAt,
    ) {
    }
}
```

- [ ] **Step 2: Add missing Event contract classes**

Add readonly scalar classes for enrollment, finance, attendance, Corporate, WhatsApp, webinar, and tracking lifecycle facts. Each class uses IDs, safe scalar metadata, and `occurredAt`.

- [ ] **Step 3: Use explicit after-commit dispatch in safe Phase 2 Actions**

```php
DB::afterCommit(fn () => B2bLeadConverted::dispatch(
    leadId: $lead->id,
    organizationId: (string) $organization->id,
    actorUserId: $data->actor->id,
    occurredAt: now()->toISOString(),
));
```

- [ ] **Step 4: Re-run EventBus RED tests**

Run:

```bash
php artisan test tests/Feature/Architecture/EventBusTest.php
```

Expected: event contract and after-commit tests move green; provider/listener tests may remain red until Task 3.

### Task 3: Add Listener Provider, Idempotency Foundation, And Architecture Checks

**Files:**
- Create: `app/Domain/Common/Automation/ListenerIdempotency.php`
- Create: `app/Domain/Tracking/Listeners/TrackB2bLeadConverted.php`
- Create: `app/Providers/DomainEventBusServiceProvider.php`
- Modify: `bootstrap/providers.php`
- Modify: `config/architecture-check.php`
- Modify: `app/Console/Commands/ArchitectureCheckCommand.php`

- [ ] **Step 1: Add idempotency runner**

```php
public function once(string $listener, string $eventKey, Closure $callback): bool
{
    $key = 'domain-listener:' . sha1($listener . '|' . $eventKey);
    if (! Cache::add($key, true, now()->addDays(7))) {
        return false;
    }

    try {
        $callback();
    } catch (Throwable $exception) {
        Cache::forget($key);
        throw $exception;
    }

    return true;
}
```

- [ ] **Step 2: Add first event-driven tracking listener**

```php
class TrackB2bLeadConverted implements ShouldQueue
{
    public function handle(B2bLeadConverted $event): void
    {
        $this->idempotency->once(static::class, "lead:{$event->leadId}", function () use ($event) {
            $this->tracking->dispatch('B2bLeadConverted', [...]);
        });
    }
}
```

- [ ] **Step 3: Register listeners in a focused provider**

```php
Event::listen(B2bLeadConverted::class, TrackB2bLeadConverted::class);
```

- [ ] **Step 4: Extend architecture checks**

Add scans for:

- direct provider/messaging dispatch from Livewire surfaces.
- direct WhatsApp provider calls inside non-WhatsApp business Actions.
- direct analytics/tracking dispatch from Livewire UI.
- listener direct cross-module Enrollment/Payment writes.

Use baseline reporting for uncertain existing debt.

- [ ] **Step 5: Re-run architecture tests**

Run:

```bash
php artisan test tests/Feature/Architecture/EventBusTest.php tests/Feature/Architecture/AutomationBoundaryTest.php tests/Feature/Architecture/DomainBoundaryTest.php
```

Expected: PASS.

### Task 4: Document The Event Catalog And Verify Regressions

**Files:**
- Create: `docs/event_catalog.md`
- Create: `docs/event_driven_architecture.md`
- Modify: `docs/domain_boundaries.md` only if the event rules need a small cross-reference.

- [ ] **Step 1: Write the event catalog**

Each row includes:

```markdown
| Event | Owner | Payload | Current emitter | Planned emitter | Listeners | Automation safe | Idempotency |
```

- [ ] **Step 2: Write the event-driven architecture guide**

Document after-commit dispatch, listener boundaries, idempotency keys, retry/dead-letter posture, notification orchestration direction, tracking direction, and CRM future integration.

- [ ] **Step 3: Run regression verification**

Run:

```bash
php artisan test tests/Feature/Architecture/DomainBoundaryTest.php tests/Feature/Architecture/EventBusTest.php tests/Feature/Architecture/AutomationBoundaryTest.php tests/Feature/Architecture/WorkflowActionTest.php tests/Feature/Admin/CohortSessionsPageTest.php tests/Feature/WhatsApp/WhatsAppDirectIntegrationTest.php
php artisan app:architecture-check
```

Expected: touched Phase 2 workflows and Phase 3 architecture tests pass; architecture check reports baseline findings rather than breaking legacy flows.

- [ ] **Step 4: Syntax-check touched PHP files**

Run `php -l` on new/modified PHP files from the Phase 3 slice.
