# Track D Status

**NotificationService LIVE** (2026-09-24, merged `81dd0a4`): the real dispatcher
(`App\Services\Engagement\NotificationDispatcher`, implementing the frozen
`NotificationService` interface) is **on main**. A/B/C: re-run your suites against
the real thing — inject `App\Services\Engagement\NotificationService` (constructor
injection; no-arg default resolves the concrete transport adapters, so production
wiring needs no container config). Event-side wiring is auto-discovered
(`NotificationRequested` → `HandleNotificationRequested`). Criticals bypass
mute/quiet/digest at the dispatcher level.

Pre-flight (2026-09-24): Track A Phase 0 verified on `origin/main` — T-A-01…T-A-06
+ T-A-24 all merged (merge commits c7a70f9, 91c1c62, f62aa76, 4f73694, 0d54b58,
4ad28ae, 5f33d23). track_a.md's "T-A-05 pending" line is stale — it is merged
(4ad28ae). GATE 1 pre-condition satisfied for Track D.

| Task | Status | Branch | Notes / blockers |
|---|---|---|---|
| T-D-01 | ✅ merged (`81dd0a4`) | track-d/T-D-01-notification-platform | NotificationService interface + real dispatcher + endpoints. ACs covered (critical-never-muted, quiet-hours, digest, unread counts, idempotency, event bus). 17 tests; full suite 102 passing. |
| T-D-02 | ✅ merged (`4d0dace`) | d/T-D-02-analytics-ingestion | analytics conventions + 15-event idempotent ingestion. Merged to main 2026-09-30. |
| T-D-03 | ✅ merged (`708f9ef`) | d/T-D-03-engagement-base-model | `BaseEngagementModel` ($connection='engagement', extends BaseDomainModel → audited) + engagement schema-marker migration; `GamificationConfigService` (interface, frozen for C's DEPS⚡) + stub; D-owned `EngagementServiceProvider` binds `NotificationService`→dispatcher + `GamificationConfigService`→stub + additive C-consumed methods. Merged to main 2026-09-30. |
| T-D-04 | 🚀 pushed (separate branch) | d/T-D-04-live-sessions | Live sessions (class/doubt-clearing/webinar): scheduling (no past-dated 422, **no teacher double-booking → 409**), join (late-joiner flag, group-cap 409), cancel (**never silent** — every enrollee notified), doubt bookings (DEPS⚡ T-A-17 cap: free 1 / entitled 5, **exceed → 409 entitlement_exceeded**), recordings (retention sweep idempotent; two-step replace), feedback (**once per session → 409**; instructor view asserts no user_id/name/email). 14 new tests; Engagement 37 passing; full suite 193/193 green. |
| T-D-05 | ⏸ not started | — | Forums/study groups/moderation. |
| T-D-06 | ⏸ not started | — | Customer support. |
| T-D-07 | 🚀 pushed (this branch) | d/T-D-07-affiliate-portal | Affiliate portal runtime: enrollment gate (approved-only, re-checked per operation — unapproved → 403 on all referral tools), referral links (unique token; **QR rotates on regeneration — stale QR 410**), tracked links (labeled, deletable, reported separately), attribution engine (click→signup→purchase per channel/campaign), commission accrual **frozen at accrual-time tier/rate (never retroactive)** + `source_ref` idempotency (duplicate events = no-op), **payouts: min threshold 409 + verified payment method + balance sufficiency; pending commissions settled with payout linkage; one statement per payout**, commission entries **immutable — any write endpoint 409 `commission_immutable`; corrections via disputes only** (open flags entry, second dispute 409), tier changes (Bronze→Platinum, history, **new rate on new accruals only — verified**), win-back sweep (**fires only for leads inactive >14 days; active leads skipped; step never fires twice** — AC fixture test), scheduled promotions (future-dated only; edit/cancel conflict once due; auto-publish sweep), affiliate notifications via the real dispatcher (commission earned, payout state, tier upgrade). 15 new tests; Engagement 32 passing; full suite 243/243 green. |
| T-D-08…T-D-19 | ⏸ not started | — | see developer_D.md. T-D-08 will replace the `AffiliateProgramConfig` stub with the real gamification config (point_rules, badges, leaderboard). |

⚠️ Shared-file note (T-D-03 + T-D-07): `bootstrap/providers.php` gets one-line
append-only entries — `EngagementServiceProvider` (T-D-03) and
`AffiliateServiceProvider` (T-D-07; binds the DEPS⚡ `AffiliateProgramConfig`
contract to its Phase-1 stub). A owns that file in Phase 0 — flagged per §2;
both entries are purely additive (no existing lines touched) and revert
cleanly if you prefer a different registration mechanism. T-D-07 deliberately
uses its own provider so its branch cannot conflict with T-D-03's provider line.

⚠️ DEPS⚡ note (T-D-07): `App\Services\Billing\SubscriptionService` (A's T-A-23)
is consumed by the affiliate conversion flow only when A's billing side reports
conversions — Phase-1 tests exercise the D-owned path (Mockery doubles where a
DEPS⚡ contract is involved, T-B-05 pattern). Real verification lands in T-D-19
once T-A-23/T-A-15/T-A-16 merge. `AffiliateProgramConfig` (A's T-A-16 contract)
is a D-owned interface + stub for now; A rebinds the singleton when T-A-16 ships
(one-line change, no call-site churn).

## Phase Gates (Track D)
- Gate 1 (end Phase 0): pending — T-D-01, T-D-02, T-D-03 merged; T-D-04/07 pushed.
- Gate 2 (end Phase 1): pending.
- Gate 3 (end Phase 2): pending.
- Gate 4 (end Phase 3): pending.

## Blockers
- (none new from T-D-07)
- Watch: T-A-12 (TenantGate), T-A-21 (seats), T-A-23 (SubscriptionService —
  DEPS⚡ double in use), T-A-15 (discounts), T-A-16 (affiliate program config —
  D-owned stub in use until A rebinds), T-B CatalogService, T-C ProgressService —
  verified for real in T-D-19.
- (T-D-02 A-side DomainEventsTest count-assertion blocker was resolved when T-D-02 merged 4d0dace — no longer carried.)
