# NUMU — MCP Gap Analysis & Close-out Plan

> Every admin-editable UI capability that is **not yet reachable via API**, with the exact existing service/validation each fix must reuse (no logic duplication), and a prioritized build order.

## Current write coverage

| Entity | API Update coverage | State |
|---|---|---|
| Investors | ~82% → **~100%** (Batch 4 ✅) | preferences pivots + favorite + note-edit + tag-rename done. **Intentional exceptions (owner decision 2026-06-25):** notes are edit-but-never-delete; tags are add/edit-but-never-delete/detach. |
| Companies (Startups) | ~70% → **~100%** (Batches 3a+3b ✅) | **team-member CRUD** (shared `StartupMemberService`) + `cap_table` fixed + **files upload-by-URL / move / delete** (shared `StartupFileService`, SSRF-guarded, mime+size validated) |
| Demos | ~12% → **~100%** (Batches 1–2 ✅) | update + attendance + create + audience + **slots** — full parity |
| Committees | ~10% → **~100%** (Batches 1–2 ✅) | update + attendance + create + members + evaluations + **slots** — full parity |

### ✅ Batch 1 — DONE (2026-06-21)
Implemented + verified (routes, MCP tools, audit, rollback, full suite green, live write-path smoke test):
- `PATCH /api/v1/demos/{id}` · MCP `demo_update` — core fields (title_ar/en, type/mode, date/time/url), reuses `DemoController::update` shape; month-uniqueness guarded (422 `month_taken`).
- `PATCH /api/v1/demos/{id}/investors/{investor}/attendance` · `demo_set_investor_attendance` — reuses `DemoAttendanceService::recordAdminTransition` + effective-audience guard.
- `PATCH /api/v1/demos/{id}/startups/{startup}/attendance` · `demo_set_startup_attendance` — roster guard.
- `PATCH /api/v1/committees/{id}` · `committee_update`; `.../investors/{investor}/attendance` · `committee_set_investor_attendance`; `.../startups/{startup}/attendance` · `committee_set_startup_attendance` — reuse `CommitteeAttendanceService`.
- All 6 audited via `AiActivityLogger` and registered reversible in `AiRollbackService` (`demo.update`, `committee.update`, `*_attendance_update`).

---

## Gap inventory (by priority)

### P0 — Demos & Committees write (largest hole)

| Gap | UI source | Reuse (no new logic) | Proposed endpoint / MCP tool |
|---|---|---|---|
| Update demo core fields (title_ar/en, type/mode, meeting_date/time/url) | `DemoController::update` | `DemoRequest` rules + model save | `PATCH /api/v1/demos/{id}` · `demo_update` |
| Create demo | `DemoController::store` | `DemoRequest` + `syncSlots`/`sync` | `POST /api/v1/demos` · `demo_create` |
| Update committee core fields (meeting_date/time/url) | `CommitteeController::update` | `CommitteeRequest` | `PATCH /api/v1/committees/{id}` · `committee_update` |
| Create committee | `CommitteeController::store` | `CommitteeRequest` | `POST /api/v1/committees` · `committee_create` |
| Demo/committee slots (set startup+time list) | `syncSlots()` | controller `syncSlots` + slot validation | `PUT .../{id}/slots` · `*_set_slots` |
| Demo investors/groups link (sync pins + groups) | `investors()->sync` / `groups()->sync` | model pivots | `PUT /api/v1/demos/{id}/audience` · `demo_set_audience` |
| Committee members link | `investors()->sync` | model pivot | `PUT /api/v1/committees/{id}/members` · `committee_set_members` |
| Investor attendance (admin status) | `updateInvestorAttendance` | `DemoAttendanceService::recordAdminTransition` / `CommitteeAttendanceService` | `PATCH .../{id}/investors/{investor}/attendance` · `*_set_investor_attendance` |
| Startup attendance (admin status) | `updateStartupAttendance` | `updateOrCreate` on attendance table | `PATCH .../{id}/startups/{startup}/attendance` · `*_set_startup_attendance` |
| Committee evaluations (pass/fail matrix) | `saveEvaluations` | UPSERT on `committee_evaluations` | `PUT /api/v1/committees/{id}/evaluations` · `committee_set_evaluations` |

