# AUDIT Log Interpretation Guide

**Purpose:** Interpret `AUDIT:` logs to verify correct email/notification flow and locate failures.  
**Scope:** Logs only; no code changes.

---

## 1. What Gets Logged

| Log message | Where | Context keys |
|-------------|--------|--------------|
| `AUDIT: HistoryNotifier triggered` | HistoryNotifier::handle | colab_id, state, user_id |
| `AUDIT: Email scheduled` | MailingService::sendEmail, sendEmailAt | method, to, colab_id |
| `AUDIT: Email job started` | SendEmailJob::handle | to, mailable, colab_id |
| `AUDIT: Notification attempt` | NotificationService (start of each send* / sendNotificationAt) | method, user_id, colab_id |
| `AUDIT: Scheduled notification job started` | SendScheduledNotificationJob::handle | method, colab_id |

**Important:**  
- **Immediate emails** go through `SendEmailJob::dispatch()` from MailingService methods (e.g. `sendClientCanceledPendingCompanyEmail`). Those methods do **not** call `sendEmail()` or `sendEmailAt()`, so you will **not** see `AUDIT: Email scheduled` for them. You go straight from HistoryNotifier to `AUDIT: Email job started` when the job runs.  
- **Scheduled emails** go through `sendEmailAt()` → you see `AUDIT: Email scheduled` (method: `sendEmailAt`) then later `AUDIT: Email job started`.  
- **Notification attempt** with `method: sendNotificationAt` means “scheduling a notification”, not “sending it now”. The actual send is when you see `AUDIT: Scheduled notification job started` and then another `AUDIT: Notification attempt` with the concrete method (e.g. `sendPendingColabReminder1CompanyNotification`).

---

## 2. Main Flows and Expected Log Sequences

### 2.1 Colab cancellation (pending) — Client cancels

**Trigger:** State → CANCELLED, previous state PENDING_COMPANY, user is company.

**Expected (order may vary for jobs):**

1. `AUDIT: HistoryNotifier triggered` — state = cancelled (or similar), colab_id set, user_id may be set if actor was passed.
2. **No** `AUDIT: Email scheduled` — emails are dispatched directly.
3. `AUDIT: Email job started` — once per email (mailable: `ClientCanceledPendingCompanyEmail`, `ClientCanceledPendingInfluencerEmail`, `ClientCanceledPendingInternalEmail`).
4. `AUDIT: Notification attempt` — method: `sendCanceledColabNotifications` (or the two underlying methods: `sendClientCanceledPendingCompanyNotification`, `sendClientCanceledPendingInfluencerNotification`).

**If influencer cancels pending:**  
Same idea; you’ll see mailables like `InfluencerCanceledPendingCompanyEmail`, `CanceledColabEmail`, and notifications `sendInfluencerCanceledPendingCompanyNotification`, `sendCanceledColabCompanyNotification`.

**If auto-cancel (user null):**  
`AutoCanceledColabNomadeEmail`, `AutoCanceledColabCompanyEmail`, `AutoCanceledColabInfluencerEmail` and `sendAutoCanceledColabCompanyNotification`, `sendAutoCanceledColabInfluencerNotification`.

---

### 2.2 Colab cancellation (accepted) — Client cancels accepted colab

**Trigger:** State → CANCELLED, previous state ACCEPTED, user is company.

**Expected:**

1. `AUDIT: HistoryNotifier triggered` — state = cancelled, colab_id set.
2. `AUDIT: Email job started` — mailables: `ClientCanceledAcceptedCompanyEmail`, `ClientCanceledAcceptedInfluencerEmail`, `ClientCanceledAcceptedInternalEmail`.
3. `AUDIT: Notification attempt` — `sendClientCanceledAcceptedCompanyNotification`, `sendClientCanceledAcceptedInfluencerNotification`.

---

### 2.3 Colab accepted (company accepts)

**Trigger:** State → ACCEPTED (from PENDING_COMPANY), user company.

**Expected:**

1. `AUDIT: HistoryNotifier triggered` — state = accepted, colab_id set.
2. `AUDIT: Email job started` — `ClientAcceptedColabCompanyEmail`, `AcceptedColabInfluencerEmail` (and possibly calendar/publish reminders later via jobs).
3. `AUDIT: Notification attempt` — `sendClientAcceptedColabCompanyNotification`, `sendAcceptedColabInfluencerNotification`.

