# LeadPilot — Authoritative Business Rules & State Logic

**Document Version:** 1.0.0 (Phase 1 Scope Lock)  
**Status:** Approved & Enforced  

---

## 1. Workspace Ownership & Multi-Tenancy Invariants

* **Rule 1.1 (Tenant Isolation):** Every core business entity (`leads`, `follow_ups`, `sequences`, `templates`, `activities`, `tags`, `api_tokens`) MUST contain a non-nullable `workspace_id` foreign key.
* **Rule 1.2 (Global Scope Enforcement):** All Eloquent models for tenant resources MUST utilize an automatic global scope enforcing `workspace_id = active_workspace_id`.
* **Rule 1.3 (IDOR Immunity):** Route-model binding or explicit lookups on tenant resources using a UUID or ID that does not belong to the active workspace MUST return HTTP 404 (Not Found), never HTTP 403 (Forbidden), to prevent cross-tenant ID enumeration.
* **Rule 1.4 (Role Hierarchy):**
  * `Owner`: Full access, billing management, workspace deletion, team management.
  * `Admin`: Team management, integration setup, pipeline configuration, all lead operations.
  * `Member`: View/edit assigned leads (or all workspace leads if configured), create leads, execute follow-ups.

---

## 2. Lead Status vs. Pipeline Stage State Machine

Lead status (lifecycle condition) and pipeline stage (sales funnel step) are strictly decoupled to preserve operational clarity.

```
+---------------------------------------------------------------------------------------------------+
|                                 LEADPILOT STATE TRANSITIONS                                       |
+---------------------------------------------------------------------------------------------------+
|  LIFECYCLE STATUS (lead.status):                                                                  |
|    - active    (Lead is being worked; default for new leads)                                      |
|    - won       (Successfully converted deal)                                                      |
|    - lost      (Unsuccessful deal with mandatory lost reason)                                     |
|    - archived  (Historical lead retained for data integrity but hidden from active queues)        |
|                                                                                                   |
|  PIPELINE STAGE (lead.pipeline_stage):                                                            |
|    [New]  ----->  [Contacted]  ----->  [Qualified]  ----->  [Proposal]  ----->  [Won]             |
|      |                  |                    |                  |                                 |
|      +------------------+--------------------+------------------+------------>  [Lost]            |
+---------------------------------------------------------------------------------------------------+
```

### Permitted State Combinations:

| Pipeline Stage | Allowed Lifecycle `status` | Required Accompanying Attributes | Auto-Actions Triggered |
| :--- | :--- | :--- | :--- |
| `new` | `active` | Source, Contact info | AI qualification job queued, Attention timer starts. |
| `contacted` | `active` | First contacted timestamp | `first_contacted_at` locked; response time computed. |
| `qualified` | `active` | Estimated value (optional) | Stage update activity logged. |
| `proposal` | `active` | Estimated deal value (mandatory) | Stale proposal timer (5-day threshold) activated. |
| `won` | `won` | Final deal value, `won_at` | Status set to `won`; all pending follow-ups cancelled. |
| `lost` | `lost` | `lost_reason`, `lost_at`, notes | Status set to `lost`; all sequences terminated immediately. |

* **Rule 2.1 (Won Invariant):** When `pipeline_stage` is updated to `won`, `status` MUST be automatically set to `won`.
* **Rule 2.2 (Lost Invariant):** When `pipeline_stage` is updated to `lost`, `status` MUST be automatically set to `lost`, and a valid `lost_reason` MUST be supplied.
* **Rule 2.3 (Resurrection Rule):** If an inbound enquiry or user action transitions a `lost` lead back to any active stage (`new`, `contacted`, `qualified`, `proposal`), `status` MUST revert to `active`, and a `lead_resurrected` activity MUST be recorded.

---

## 3. Standard Lost Reasons Dictionary

Every lost deal must record exactly one primary reason from the standard dictionary:

1. `Price`: Prospect found the fee/quote too high or out of budget.
2. `Competitor`: Prospect selected a competing service provider or alternative tool.
3. `No Response`: Prospect became completely unresponsive after multiple verified follow-up attempts.
4. `Not Qualified`: Prospect did not meet core qualification criteria (e.g. wrong geography, unsupported request).
5. `Timing`: Prospect postponed or cancelled the project indefinitely.
6. `Requirement Changed`: Prospect altered their scope such that the business could no longer deliver it.
7. `Other`: Unclassified reason (mandatory free-text notes required).

---

## 4. Follow-up Engine & Invariant Automation Rules

```
+-----------------------------------------------------------------------------------+
|                        FOLLOW-UP STATE MACHINE (follow_ups.status)                |
+-----------------------------------------------------------------------------------+
|   [Scheduled]  ----->  [Due]  ----->  [Overdue]                                   |
|        |                 |               |                                        |
|        +-----------------+---------------+----->  [Completed] (Touchpoint logged) |
|        |                 |               |                                        |
|        +-----------------+---------------+----->  [Cancelled] (Lead Won/Lost)     |
|        |                                                                          |
|        +--------------------------------------->  [Snoozed]   (Rescheduled)       |
+-----------------------------------------------------------------------------------+
```

* **Rule 4.1 (State Calculation):**
  * `scheduled`: Target timestamp $T_{\text{target}} > \text{NOW}()$.
  * `due`: $T_{\text{target}} \le \text{NOW}()$ and $T_{\text{target}} \ge (\text{NOW}() - 24\text{ hours})$.
  * `overdue`: $T_{\text{target}} < (\text{NOW}() - 24\text{ hours})$.
* **Rule 4.2 (The Golden Sequence Invariant):** Any active follow-up sequence MUST be automatically PAUSED or CANCELLED under the following conditions:
  1. Lead status transitions to `won` or `lost`.
  2. Any new inbound communication (email/form/webhook) arrives from the lead's email/phone.
  3. A sales rep logs a manual activity of type `call_completed` or `meeting_held`.
