# Numu — AI Agents: Investor Execution Parity

> Investor agents are now **architecturally identical** to startup agents across
> the whole lifecycle: registration → run → decision → policy → approval →
> **execution via `InvestorLabelActionService`** → notifications/automations/audit.
> The only difference is the *subject* of a run: `startup_id` **or** `investor_id`.
> **Verified live:** 19/19 investor lifecycle assertions + 14/14 unit tests pass.

---

## 1. What changed (subject = polymorphic startup | investor)

| Layer | Change |
|---|---|
| **Schema** | `agent_runs`, `agent_approval_requests`, `execution_requests` → added nullable `investor_id` (FK `investors`, nullOnDelete, indexed); `startup_id` made nullable. Each row carries **exactly one** subject. |
| **Models** | `investor_id` fillable + `investor()` relation on all three. `AgentRun::subjectType()` (`startup`\|`investor`) + `subjectId()`. |
| **Executor** | `AgentActionExecutor::executeForRun()` branches on `subjectType()` → `executeStartup()` or **`executeInvestor()`** → `InvestorLabelActionService::apply($investor, $optionId, $opts, $request, source='agent')` (the single investor execution path). |
| **Orchestrator** | `AgentDecisionService` scopes policy resolution by subject + claims the execution slot with `startup_id`/`investor_id`. `ApprovalService::createFromRun` carries `investor_id`. `ApprovalExecutionService::approve` executes the investor action on approve. |
| **Controllers** | `POST /agent-runs` and `POST /execution-requests` accept **exactly one** of `startup_id`\|`investor_id` (`required_without` + `prohibits`). |
| **Testing** | `POST /api/v1/ai/testing/investor-scenario` now runs the **full lifecycle** (was policy-only). |

No business logic duplicated — execution goes through the existing `InvestorLabelActionService`
(fires `InvestorLabelOptionChanged` → `InvestorWorkflowObserver` group move + notifications + logs).

---

## 2. Investor action catalog (existing label `key=action, type=investor`)

`approve` · `approve_w` · `reject` · `move_to_archive` · `create_deals` · `default` · `new`
(manage them in **Actions → Investors** tab; assign agents + policies exactly like startups).

---

## 3. Investor workflow (mirror of startup)

```
Make → Investor Agent → POST /agent-runs {investor_id}      → agent_runs(queued, investor_id)
     → POST /agent-runs/{run}/decision {action_slug,…}
          → Policy Engine.resolve(agent, action)            → always_allow | needs_approval | blocked
          ├ always_allow   → InvestorLabelActionService.apply() → investor.action_option_id changed
          │                    → InvestorLabelOptionChanged → group move / notifications / audit
          ├ needs_approval → agent_approval_requests(PENDING, investor_id)  → admin approve → execute
          └ blocked        → run.execution_status=blocked (no execution, no approval)
```

Identical 4 policy branches, identical approval screen (`/admin/approvals` shows investor or
startup), identical idempotency + audit. **Each arrow is human-approved by default** (set a
policy to `always_allow` to automate, `blocked` to deny).

---

## 4. API scenarios (investor)

> Auth `Bearer naat_…` (full). Send **exactly one** subject: `investor_id` (here) or `startup_id`.

### Create an investor run
```bash
POST /api/v1/ai/agent-runs
{ "agent_key":"investor_review", "investor_id": 1,
  "context": { "source":"make", "workflow":"investor_pipeline" } }
# → 201 { "data": { "id": 40, "investor_id":1, "startup_id":null, "analysis_status":"queued", … } }
```

### Decision (always_allow → Numu executes the investor action)
```bash
POST /api/v1/ai/agent-runs/40/decision
{ "action_slug":"create_deals", "confidence":0.9, "reason":"investor cleared review" }
# → { "data": { "policy":"always_allow","decision":"executed","executed":true,
#               "execution_request_id":N, "result":{ "subject":"investor","action":"create_deals","changed":true } } }
```

### Decision (needs_approval) + approve
```bash
POST /api/v1/ai/agent-runs/41/decision { "action_slug":"approve", "confidence":0.82 }
# → { "data": { "policy":"needs_approval","executed":false,"approval_id":M } }
GET  /api/v1/ai/approvals/M            # investor_id set, status pending
POST /api/v1/ai/approvals/M/approve    # → Numu executes the investor action, status approved
```

### Policy for an investor agent
```bash
PUT /api/v1/ai/action-policies/investor_review/create_deals { "policy":"needs_approval","environment":"prod" }
GET /api/v1/ai/action-policies/investor_review            # all its assigned actions
GET /api/v1/ai/action-policies/investor_review/approve?environment=prod   # resolve one
```

### One-call testing (Postman)
```bash
POST /api/v1/ai/testing/investor-scenario
{ "investor_id":1, "agent_key":"investor_review", "action_slug":"create_deals",
  "policy":"always_allow", "confidence":0.9, "auto_approve":false, "revert":true }
# runs the full lifecycle and (revert:true) restores the investor afterward.
```
Postman: folder **8** → "Investor scenario — full lifecycle" + "needs_approval + auto-approve";
folder **3** → "POST create run (investor)".

---

## 5. Registering investor agents

```bash
POST /api/v1/ai/agents { "agent_key":"investor_review","agent_type":"investor","name":"Investor Review","environment":"prod" }
# or in the UI: AI Agents → 👥 Investors tab → Register Agent (Agent Type = Investor)
```
No investor agents are **seeded** (the investor pipeline isn't standardized) — create the ones
you need. Everything else (policies, approvals, execution, audit) works immediately.

---

## 6. Verification (live, investor #1 + `create_deals`, auto-reverted)

```
INVESTOR always_allow → executes ........ 7/7 PASS  (executed via InvestorLabelActionService, action_option_id changed, exec-request carries investor_id, no approval)
INVESTOR needs_approval → approve → exec . 7/7 PASS  (approval carries investor_id, unchanged → approve → executed)
INVESTOR blocked → denied ............... 4/4 PASS
INVESTOR default-blocked (unassigned) ... 1/1 PASS
TOTAL: 19/19 PASS  · investor reverted · test agent + data cleaned
Unit suite: 14/14 PASS
```

---

## 7. Status — now complete for BOTH types

| Capability | Startup | Investor |
|---|---|---|
| Registry (API + UI, typed) | ✅ | ✅ |
| Runtime enable/pause | ✅ | ✅ |
| Runs (subject-scoped) | ✅ | ✅ (`investor_id`) |
| Decision orchestration | ✅ | ✅ |
| Policy resolution | ✅ | ✅ |
| Auto approval creation | ✅ | ✅ |
| Approve → Numu executes | ✅ | ✅ |
| Native execution (single path) | `StartupLabelActionService` | `InvestorLabelActionService` |
| Notifications / automations / audit | ✅ | ✅ |
| Testing endpoint (full lifecycle) | ✅ | ✅ |
| Handoffs (TD-002) | ✅ | ✅ (`handoffs.investor_id`) |
| Expected-state validation (TD-005) | ✅ | ✅ |
| Pause enforcement (TD-003) | ✅ | ✅ |
| Seeded default agents | ✅ (7) | — (create as needed) |

**Remaining (non-blocking):** seed canonical investor agents once the investor pipeline is
standardized; otherwise the system is feature-complete and symmetric for both entity types.