**If datable colab:**  
Later (when delayed jobs run) you may see:  
- `AUDIT: Email scheduled` (method: `sendEmailAt`) for 24h/2h/1h calendar emails.  
- `AUDIT: Notification attempt` (method: `sendNotificationAt`) for same reminders.  
Then when those run:  
- `AUDIT: Email job started` (mailable e.g. `Reminder24HoursCalendarInfluencerEmail`).  
- `AUDIT: Scheduled notification job started` → `AUDIT: Notification attempt` (e.g. `send24HoursCalendarReminderNotification`).

---

### 2.4 Colab modification (in progress)

**Trigger:** State → MODIFICATION_IN_PROGRESS. Depends who requested (influencer vs company).

**Influencer requested (from PENDING_NOMADE):**

1. `AUDIT: HistoryNotifier triggered` — state = modification_in_progress.
2. `AUDIT: Email job started` — `ConfirmedModificationRequestCompanyEmail`, `ConfirmedModificationRequestInfluencerEmail`, `NewChatCompanyEmail`.
3. `AUDIT: Notification attempt` — `sendConfirmedModificationRequestCompanyNotification`, `sendConfirmedModificationRequestInfluencerNotification`, `sendNewChatCompanyNotification`.

**Company requested (from ACCEPTED, datable):**  
Emails/notifications for `NewChatInfluencerEmail`, `sendNewChatInfluencerNotification`, plus scheduling of modification reminders (see Reminders below).

**Client cancels modification:**  
State → CANCELLED from MODIFICATION_IN_PROGRESS, user company:  
- Emails: `ClientCanceledModificationCompanyEmail`, `ClientCanceledModificationInfluencerEmail`, `ClientCanceledModificationInternalEmail`.  
- Notifications: `sendClientCanceledModificationCompanyNotification`, `sendClientCanceledModificationInfluencerNotification`.

---

### 2.5 Reminders (scheduled emails / notifications)

**Scheduling (at state transition or when reminder is set up):**

- **Emails:** `AUDIT: Email scheduled` — method: `sendEmailAt`, colab_id set (for pending, product sent, modification, calendar, etc.).
- **Notifications:** `AUDIT: Notification attempt` — method: `sendNotificationAt`, colab_id set.

**When the reminder runs (later):**

- **Email:** `AUDIT: Email job started` — mailable identifies type (e.g. `PendingColabReminder1CompanyEmail`, `Reminder1ProductReceivedInfluencerEmail`).
- **Notification:** `AUDIT: Scheduled notification job started` (method = e.g. `sendPendingColabReminder1CompanyNotification`, colab_id set) then `AUDIT: Notification attempt` (same method).

So for reminders, “Email scheduled” / “Notification attempt (sendNotificationAt)” can appear **much earlier** than “Email job started” / “Scheduled notification job started” — that’s normal.

---

## 3. Breakpoints: What Missing Logs Mean

### Only `AUDIT: HistoryNotifier triggered` appears

- **Possible causes:**  
  - Listener ran but downstream code didn’t (e.g. `$user` null and handler returns early, e.g. in `handleRejectedState` / `handleCancelledState` branches).  
  - Exception after the audit log inside `handleStateTransition` or inside a handler (check exception logs).  
  - Handler chose a branch that sends nothing (e.g. wrong previous state or user type).
- **Check:** state and user_id in the HistoryNotifier log; then trace which handle*State ran and whether conditions (user, previous state) match the expected branch.

### `AUDIT: Email scheduled` appears but no `AUDIT: Email job started`

- **Meaning:** A scheduled email was queued (sendEmailAt) but the job either hasn’t run yet, failed to run, or was removed.
- **Checks:**  
  - Queue worker running and processing `default` queue.  
  - Job in `jobs` table (or failed_jobs if it failed).  
  - If job uses `expectedStateId`: colab may have changed state so the job exits without sending (see “expectedStateId logic” below).

### `AUDIT: Email job started` but email not received

- **Meaning:** Job ran and attempted send. Failure is after the audit log (e.g. Mail::send failed, wrong address, or delivery failure).
- **Checks:**  
  - `Log::channel('email_errors')` and app logs for exceptions.  
  - `AUDIT: Email job started` is at the **start** of handle(); if state check runs next and returns, no Mail::send is executed (see expectedStateId).  
  - SMTP/delivery logs and recipient address.

### `AUDIT: Notification attempt` but no push / in-app

- **Meaning:** NotificationService method was entered. Failure is inside that method (e.g. FCM token missing, exception in messagingService or create()).
- **Checks:**  
  - Logs after that line (exceptions, FCM errors).  
  - If method is `sendNotificationAt`: that log means “scheduling”, not “sending”; look for `AUDIT: Scheduled notification job started` and the concrete `AUDIT: Notification attempt` for the actual send.