* **Rule 4.3 (Sequence Immutability):** Sequences only dispatch follow-up nudges or reminders during workspace working hours (configured in workspace settings, e.g. 09:00–18:00 in workspace timezone).

---

## 5. "No Lead Left Behind" Attention Engine Logic

The Attention Engine continuously evaluates active leads against four deterministic rules to compute the **Attention Queue**:

```
+------------------------------------------------------------------------------------------------------+
| ATTENTION CONDITION       | TRIGGER THRESHOLD                           | URGENCY LEVEL | ACTION     |
+---------------------------+---------------------------------------------+---------------+------------+
| 1. Response Overdue (Hot) | Stage = 'new' AND Temp = 'HOT' AND Age > 2h | URGENT (P0)   | Contact Now|
| 2. Response Overdue (Std) | Stage = 'new' AND Age > 24h                 | HIGH (P1)     | Contact Now|
| 3. Follow-up Overdue      | Active follow-up > 24h past due             | HIGH (P1)     | Follow Up  |
| 4. Stale Proposal         | Stage = 'proposal' AND Inactive > 5 days    | MEDIUM (P2)   | Send Nudge |
| 5. Neglected Qualified    | Stage = 'qualified' AND Inactive > 7 days   | MEDIUM (P2)   | Review Deal|
+------------------------------------------------------------------------------------------------------+
```

* **Rule 5.1 (Sorting Order):** Attention items are strictly sorted by: `Urgency Level (URGENT > HIGH > MEDIUM)` $\to$ `AI Lead Score DESC` $\to$ `Attention Trigger Age DESC`.

---

## 6. AI Intelligence & Safety Boundary Rules

```
+-----------------------------------------------------------------------------------+
|                             AI SYSTEM BOUNDARY                                    |
+-----------------------------------------------------------------------------------+
|  [PERMITTED CAPABILITIES]              |  [STRICTLY PROHIBITED CAPABILITIES]      |
|  - Parse unstructured inquiry text     |  - Direct database writes without rules  |
|  - Assign Lead Score (0-100)           |  - Mutating lead status to Won/Lost      |
|  - Assign Temperature (HOT/WARM/COLD)  |  - Deleting leads or activities          |
|  - Extract Intent & Budget signals     |  - Modifying user roles or permissions   |
|  - Recommend next action and template  |  - Sending unapproved outbound emails    |
+-----------------------------------------------------------------------------------+
```

* **Rule 6.1 (Structured JSON Contract):** The AI provider must return a strictly validated JSON payload containing: `score`, `temperature`, `intent`, `urgency`, `fit_summary`, `reason`, `recommended_action`.
* **Rule 6.2 (Safe Fallback Heuristic):** If the LLM provider fails, times out ($>8\text{ seconds}$), or returns invalid JSON:
  * The system creates an `ai_intelligence` record with `status = 'fallback'`.
  * Deterministic scoring is applied:
    * Lead containing phone + company + budget keyword $\to$ `Score = 70`, `Temp = 'WARM'`.
    * Lead containing only email + brief message $\to$ `Score = 40`, `Temp = 'COLD'`.
  * Lead creation NEVER fails due to AI downtime.

---

## 7. Deduplication Invariants

* **Rule 7.1 (Exact Match - Email):**
  * When an incoming lead has a normalized email (`lowercase(trim(email))`) that exists in the same workspace:
  * If the existing lead is `active`, do NOT create a duplicate lead record. Instead:
    1. Append the new inquiry text to the existing lead's activity timeline as an `inbound_enquiry` event.
    2. Reset the lead's attention flag to `Needs Attention`.
    3. Re-queue AI scoring with the combined conversation context.
* **Rule 7.2 (Secondary Match - Phone):**
  * If phone numbers match exactly within a workspace but emails differ:
  * System creates the lead but flags `is_possible_duplicate = true` with a reference to the candidate lead ID and displays a prominent warning in the Lead Detail header.

---

## 8. Webhook & Ingestion Idempotency

* **Rule 8.1 (Idempotency Key):** API requests supplying an `Idempotency-Key` header cache the response in Redis/DB for 24 hours. Duplicate requests return the cached response with zero duplicate DB insertions.
* **Rule 8.2 (Payload Key Mapping):** Inbound webhooks store the raw JSON payload in a `webhook_logs` table before processing. If schema mapping fails, the raw payload is preserved for manual replaying or debugging.

---

## 9. Definition of "Leads Recovered" & Attributed Revenue

To prevent vanity metrics and ensure rock-solid commercial trust:

* **Rule 9.1 (Strict Recovery Criteria):** A lead is marked `is_recovered = true` if and ONLY if:
  1. The lead was in a verified `needs_attention` or `stale` condition for a minimum of 48 continuous hours.
  2. A user executed a surfaced LeadPilot action (e.g. sent follow-up, logged call) directly from the Attention Hub or Lead Detail.
  3. The lead subsequently advanced at least one pipeline stage or was marked `Won` within 30 days of the action.
* **Rule 9.2 (Attributed Revenue):** "Recovered Revenue" equals the sum of `deal_value` for leads meeting the strict Rule 9.1 criteria.

---

## 10. Immutable Activity Audit Log

* **Rule 10.1 (No Silent Mutations):** Every update to `pipeline_stage`, `status`, `assigned_user_id`, `deal_value`, or `lost_reason` MUST generate an immutable row in the `activities` table with `user_id` (or `system`), `event_type`, `old_value`, `new_value`, and timestamp.
* **Rule 10.2 (Audit Preservation):** Activity logs cannot be edited or deleted by any user role (including Workspace Owner) to preserve complete operational integrity.