### P1 — Startup members & files

| Gap | UI source | Reuse | Proposed endpoint / MCP tool |
|---|---|---|---|
| Team-member create/update/delete | `StartupController::storeMember/updateMember/destroyMember` | `memberFieldRules()` + same writes | `POST/PATCH/DELETE /api/v1/startups/{id}/members[/{member}]` · `startup_member_{create,update,delete}` |
| File upload/replace/move/delete (pitch_deck/other/DD) | `storeFile/replaceFile/moveFileSlot/destroyFile` | `SecureStartupFileStore` + `fileUploadRules()` | `POST/DELETE /api/v1/startups/{id}/files[...]` · `startup_file_{upload,move,delete}` |
| `cap_table` DD document_type | bug: in popover, missing from `fileUploadRules()` whitelist | add to whitelist | (validation fix) |

### P2 — Multiselect pivots & note/tag mutation

| Gap | UI source | Reuse | Proposed endpoint / MCP tool |
|---|---|---|---|
| Investor preferred sectors / stages / payment_methods | `EditsEntitySections::syncPivot` | same | `PUT /api/v1/investors/{id}/preferences` · `investor_set_preferences` |
| Startup geographic_focuses | `syncPivot` | same | `PUT /api/v1/startups/{id}/geographic-focus` · `startup_set_geographic_focus` |
| Note update / delete (investor+startup) | `NotesController::update/destroy` | same | `PATCH/DELETE /api/v1/notes/{id}` · `note_{update,delete}` |
| Tag detach / full-sync / rename | `updateTags` / `TagController::update` | `tags()->sync` | `PUT /api/v1/investors/{id}/tags` · `investor_set_tags`; `PATCH /api/v1/tags/{id}` · `tag_update` |
| Favorite toggle (investor+startup) | `FavoriteController::toggle` | same | `POST /api/v1/favorites/toggle` · `favorite_toggle` |

### Deliberately NOT exposed (by design / owner decision)
- **DELETE on any entity** (investor/startup/demo/committee) — not exposed anywhere in the API today; out of scope unless explicitly requested.
- **Note delete** — notes are create + **edit** only; never deleted via API (audit-trail integrity). Owner decision 2026-06-25.
- **Tag delete / detach / full-sync** — tags are add + **rename/recolor** only; never deleted or detached via API (a rename/delete fans out to every entity carrying the tag). Owner decision 2026-06-25.
- **Startup file create/delete via rollback** — `file_upload`/`file_delete` are audited but not auto-reversible (the blob is written/purged on disk); `file_move` IS reversible.
- Monday-sync internals, privacy-consent audit columns — never surfaced in UI either.

---

## Implementation rules (must follow)
1. **Reuse, don't duplicate.** Every new write endpoint routes through the SAME FormRequest rules and the SAME service the admin UI uses (`DemoRequest`, `CommitteeRequest`, `DemoAttendanceService`, `CommitteeAttendanceService`, `SecureStartupFileStore`, `EditsEntitySections::syncPivot`, `memberFieldRules()`).
2. **Same audit + rollback.** New MCP write tools go through `AiActivityLogger` (atomic write+log) and register reversible action types in `AiRollbackService` where applicable.
3. **Scopes:** reads → `numu:read`; writes → `numu:write` (full tier). Per-tool scope override in `McpToolRegistry::requiredScopeFor`.
4. **Pattern:** new `Ai*WriteController` (JSON) under `app/Http/Controllers/Api/V1/Ai/`, registered as MCP tools in `McpToolRegistry`, routed in `routes/api.php` behind `McpAuth:full`.

---

## Build order
1. **Batch 1 (P0a):** Demo + Committee **core-field update** + **attendance** (investor & startup). Highest pain, cleanest reuse.
2. **Batch 2 (P0b):** Demo/Committee **create** + **slots** + **audience/members links** + committee **evaluations**.
3. **Batch 3 (P1):** Startup **members** + **files** CRUD + `cap_table` validation fix.
4. **Batch 4 (P2):** multiselect pivots, note update/delete, tag sync/rename, favorite toggle.

After each batch: register MCP tools, add to Postman, run `php artisan test`, update coverage table here.