### `AUDIT: Notification attempt` with user_id null

- **Meaning:** For almost all NotificationService methods we log `user_id => null` by design (only a few methods set user_id, e.g. chat). So “user_id null” does **not** imply “no recipient”.
- **When it’s a problem:** If you added or expect a specific user_id for a given method and it’s null, then the caller may not have passed the user or the user resolution failed. For standard colab notifications, null is expected.

---

## 4. Special Cases

### 4.1 Scheduled emails: “Email scheduled” much earlier than “Email job started”

- Reminders are queued with a delay (e.g. 6 hours, 24 hours). So you see:
  - At T0: `AUDIT: Email scheduled` (method: sendEmailAt).
  - At T0 + delay: `AUDIT: Email job started` when the worker runs the delayed job.
- Same for notifications: `AUDIT: Notification attempt` (method: sendNotificationAt) at schedule time, then much later `AUDIT: Scheduled notification job started` and the concrete notification attempt.

### 4.2 expectedStateId logic — job skipped intentionally

- SendEmailJob and SendScheduledNotificationJob can receive `expectedStateId`. When both `collabId` and `expectedStateId` are set:
  - Job loads the colab and checks current state.
  - If current state ≠ expectedStateId, the job **returns without sending** (no Mail::send / no notification).
- So you can see:
  - `AUDIT: Email job started` (or `AUDIT: Scheduled notification job started`) then no actual send and no error — colab state changed (e.g. accepted → cancelled), so the reminder was correctly skipped.
- Used for: pending reminders (must still be pending), product-sent reminders (must still be in “sent” state), etc.

### 4.3 Null user in HistoryNotifier

- **Expected when:** HistoryNotifier runs in a queue worker. There is no HTTP request, so `Auth::user()` is null. If the event didn’t store the actor, `user_id` will be null.
- **Problematic when:** You need to know who cancelled/accepted (e.g. company vs influencer vs Nomade). Then check whether the event is populated with the acting user when the history is pushed; if not, handlers that branch on `$user->isCompany()` etc. may take the wrong path or return early.

---

## 5. How to Debug Using Only Logs

1. **Filter by colab_id**  
   Use the same colab_id across all AUDIT lines to follow one colab’s flow.

2. **Order of events**  
   - First: `AUDIT: HistoryNotifier triggered` (state tells you which transition).  
   - Then: either `AUDIT: Email scheduled` (only if path goes through sendEmail/sendEmailAt) or directly `AUDIT: Email job started` (one per email).  
   - Then: `AUDIT: Notification attempt` (one per notification; if method is `sendNotificationAt`, treat as “scheduled”).  
   - For scheduled items: later `AUDIT: Email job started` or `AUDIT: Scheduled notification job started` + concrete `AUDIT: Notification attempt`.

3. **Identify the flow**  
   Use `state` and, when present, `mailable` / `method` to match to the flows in Section 2 (cancellation pending/accepted, accepted, modification, reminders).

4. **Find the break**  
   - No emails/jobs after HistoryNotifier → handler or branch (user/state) or exception.  
   - Email scheduled but no Email job started → queue/job not run or removed; or state check (expectedStateId) skipped send.  
   - Email job started but no delivery → Mail/send error or state skip.  
   - Notification attempt but no delivery → FCM/DB error or method is sendNotificationAt (scheduling only).

5. **Scheduled reminders**  
   Correlate by colab_id and method name. “Email scheduled” / “Notification attempt (sendNotificationAt)” at schedule time; “Email job started” / “Scheduled notification job started” at run time. If the second never appears, job wasn’t run or was skipped (e.g. state check).

---

## 6. Quick Reference — Log Types and Meaning

| You see | Meaning |
|--------|--------|
| HistoryNotifier triggered | State transition was processed; check state and user_id for which branch ran. |
| Email scheduled (sendEmail) | Generic email queued (immediate); next should be Email job started. |
| Email scheduled (sendEmailAt) | Delayed email queued; Email job started will appear when the delay elapses. |
| Email job started | Worker is running the email job; mailable says which email. May exit without sending if expectedStateId check fails. |
| Notification attempt (sendNotificationAt) | A notification was **scheduled**, not sent yet. |
| Notification attempt (other method) | That notification send method was **invoked** (push + in-app). |
| Scheduled notification job started | Worker is running a scheduled notification; next the concrete send* method will log Notification attempt. |

This guide is analysis-only and does not modify any code.
